Merge branch 'master' of github.com:fit2cloudrd/metersphere-server
This commit is contained in:
commit
fa13003865
|
@ -49,7 +49,6 @@ public class APITestController {
|
|||
return apiTestService.getApiTestByProjectId(projectId);
|
||||
}
|
||||
|
||||
|
||||
@PostMapping(value = "/schedule/update")
|
||||
public void updateSchedule(@RequestBody Schedule request) {
|
||||
apiTestService.updateSchedule(request);
|
||||
|
@ -90,6 +89,11 @@ public class APITestController {
|
|||
return apiTestService.run(request);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/run/independent", consumes = {"multipart/form-data"})
|
||||
public String runIndependent(@RequestBody SaveAPITestRequest request, @RequestPart(value = "files") List<MultipartFile> files) {
|
||||
return apiTestService.runIndependent(request, files);
|
||||
}
|
||||
|
||||
@PostMapping(value = "/import", consumes = {"multipart/form-data"})
|
||||
@RequiresRoles(value = {RoleConstants.TEST_USER, RoleConstants.TEST_MANAGER}, logical = Logical.OR)
|
||||
public ApiTest testCaseImport(@RequestPart(value = "file", required = false) MultipartFile file, @RequestPart("request") ApiTestImportRequest request) {
|
||||
|
|
|
@ -10,6 +10,7 @@ public class Scenario {
|
|||
private String name;
|
||||
private String url;
|
||||
private String environmentId;
|
||||
private Boolean enableCookieShare;
|
||||
private List<KeyValue> variables;
|
||||
private List<KeyValue> headers;
|
||||
private List<Request> requests;
|
||||
|
|
|
@ -101,7 +101,12 @@ public class APIReportService {
|
|||
if (running != null) {
|
||||
return running.getId();
|
||||
}
|
||||
ApiTestReport report = buildReport(test, triggerMode, APITestStatus.Running.name());
|
||||
apiTestReportMapper.insert(report);
|
||||
return report.getId();
|
||||
}
|
||||
|
||||
public ApiTestReport buildReport(ApiTest test, String triggerMode, String status) {
|
||||
ApiTestReport report = new ApiTestReport();
|
||||
report.setId(UUID.randomUUID().toString());
|
||||
report.setTestId(test.getId());
|
||||
|
@ -110,11 +115,9 @@ public class APIReportService {
|
|||
report.setDescription(test.getDescription());
|
||||
report.setCreateTime(System.currentTimeMillis());
|
||||
report.setUpdateTime(System.currentTimeMillis());
|
||||
report.setStatus(APITestStatus.Running.name());
|
||||
report.setStatus(status);
|
||||
report.setUserId(test.getUserId());
|
||||
apiTestReportMapper.insert(report);
|
||||
|
||||
return report.getId();
|
||||
return report;
|
||||
}
|
||||
|
||||
public ApiTestReport getRunningReport(String testId) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import org.springframework.web.multipart.MultipartFile;
|
|||
|
||||
import javax.annotation.Resource;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
@ -299,8 +300,8 @@ public class APITestService {
|
|||
if (info.length > 1) {
|
||||
provider.setVersion(info[1]);
|
||||
}
|
||||
provider.setService(info[0]);
|
||||
provider.setServiceInterface(p);
|
||||
provider.setService(p);
|
||||
provider.setServiceInterface(info[0]);
|
||||
Map<String, URL> services = providerService.findByService(p);
|
||||
if (services != null && !services.isEmpty()) {
|
||||
String[] methods = services.values().stream().findFirst().get().getParameter(CommonConstants.METHODS_KEY).split(",");
|
||||
|
@ -314,6 +315,7 @@ public class APITestService {
|
|||
}
|
||||
|
||||
public List<ScheduleDao> listSchedule(QueryScheduleRequest request) {
|
||||
request.setEnable(true);
|
||||
List<ScheduleDao> schedules = scheduleService.list(request);
|
||||
List<String> resourceIds = schedules.stream()
|
||||
.map(Schedule::getResourceId)
|
||||
|
@ -327,4 +329,29 @@ public class APITestService {
|
|||
}
|
||||
return schedules;
|
||||
}
|
||||
|
||||
public String runIndependent(SaveAPITestRequest request, List<MultipartFile> files) {
|
||||
if (files == null || files.isEmpty()) {
|
||||
throw new IllegalArgumentException(Translator.get("file_cannot_be_null"));
|
||||
}
|
||||
// ApiTest test = createTest(request);
|
||||
// saveFile(test.getId(), files);
|
||||
MultipartFile file = files.get(0);
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = new ByteArrayInputStream(file.getBytes());
|
||||
} catch (IOException e) {
|
||||
LogUtil.error(e);
|
||||
}
|
||||
|
||||
APITestResult apiTest = get(request.getId());
|
||||
if (SessionUtils.getUser() == null) {
|
||||
apiTest.setUserId(request.getUserId());
|
||||
}
|
||||
String reportId = apiReportService.create(apiTest, request.getTriggerMode());
|
||||
changeStatus(request.getId(), APITestStatus.Running);
|
||||
|
||||
jMeterService.run(request.getId(), is);
|
||||
return reportId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
<if test="request.workspaceId != null">
|
||||
and schedule.workspace_id = #{request.workspaceId}
|
||||
</if>
|
||||
<if test="request.enable != null">
|
||||
and schedule.enable = #{request.enable}
|
||||
</if>
|
||||
<if test="request.filters != null and request.filters.size() > 0">
|
||||
<foreach collection="request.filters.entrySet()" index="key" item="values">
|
||||
<if test="values != null and values.size() > 0">
|
||||
|
|
|
@ -420,6 +420,7 @@ public class PerformanceTestService {
|
|||
}
|
||||
|
||||
public List<ScheduleDao> listSchedule(QueryScheduleRequest request) {
|
||||
request.setEnable(true);
|
||||
List<ScheduleDao> schedules = scheduleService.list(request);
|
||||
List<String> resourceIds = schedules.stream()
|
||||
.map(Schedule::getResourceId)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<template v-slot:title>{{$t('commons.project')}}</template>
|
||||
<ms-recent-list :options="projectRecent"/>
|
||||
<el-divider class="menu-divider"/>
|
||||
<ms-show-all :index="'/api/project'"/>
|
||||
<ms-show-all :index="'/api/project/all'"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/project/create'"
|
||||
:title="$t('project.create')"/>
|
||||
</el-submenu>
|
||||
|
@ -20,7 +20,7 @@
|
|||
<template v-slot:title>{{$t('commons.test')}}</template>
|
||||
<ms-recent-list :options="testRecent"/>
|
||||
<el-divider class="menu-divider"/>
|
||||
<ms-show-all :index="'/api/test/list'"/>
|
||||
<ms-show-all :index="'/api/test/list/all'"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/api/test/create'"
|
||||
:title="$t('load_test.create')"/>
|
||||
</el-submenu>
|
||||
|
@ -29,7 +29,7 @@
|
|||
<template v-slot:title>{{$t('commons.report')}}</template>
|
||||
<ms-recent-list :options="reportRecent"/>
|
||||
<el-divider class="menu-divider"/>
|
||||
<ms-show-all :index="'/api/report/list'"/>
|
||||
<ms-show-all :index="'/api/report/list/all'"/>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
|
@ -60,7 +60,7 @@
|
|||
title: this.$t('project.recent'),
|
||||
url: "/project/recent/5",
|
||||
index: function (item) {
|
||||
return '/api/' + item.id;
|
||||
return '/api/test/list/' + item.id;
|
||||
},
|
||||
router: function (item) {
|
||||
return {name: 'ApiTestList', params: {projectId: item.id, projectName: item.name}}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<ms-test-heatmap :values="values"/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ms-api-test-schedule-list :group="'API_TEST'"/>
|
||||
<ms-schedule-list :group="'API_TEST'"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ms-main-container>
|
||||
|
@ -28,13 +28,13 @@
|
|||
import MsApiTestRecentList from "./ApiTestRecentList";
|
||||
import MsApiReportRecentList from "./ApiReportRecentList";
|
||||
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
|
||||
import MsApiTestScheduleList from "./ApiTestScheduleList";
|
||||
import MsScheduleList from "./ScheduleList";
|
||||
|
||||
export default {
|
||||
name: "ApiTestHome",
|
||||
|
||||
components: {
|
||||
MsApiTestScheduleList,
|
||||
MsScheduleList,
|
||||
MsTestHeatmap, MsApiReportRecentList, MsApiTestRecentList, MsMainContainer, MsContainer
|
||||
},
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<el-card class="table-card" v-loading="result.loading">
|
||||
<template v-slot:header>
|
||||
<span class="title">{{$t('commons.trigger_mode.schedule')}}</span>
|
||||
<span class="title">{{$t('schedule.running_task')}}</span>
|
||||
</template>
|
||||
<el-table height="289" border :data="tableData" class="adjust-table table-content" @row-click="link">
|
||||
<el-table-column prop="resourceName" :label="$t('schedule.test_name')" width="150" show-overflow-tooltip/>
|
||||
|
@ -32,7 +32,7 @@
|
|||
import {SCHEDULE_TYPE} from "../../../../common/js/constants";
|
||||
|
||||
export default {
|
||||
name: "MsApiTestScheduleList",
|
||||
name: "MsScheduleList",
|
||||
components: {CrontabResult},
|
||||
data() {
|
||||
return {
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<el-card class="scenario-results">
|
||||
<div class="scenario-header">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="16">
|
||||
|
|
|
@ -157,9 +157,11 @@
|
|||
saveTest() {
|
||||
this.save(() => {
|
||||
this.$success(this.$t('commons.save_success'));
|
||||
if (this.create) {
|
||||
this.$router.push({
|
||||
path: '/api/test/edit?id=' + this.test.id
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
runTest() {
|
||||
|
|
|
@ -21,6 +21,9 @@
|
|||
</div>
|
||||
</template>
|
||||
</el-select>
|
||||
<el-form-item class="cookie-item">
|
||||
<el-checkbox v-model="scenario.enableCookieShare">{{'共享 Cookie'}}</el-checkbox>
|
||||
</el-form-item>
|
||||
</el-form-item>
|
||||
|
||||
<el-tabs v-model="activeName">
|
||||
|
@ -168,4 +171,8 @@
|
|||
font-weight: 600;
|
||||
}
|
||||
|
||||
.cookie-item {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
<template>
|
||||
<div>
|
||||
<component :is="component" :is-read-only="isReadOnly" :request="request"/>
|
||||
<!-- <ms-scenario-results :scenarios="content.scenarios"/>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {Request, RequestFactory} from "../../model/ScenarioModel";
|
||||
import MsApiHttpRequestForm from "./ApiHttpRequestForm";
|
||||
import MsApiDubboRequestForm from "./ApiDubboRequestForm";
|
||||
import MsScenarioResults from "../../../report/components/ScenarioResults";
|
||||
|
||||
export default {
|
||||
name: "MsApiRequestForm",
|
||||
components: {MsApiDubboRequestForm, MsApiHttpRequestForm},
|
||||
components: {MsScenarioResults, MsApiDubboRequestForm, MsApiHttpRequestForm},
|
||||
props: {
|
||||
request: Request,
|
||||
isReadOnly: {
|
||||
|
@ -17,6 +21,12 @@
|
|||
default: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
reportId: "",
|
||||
content:{}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
component({request: {type}}) {
|
||||
let name;
|
||||
|
@ -29,10 +39,46 @@
|
|||
}
|
||||
return name;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getReport();
|
||||
},
|
||||
methods: {
|
||||
getReport() {
|
||||
// // this.reportId = "00143d36-a58a-477e-a05a-556c1d48046c";
|
||||
// if (this.reportId) {
|
||||
// let url = "/api/report/get/" + this.reportId;
|
||||
// this.$get(url, response => {
|
||||
// let report = response.data || {};
|
||||
// if (response.data) {
|
||||
// // if (this.isNotRunning) {
|
||||
// try {
|
||||
// this.content = JSON.parse(report.content);
|
||||
// } catch (e) {
|
||||
// console.log(report.content)
|
||||
// throw e;
|
||||
// }
|
||||
// // this.getFails();
|
||||
// // this.loading = false;
|
||||
// // }
|
||||
// // else {
|
||||
// // setTimeout(this.getReport, 2000)
|
||||
// // }
|
||||
// } else {
|
||||
// this.loading = false;
|
||||
// this.$error(this.$t('api_report.not_exist'));
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
.scenario-results {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
|
|
@ -328,6 +328,15 @@ export class HTTPSamplerArguments extends Element {
|
|||
}
|
||||
}
|
||||
|
||||
export class CookieManager extends DefaultTestElement {
|
||||
constructor(testName) {
|
||||
super('CookieManager', 'CookiePanel', 'CookieManager', testName);
|
||||
this.collectionProp('CookieManager.cookies');
|
||||
this.boolProp('CookieManager.clearEachIteration', false, false);
|
||||
this.boolProp('CookieManager.controlledByThreadGroup', false, false);
|
||||
}
|
||||
}
|
||||
|
||||
export class DurationAssertion extends DefaultTestElement {
|
||||
constructor(testName, duration) {
|
||||
super('DurationAssertion', 'DurationAssertionGui', 'DurationAssertion', testName);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {
|
||||
Arguments,
|
||||
Arguments, CookieManager,
|
||||
DubboSample,
|
||||
DurationAssertion,
|
||||
Element,
|
||||
|
@ -174,6 +174,7 @@ export class Scenario extends BaseConfig {
|
|||
this.environmentId = undefined;
|
||||
this.dubboConfig = undefined;
|
||||
this.environment = undefined;
|
||||
this.enableCookieShare = false;
|
||||
|
||||
this.set(options);
|
||||
this.sets({variables: KeyValue, headers: KeyValue, requests: RequestFactory}, options);
|
||||
|
@ -765,6 +766,8 @@ class JMXGenerator {
|
|||
|
||||
this.addScenarioHeaders(threadGroup, scenario);
|
||||
|
||||
this.addScenarioCookieManager(threadGroup, scenario);
|
||||
|
||||
scenario.requests.forEach(request => {
|
||||
if (!request.isValid()) return;
|
||||
let sampler;
|
||||
|
@ -822,6 +825,12 @@ class JMXGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
addScenarioCookieManager(threadGroup, scenario) {
|
||||
if (scenario.enableCookieShare) {
|
||||
threadGroup.put(new CookieManager(scenario.name));
|
||||
}
|
||||
}
|
||||
|
||||
addScenarioHeaders(threadGroup, scenario) {
|
||||
let environment = scenario.environment;
|
||||
if (environment) {
|
||||
|
|
|
@ -7,10 +7,14 @@
|
|||
</span>
|
||||
<el-switch :disabled="!schedule.value || isReadOnly" v-model="schedule.enable" @change="scheduleChange"/>
|
||||
<ms-schedule-edit :is-read-only="isReadOnly" :schedule="schedule" :save="save" :custom-validate="customValidate" ref="scheduleEdit"/>
|
||||
<crontab-result v-show="false" :ex="schedule.value" ref="crontabResult" @resultListChange="resultListChange"/>
|
||||
|
||||
</div>
|
||||
<div>
|
||||
<span :class="{'disable-character': !schedule.enable}"> {{$t('schedule.next_execution_time')}}:{{this.recentList.length > 0 && schedule.enable ? this.recentList[0] : $t('schedule.not_set')}} </span>
|
||||
<span>
|
||||
{{$t('schedule.next_execution_time')}}:
|
||||
<span :class="{'disable-character': !schedule.enable}" v-if="!schedule.enable">{{$t('schedule.not_set')}}</span>
|
||||
<crontab-result v-if="schedule.enable" :enable-simple-mode="true" :ex="schedule.value" ref="crontabResult"/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -59,9 +63,6 @@
|
|||
scheduleChange() {
|
||||
this.$emit('scheduleChange');
|
||||
},
|
||||
resultListChange(resultList) {
|
||||
this.recentList = resultList;
|
||||
},
|
||||
flashResultList() {
|
||||
this.$refs.crontabResult.expressionChange();
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<span>
|
||||
<span v-if="enableSimpleMode">{{resultList && resultList.length > 0 ? resultList[0] : ''}}</span>
|
||||
<div v-if="!enableSimpleMode" class="popup-result">
|
||||
<p class="title">{{$t('schedule.cron.recent_run_time')}}</p>
|
||||
|
@ -9,7 +9,7 @@
|
|||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
methods: {
|
||||
changeRoute() {
|
||||
// 解决在列表页面点击 显示全部 无效的问题(点击显示全部后改变路由)
|
||||
this.$router.push(this.index + '/all');
|
||||
this.$router.replace({path: this.index, query: {type: 'all'}});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
<template v-slot:title>{{$t('commons.project')}}</template>
|
||||
<ms-recent-list :options="projectRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/performance/project'"/>
|
||||
<ms-show-all :index="'/performance/project/all'"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/project/create'" :title="$t('project.create')"/>
|
||||
</el-submenu>
|
||||
|
||||
|
@ -21,7 +21,7 @@
|
|||
<template v-slot:title>{{$t('commons.test')}}</template>
|
||||
<ms-recent-list :options="testRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/performance/test'"/>
|
||||
<ms-show-all :index="'/performance/test/all'"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/performance/test/create'" :title="$t('load_test.create')"/>
|
||||
</el-submenu>
|
||||
|
||||
|
@ -30,7 +30,7 @@
|
|||
<template v-slot:title>{{$t('commons.report')}}</template>
|
||||
<ms-recent-list :options="reportRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/performance/report'"/>
|
||||
<ms-show-all :index="'/performance/report/all'"/>
|
||||
</el-submenu>
|
||||
</el-menu>
|
||||
</el-col>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
<ms-test-heatmap :values="values"/>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<ms-api-test-schedule-list :group="'PERFORMANCE_TEST'"/>
|
||||
<ms-schedule-list :group="'PERFORMANCE_TEST'"/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ms-main-container>
|
||||
|
@ -28,12 +28,12 @@
|
|||
import MsPerformanceTestRecentList from "./PerformanceTestRecentList"
|
||||
import MsPerformanceReportRecentList from "./PerformanceReportRecentList"
|
||||
import MsTestHeatmap from "../../common/components/MsTestHeatmap";
|
||||
import MsApiTestScheduleList from "../../api/home/ApiTestScheduleList";
|
||||
import MsScheduleList from "../../api/home/ScheduleList";
|
||||
|
||||
export default {
|
||||
name: "PerformanceTestHome",
|
||||
components: {
|
||||
MsApiTestScheduleList,
|
||||
MsScheduleList,
|
||||
MsTestHeatmap,
|
||||
MsMainContainer,
|
||||
MsContainer,
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
<template v-slot:title>{{$t('commons.project')}}</template>
|
||||
<ms-recent-list :options="projectRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/track/project'"/>
|
||||
<ms-show-all :index="'/track/project/all'"/>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/project/create'" :title="$t('project.create')"/>
|
||||
</el-submenu>
|
||||
|
||||
|
@ -22,7 +22,7 @@
|
|||
<template v-slot:title>{{$t('test_track.case.test_case')}}</template>
|
||||
<ms-recent-list :options="caseRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/track/case'"/>
|
||||
<ms-show-all :index="'/track/case/all'"/>
|
||||
<el-menu-item :index="testCaseEditPath" class="blank_item"></el-menu-item>
|
||||
<el-menu-item :index="testCaseProjectPath" class="blank_item"></el-menu-item>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/case/create'" :title="$t('test_track.case.create_case')"/>
|
||||
|
@ -32,7 +32,7 @@
|
|||
<template v-slot:title>{{$t('test_track.plan.test_plan')}}</template>
|
||||
<ms-recent-list :options="planRecent"/>
|
||||
<el-divider/>
|
||||
<ms-show-all :index="'/track/plan'"/>
|
||||
<ms-show-all :index="'/track/plan/all'"/>
|
||||
<el-menu-item :index="testPlanViewPath" class="blank_item"></el-menu-item>
|
||||
<ms-create-button v-permission="['test_manager','test_user']" :index="'/track/plan/create'" :title="$t('test_track.plan.create_plan')"/>
|
||||
</el-submenu>
|
||||
|
|
|
@ -717,6 +717,7 @@ export default {
|
|||
test_name: 'Test Name',
|
||||
running_rule: 'Rule',
|
||||
job_status: 'Status',
|
||||
running_task: 'Running Task',
|
||||
please_input_cron_expression: "Please Input Cron Expression",
|
||||
generate_expression: "Generate Expression",
|
||||
cron_expression_format_error: "Cron Expression Format Error",
|
||||
|
|
|
@ -716,6 +716,7 @@ export default {
|
|||
test_name: '测试名称',
|
||||
running_rule: '运行规则',
|
||||
job_status: '任务状态',
|
||||
running_task: '运行中的任务',
|
||||
next_execution_time: "下次执行时间",
|
||||
edit_timer_task: "编辑定时任务",
|
||||
please_input_cron_expression: "请输入 Cron 表达式",
|
||||
|
|
|
@ -715,6 +715,7 @@ export default {
|
|||
test_name: '測試名稱',
|
||||
running_rule: '運行規則',
|
||||
job_status: '任務狀態',
|
||||
running_task: '運行中的任務',
|
||||
please_input_cron_expression: "請輸入 Cron 表達式",
|
||||
generate_expression: "生成表達式",
|
||||
cron_expression_format_error: "Cron 表達式格式錯誤",
|
||||
|
|
Loading…
Reference in New Issue