add:性能测试-测试
This commit is contained in:
63
test-ui/src/api/performance/index.js
Normal file
63
test-ui/src/api/performance/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 测试-列表查询
|
||||
export function getPerfromanceList(query) {
|
||||
return request({
|
||||
url: '/test/performanceTest/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 测试-列表删除
|
||||
export function deleteTest(data) {
|
||||
return request({
|
||||
url: '/test/performanceTest/' + data,
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 测试-列表新增
|
||||
export function addTest(data) {
|
||||
return request({
|
||||
url: '/test/performanceTest/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 测试-列表新增并执行
|
||||
export function addAndExecuteTest(data) {
|
||||
return request({
|
||||
url: '/test/performanceTest/addAndExecute',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 引用接口自动化场景
|
||||
export function getTestCaseList(query) {
|
||||
return request({
|
||||
url: '/test/performanceTest/testCaseList',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
//测试-编辑
|
||||
export function editTest(data) {
|
||||
return request({
|
||||
url: '/test/performanceTest',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
//测试-编辑-保存并执行
|
||||
export function editAndExecuteTest(data) {
|
||||
return request({
|
||||
url: '/test/performanceTest/editAndExecute',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -144,6 +144,34 @@ export const constantRoutes = [
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/performance/add',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/test/performance/performanceAdd'),
|
||||
name: 'performanceAdd',
|
||||
noCache: true,
|
||||
meta: { title: '新增测试', activeMenu: '/performance' }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/performance/edit',
|
||||
component: Layout,
|
||||
hidden: true,
|
||||
children: [
|
||||
{
|
||||
path: '',
|
||||
component: () => import('@/views/test/performance/performanceEdit'),
|
||||
name: 'performanceAdd',
|
||||
noCache: true,
|
||||
meta: { title: '修改测试', activeMenu: '/performance' }
|
||||
}
|
||||
]
|
||||
},
|
||||
]
|
||||
|
||||
// 动态路由,基于用户权限动态去加载
|
||||
|
||||
@@ -1,11 +1,138 @@
|
||||
<script setup>
|
||||
<template>
|
||||
<div class="performance">
|
||||
<div class="header">
|
||||
<el-button icon="el-icon-circle-plus-outline" @click="addTest">创建测试</el-button>
|
||||
<div class="search">
|
||||
<el-input v-model="serachForm.performanceName" placeholder="根据名称搜索"></el-input>
|
||||
<el-button type="text" style="margin-left: 10px;" @click="serachList">高级搜索</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider></el-divider>
|
||||
<div class="table">
|
||||
<el-table :data="data">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column prop="id" label="ID" width="50" align="center" />
|
||||
<el-table-column prop="performanceName" label="名称" width="150" align="center" />
|
||||
<el-table-column prop="createBy" label="创建人" width="100" sortable align="center" />
|
||||
<el-table-column prop="createTime" label="创建时间" sortable align="center" />
|
||||
<el-table-column prop="updateTime" label="更新时间" sortable align="center" />
|
||||
<el-table-column prop="status" label="状态" width="50" align="center">
|
||||
<template slot-scope="scope">
|
||||
<div v-if="scope.row.status === '0'">否</div>
|
||||
<div v-else>是</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="action" label="操作" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-edit-outline" @click="hadleClickEdit(scope.row)">编辑</el-button>
|
||||
<el-button type="text" icon="el-icon-delete" style="color: red;"
|
||||
@click="hadleClickDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<div class="page">
|
||||
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
:page-size="serachForm.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="total" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getPerfromanceList, deleteTest } from '../../../api/performance'
|
||||
|
||||
export default {
|
||||
name: "Performance",
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
serachForm: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
performanceName: '', // 名称
|
||||
},
|
||||
total: 0,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getPerformaceData()
|
||||
},
|
||||
methods: {
|
||||
// 创建测试
|
||||
addTest() {
|
||||
this.$tab.openPage("新增测试", "/performance/add");
|
||||
},
|
||||
// 高级搜索
|
||||
serachList() {
|
||||
this.getPerformaceData()
|
||||
},
|
||||
// 编辑
|
||||
hadleClickEdit(val) {
|
||||
this.$tab.openPage("修改测试", "/performance/edit", { data: val });
|
||||
},
|
||||
// 删除
|
||||
hadleClickDelete(val) {
|
||||
deleteTest(String(val.id)).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({ message: '删除成功', type: 'success' })
|
||||
this.getPerformaceData()
|
||||
} else {
|
||||
this.$message({ message: '删除失败', type: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 分页
|
||||
handleSizeChange(val) {
|
||||
this.serachForm.pageSize = val
|
||||
this.getPerformaceData()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.serachForm.pageNum = val
|
||||
this.getPerformaceData()
|
||||
},
|
||||
// networking
|
||||
// 获取测试列表
|
||||
getPerformaceData() {
|
||||
getPerfromanceList(this.serachForm).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.data = res.rows
|
||||
this.total = res.total
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>性能</p>
|
||||
</template>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
.performance {
|
||||
padding: 10px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep .el-divider--horizontal {
|
||||
display: block;
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
margin: 10px 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
431
test-ui/src/views/test/performance/performanceAdd.vue
Normal file
431
test-ui/src/views/test/performance/performanceAdd.vue
Normal file
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<div class="performanceAdd">
|
||||
<div class="header">
|
||||
<div class="name">
|
||||
<div class="name-wrap">测试名称</div>
|
||||
<el-input v-model="addForm.performanceName" placeholder="请输入名称" maxlength="255" show-word-limit></el-input>
|
||||
</div>
|
||||
<div class="save">
|
||||
<el-button type="primary" plain @click="handleWithSave">保存</el-button>
|
||||
<el-button type="primary" plain @click="handleWithSaveAndExecute">保存并执行</el-button>
|
||||
<el-button type="warning" plain @click="handleWithCancel">取消</el-button>
|
||||
</div>
|
||||
<div class="date">
|
||||
<div class="date-top">
|
||||
<i class="el-icon-date"></i>
|
||||
<span class="date-title">SCHEDULER</span>
|
||||
<el-switch v-model="switchOpen" @change="switchChange"></el-switch>
|
||||
</div>
|
||||
<div class="date-bottom">
|
||||
<span>下次执行时间</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="场景配置" name="first">
|
||||
<div class="title">场景列表</div>
|
||||
<el-button style="margin-top: 10px; margin-bottom: 10px;" icon="el-icon-share"
|
||||
@click="handleClickLoad">引用接口自动化场景</el-button>
|
||||
<el-table :data="changeList">
|
||||
<el-table-column prop="name" label="场景名称" />
|
||||
<el-table-column prop="status" label="Enable/Disable" />
|
||||
<el-table-column prop="action" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-delete" style="color: red;"
|
||||
@click="hadleClickDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="压力配置" name="second">
|
||||
<div class="pressure-header">
|
||||
<div class="title">速兑通接口</div>
|
||||
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
||||
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">并发用户数</div>
|
||||
<el-input-number v-model="addForm.concurrentThreads" controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">取样器错误后</div>
|
||||
<el-select v-model="addForm.errorOperType" placeholder="请选择">
|
||||
<el-option key="1" label="继续" value="1"></el-option>
|
||||
<el-option key="2" label="开始下一个线程轮询" value="2"></el-option>
|
||||
<el-option key="3" label="停止线程" value="3"></el-option>
|
||||
<el-option key="4" label="停止测试" value="4"></el-option>
|
||||
<el-option key="5" label="立即停止测试" value="5"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">执行方式</div>
|
||||
<el-radio-group v-model="addForm.executeType">
|
||||
<el-radio-button :label="1">按持续时间</el-radio-button>
|
||||
<el-radio-button :label="2">按迭代次数</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input-number v-if="addForm.executeType === 2" style="margin-left: 10px;" v-model="addForm.loopCount"
|
||||
controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">Ramp-Up</div>
|
||||
<el-input-number v-model="addForm.rampUpSeconds" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">压测时长</div>
|
||||
<el-input-number v-model="addForm.pressureHour" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">时</div>
|
||||
<el-input-number v-model="addForm.pressureMinute" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">分</div>
|
||||
<el-input-number v-model="addForm.pressureSecond" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">Rps开启</div>
|
||||
<el-switch v-model="addForm.rpsStatus" active-value="1" inactive-value="0"></el-switch>
|
||||
<div class="pressure-warap-name">Rps上限</div>
|
||||
<el-input-number v-model="addForm.rpsLimit" controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<el-dialog title="场景列表" :visible.sync="dialogVisible" width="60%">
|
||||
<div class="search">
|
||||
<el-input v-model="searchScenen.name" style="width: 300px;" placeholder="根据ID/名称/标签 搜索"
|
||||
@blur="searchSecenList"></el-input>
|
||||
</div>
|
||||
<div class="table">
|
||||
<el-table ref="tableRef" :data="sceneList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="id" width="100" label="ID" align="center" />
|
||||
<el-table-column prop="name" label="场景名称" align="center" />
|
||||
<el-table-column prop="remark" label="标签" align="center" />
|
||||
<el-table-column prop="createTime" label="修改时间" align="center" />
|
||||
</el-table>
|
||||
<div class="page">
|
||||
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
:page-size="searchScenen.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="totalScene" />
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleWithSure">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="定时任务" :visible.sync="dialogVisibleTime" width="60%" :before-close="handleClose">
|
||||
<el-tabs v-model="activeTime">
|
||||
<el-tab-pane label="任务配置" name="first"></el-tab-pane>
|
||||
<div class="editTimeTitle">编辑定时任务</div>
|
||||
<div class="crontab-wrap">
|
||||
<div class="title">Crontab表达式</div>
|
||||
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
||||
<div class="title" style="margin-left: 50px;">定时任务开关</div>
|
||||
<el-switch v-model="addForm.crontabStatus" active-value="1" inactive-value="0"></el-switch>
|
||||
</div>
|
||||
<div class="near-time">
|
||||
<div class="title">最近5次运行时间</div>
|
||||
<div v-for="(item, index) in executionTimeList" :key="index">
|
||||
{{ item.time }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tabs>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisibleTime = false, switchOpen = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisibleTime = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addTest, addAndExecuteTest, getTestCaseList } from '../../../api/performance';
|
||||
|
||||
export default {
|
||||
name: "PerformanceAdd",
|
||||
data() {
|
||||
return {
|
||||
addForm: {
|
||||
performanceName: '', // 性能测试名称
|
||||
performanceTestCaseVOList: [], // 场景列表
|
||||
concurrentThreads: '', // 并发用户数
|
||||
errorOperType: '', // 在取样器错误后要执行的动作,1:继续;2:开始下一个线程轮询;3:停止线程;4:停止测试;5:立即停止测试
|
||||
executeType: '', // 执行方式,1:按持续时间;2:按迭代次数
|
||||
rampUpSeconds: '0', // 多少秒内线程建立完成,默认0
|
||||
pressureHour: '0', // 压测时长,时,默认0
|
||||
pressureMinute: '0', // 压测时长,分,默认0
|
||||
pressureSecond: '0', // 压测时长,秒,默认0
|
||||
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
||||
rpsLimit: '0', // 每分钟rps上限数,默认0
|
||||
crontab: '', // crontab表达式
|
||||
crontabStatus: '0', // 定时任务状态:0关闭,1开启,默认0
|
||||
loopCount: '0', // 迭代次数,默认0
|
||||
},
|
||||
activeName: 'first',
|
||||
sceneList: [], // 场景列表
|
||||
dialogVisible: false, // 场景列表
|
||||
searchScenen: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: '', // 名称
|
||||
},
|
||||
totalScene: 0,
|
||||
searchScene: '',
|
||||
changeList: [],
|
||||
multipleSelection: [],
|
||||
switchOpen: false,
|
||||
dialogVisibleTime: false,
|
||||
activeTime: 'first',
|
||||
executionTimeList: [
|
||||
{
|
||||
time: "2025-02-18 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-19 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-20 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-21 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-22 10:00:00",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getTestCaseData()
|
||||
},
|
||||
methods: {
|
||||
// 保存
|
||||
handleWithSave() {
|
||||
this.changeList.forEach(item => {
|
||||
var par = {
|
||||
testCaseId: item.id,
|
||||
status: item.status
|
||||
}
|
||||
this.addForm.performanceTestCaseVOList.push(par)
|
||||
})
|
||||
console.log(this.addForm)
|
||||
addTest(this.addForm).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({ message: '新增成功', type: 'success' })
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
} else {
|
||||
this.$message({ message: '新增失败', type: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 保存并执行
|
||||
handleWithSaveAndExecute() {
|
||||
addAndExecuteTest(this.addForm).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({ message: '新增成功', type: 'success' })
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
} else {
|
||||
this.$message({ message: '新增失败', type: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 取消
|
||||
handleWithCancel() {
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
},
|
||||
handleClick() { },
|
||||
// 加载jmx文件
|
||||
handleClickLoad() {
|
||||
this.dialogVisible = true
|
||||
},
|
||||
// 删除
|
||||
hadleClickDelete(val) {
|
||||
this.changeList = this.changeList.filter(item => item !== val);
|
||||
this.multipleSelection = this.multipleSelection.filter(item => item !== val);
|
||||
this.sceneList.forEach(row => {
|
||||
if (this.multipleSelection.includes(row)) {
|
||||
this.$refs.tableRef.toggleRowSelection(row, true);
|
||||
} else {
|
||||
this.$refs.tableRef.toggleRowSelection(row, false);
|
||||
}
|
||||
})
|
||||
},
|
||||
// netWorking
|
||||
getTestCaseData() {
|
||||
getTestCaseList(this.searchScenen).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.sceneList = res.rows
|
||||
}
|
||||
})
|
||||
},
|
||||
searchSecenList() {
|
||||
this.getTestCaseData()
|
||||
},
|
||||
// 分页
|
||||
handleSizeChange(val) {
|
||||
this.searchScenen.pageSize = val
|
||||
this.getTestCaseData()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.searchScenen.pageNum = val
|
||||
this.getTestCaseData()
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
handleWithSure() {
|
||||
this.changeList = this.multipleSelection
|
||||
this.dialogVisible = false
|
||||
},
|
||||
switchChange(val) {
|
||||
this.dialogVisibleTime = val
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisibleTime = false
|
||||
this.switchOpen = false
|
||||
},
|
||||
// 根据crontab表达式更新执行时间
|
||||
updateExecutionTimes() {
|
||||
const crontab = this.addForm.crontab.trim();
|
||||
if (!crontab) {
|
||||
this.executionTimeList = [];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析crontab表达式
|
||||
const interval = cronParser.parseExpression(crontab);
|
||||
const times = [];
|
||||
|
||||
// 获取最近三次执行时间
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const nextTime = interval.next().toDate(); // 获取 Date 对象
|
||||
const formattedTime = this.formatDate(nextTime); // 格式化时间
|
||||
times.push({ time: formattedTime });
|
||||
}
|
||||
|
||||
this.executionTimeList = times;
|
||||
} catch (error) {
|
||||
console.error('无效的crontab表达式:', error);
|
||||
this.executionTimeList = [{ time: '无效的crontab表达式' }];
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
.performanceAdd {
|
||||
padding: 20px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
width: 300px;
|
||||
align-items: center;
|
||||
|
||||
.name-wrap {
|
||||
width: 80px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
margin-top: 20px;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.pressure-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.pressure-title {
|
||||
margin-left: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.pressure-wrap {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
|
||||
.pressure-warap-name {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.date-top {
|
||||
display: flex;
|
||||
|
||||
.date-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-bottom {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.editTimeTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.crontab-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
width: 150px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.near-time {
|
||||
margin-top: 20px;
|
||||
padding: 20px 10px;
|
||||
border: solid #dcdfe6 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
431
test-ui/src/views/test/performance/performanceEdit.vue
Normal file
431
test-ui/src/views/test/performance/performanceEdit.vue
Normal file
@@ -0,0 +1,431 @@
|
||||
<template>
|
||||
<div class="performanceAdd">
|
||||
<div class="header">
|
||||
<div class="name">
|
||||
<div class="name-wrap">测试名称</div>
|
||||
<el-input v-model="addForm.performanceName" placeholder="请输入名称" maxlength="255" show-word-limit></el-input>
|
||||
</div>
|
||||
<div class="save">
|
||||
<el-button type="primary" plain @click="handleWithSave">保存</el-button>
|
||||
<el-button type="primary" plain @click="handleWithSaveAndExecute">保存并执行</el-button>
|
||||
<el-button type="warning" plain @click="handleWithCancel">取消</el-button>
|
||||
</div>
|
||||
<div class="date">
|
||||
<div class="date-top">
|
||||
<i class="el-icon-date"></i>
|
||||
<span class="date-title">SCHEDULER</span>
|
||||
<el-switch v-model="switchOpen" @change="switchChange"></el-switch>
|
||||
</div>
|
||||
<div class="date-bottom">
|
||||
<span>下次执行时间</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab">
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="场景配置" name="first">
|
||||
<div class="title">场景列表</div>
|
||||
<el-button style="margin-top: 10px; margin-bottom: 10px;" icon="el-icon-share"
|
||||
@click="handleClickLoad">引用接口自动化场景</el-button>
|
||||
<el-table :data="changeList">
|
||||
<el-table-column prop="name" label="场景名称" />
|
||||
<el-table-column prop="status" label="Enable/Disable" />
|
||||
<el-table-column prop="action" label="操作">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" icon="el-icon-delete" style="color: red;"
|
||||
@click="hadleClickDelete(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="压力配置" name="second">
|
||||
<div class="pressure-header">
|
||||
<div class="title">速兑通接口</div>
|
||||
<div class="pressure-title">并发用户数{{ addForm.concurrentThreads }},压测时长{{ addForm.pressureHour }}时{{
|
||||
addForm.pressureMinute }}分{{ addForm.pressureSecond }}秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">并发用户数</div>
|
||||
<el-input-number v-model="addForm.concurrentThreads" controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">取样器错误后</div>
|
||||
<el-select v-model="addForm.errorOperType" placeholder="请选择">
|
||||
<el-option key="1" label="继续" value="1"></el-option>
|
||||
<el-option key="2" label="开始下一个线程轮询" value="2"></el-option>
|
||||
<el-option key="3" label="停止线程" value="3"></el-option>
|
||||
<el-option key="4" label="停止测试" value="4"></el-option>
|
||||
<el-option key="5" label="立即停止测试" value="5"></el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">执行方式</div>
|
||||
<el-radio-group v-model="addForm.executeType">
|
||||
<el-radio-button :label="1">按持续时间</el-radio-button>
|
||||
<el-radio-button :label="2">按迭代次数</el-radio-button>
|
||||
</el-radio-group>
|
||||
<el-input-number v-if="addForm.executeType === 2" style="margin-left: 10px;" v-model="addForm.loopCount"
|
||||
controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">Ramp-Up</div>
|
||||
<el-input-number v-model="addForm.rampUpSeconds" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">压测时长</div>
|
||||
<el-input-number v-model="addForm.pressureHour" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">时</div>
|
||||
<el-input-number v-model="addForm.pressureMinute" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">分</div>
|
||||
<el-input-number v-model="addForm.pressureSecond" controls-position="right" :min="0"></el-input-number>
|
||||
<div class="pressure-warap-name">秒</div>
|
||||
</div>
|
||||
<div class="pressure-wrap">
|
||||
<div class="pressure-warap-name">Rps开启</div>
|
||||
<el-switch v-model="addForm.rpsStatus" active-value="1" inactive-value="0"></el-switch>
|
||||
<div class="pressure-warap-name">Rps上限</div>
|
||||
<el-input-number v-model="addForm.rpsLimit" controls-position="right" :min="0"></el-input-number>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<el-dialog title="场景列表" :visible.sync="dialogVisible" width="60%">
|
||||
<div class="search">
|
||||
<el-input v-model="searchScenen.name" style="width: 300px;" placeholder="根据ID/名称/标签 搜索"
|
||||
@blur="searchSecenList"></el-input>
|
||||
</div>
|
||||
<div class="table">
|
||||
<el-table ref="tableRef" :data="sceneList" @selection-change="handleSelectionChange">
|
||||
<el-table-column type="selection" width="55" align="center" />
|
||||
<el-table-column prop="id" width="100" label="ID" align="center" />
|
||||
<el-table-column prop="name" label="场景名称" align="center" />
|
||||
<el-table-column prop="remark" label="标签" align="center" />
|
||||
<el-table-column prop="createTime" label="修改时间" align="center" />
|
||||
</el-table>
|
||||
<div class="page">
|
||||
<el-pagination background @size-change="handleSizeChange" @current-change="handleCurrentChange"
|
||||
:page-size="searchScenen.pageSize" layout="total, sizes, prev, pager, next, jumper" :total="totalScene" />
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
<el-button type="primary" @click="handleWithSure">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<el-dialog title="定时任务" :visible.sync="dialogVisibleTime" width="60%" :before-close="handleClose">
|
||||
<el-tabs v-model="activeTime">
|
||||
<el-tab-pane label="任务配置" name="first"></el-tab-pane>
|
||||
<div class="editTimeTitle">编辑定时任务</div>
|
||||
<div class="crontab-wrap">
|
||||
<div class="title">Crontab表达式</div>
|
||||
<el-input v-model="addForm.crontab" @input="updateExecutionTimes" placeholder="请输入crontab表达式" />
|
||||
<div class="title" style="margin-left: 50px;">定时任务开关</div>
|
||||
<el-switch v-model="addForm.crontabStatus" active-value="1" inactive-value="0"></el-switch>
|
||||
</div>
|
||||
<div class="near-time">
|
||||
<div class="title">最近5次运行时间</div>
|
||||
<div v-for="(item, index) in executionTimeList" :key="index">
|
||||
{{ item.time }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tabs>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisibleTime = false, switchOpen = false">取 消</el-button>
|
||||
<el-button type="primary" @click="dialogVisibleTime = false">确 定</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { editTest, editAndExecuteTest, getTestCaseList } from '../../../api/performance';
|
||||
|
||||
export default {
|
||||
name: "PerformanceEdit",
|
||||
data() {
|
||||
return {
|
||||
addForm: {
|
||||
performanceName: '', // 性能测试名称
|
||||
performanceTestCaseVOList: [], // 场景列表
|
||||
concurrentThreads: '', // 并发用户数
|
||||
errorOperType: '', // 在取样器错误后要执行的动作,1:继续;2:开始下一个线程轮询;3:停止线程;4:停止测试;5:立即停止测试
|
||||
executeType: '', // 执行方式,1:按持续时间;2:按迭代次数
|
||||
rampUpSeconds: '0', // 多少秒内线程建立完成,默认0
|
||||
pressureHour: '0', // 压测时长,时,默认0
|
||||
pressureMinute: '0', // 压测时长,分,默认0
|
||||
pressureSecond: '0', // 压测时长,秒,默认0
|
||||
rpsStatus: '0', // rps状态:0关闭,1开启,默认0
|
||||
rpsLimit: '0', // 每分钟rps上限数,默认0
|
||||
crontab: '', // crontab表达式
|
||||
crontabStatus: '0', // 定时任务状态:0关闭,1开启,默认0
|
||||
loopCount: '0', // 迭代次数,默认0
|
||||
},
|
||||
activeName: 'first',
|
||||
sceneList: [], // 场景列表
|
||||
dialogVisible: false, // 场景列表
|
||||
searchScenen: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: '', // 名称
|
||||
},
|
||||
totalScene: 0,
|
||||
searchScene: '',
|
||||
changeList: [],
|
||||
multipleSelection: [],
|
||||
switchOpen: false,
|
||||
dialogVisibleTime: false,
|
||||
activeTime: 'first',
|
||||
executionTimeList: [
|
||||
{
|
||||
time: "2025-02-18 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-19 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-20 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-21 10:00:00",
|
||||
},
|
||||
{
|
||||
time: "2025-02-22 10:00:00",
|
||||
},
|
||||
],
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getTestCaseData()
|
||||
this.addForm = this.$route.query.data
|
||||
},
|
||||
methods: {
|
||||
// 保存
|
||||
handleWithSave() {
|
||||
this.changeList.forEach(item => {
|
||||
const par = {
|
||||
testCaseId: item.id,
|
||||
status: item.status
|
||||
}
|
||||
this.addForm.performanceTestCaseVOList.push(par)
|
||||
})
|
||||
editTest(this.addForm).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({ message: '编辑成功', type: 'success' })
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
} else {
|
||||
this.$message({ message: '编辑失败', type: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 保存并执行
|
||||
handleWithSaveAndExecute() {
|
||||
editAndExecuteTest(this.addForm).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.$message({ message: '编辑成功', type: 'success' })
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
} else {
|
||||
this.$message({ message: '编辑失败', type: 'error' })
|
||||
}
|
||||
})
|
||||
},
|
||||
// 取消
|
||||
handleWithCancel() {
|
||||
this.$tab.closeOpenPage({ path: "/performance/performance" });
|
||||
},
|
||||
handleClick() { },
|
||||
// 加载jmx文件
|
||||
handleClickLoad() {
|
||||
this.dialogVisible = true
|
||||
},
|
||||
// 删除
|
||||
hadleClickDelete(val) {
|
||||
this.changeList = this.changeList.filter(item => item !== val);
|
||||
this.multipleSelection = this.multipleSelection.filter(item => item !== val);
|
||||
this.sceneList.forEach(row => {
|
||||
if (this.multipleSelection.includes(row)) {
|
||||
this.$refs.tableRef.toggleRowSelection(row, true);
|
||||
} else {
|
||||
this.$refs.tableRef.toggleRowSelection(row, false);
|
||||
}
|
||||
})
|
||||
},
|
||||
// netWorking
|
||||
getTestCaseData() {
|
||||
getTestCaseList(this.searchScenen).then(res => {
|
||||
if (res.code === 200) {
|
||||
this.sceneList = res.rows
|
||||
}
|
||||
})
|
||||
},
|
||||
searchSecenList() {
|
||||
this.getTestCaseData()
|
||||
},
|
||||
// 分页
|
||||
handleSizeChange(val) {
|
||||
this.searchScenen.pageSize = val
|
||||
this.getTestCaseData()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
this.searchScenen.pageNum = val
|
||||
this.getTestCaseData()
|
||||
},
|
||||
handleSelectionChange(val) {
|
||||
this.multipleSelection = val;
|
||||
},
|
||||
handleWithSure() {
|
||||
this.changeList = this.multipleSelection
|
||||
this.dialogVisible = false
|
||||
},
|
||||
switchChange(val) {
|
||||
this.dialogVisibleTime = val
|
||||
},
|
||||
handleClose() {
|
||||
this.dialogVisibleTime = false
|
||||
this.switchOpen = false
|
||||
},
|
||||
// 根据crontab表达式更新执行时间
|
||||
updateExecutionTimes() {
|
||||
const crontab = this.form.crontab.trim();
|
||||
if (!crontab) {
|
||||
this.executionTimeList = [];
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 解析crontab表达式
|
||||
const interval = cronParser.parseExpression(crontab);
|
||||
const times = [];
|
||||
|
||||
// 获取最近三次执行时间
|
||||
for (let i = 0; i < 3; i++) {
|
||||
const nextTime = interval.next().toDate(); // 获取 Date 对象
|
||||
const formattedTime = this.formatDate(nextTime); // 格式化时间
|
||||
times.push({ time: formattedTime });
|
||||
}
|
||||
|
||||
this.executionTimeList = times;
|
||||
} catch (error) {
|
||||
console.error('无效的crontab表达式:', error);
|
||||
this.executionTimeList = [{ time: '无效的crontab表达式' }];
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped lang="scss">
|
||||
.performanceAdd {
|
||||
padding: 20px;
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
width: 300px;
|
||||
align-items: center;
|
||||
|
||||
.name-wrap {
|
||||
width: 80px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tab {
|
||||
margin-top: 20px;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.search {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.page {
|
||||
padding: 10px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.pressure-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.pressure-title {
|
||||
margin-left: 20px;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.pressure-wrap {
|
||||
display: flex;
|
||||
padding: 10px;
|
||||
align-items: center;
|
||||
|
||||
.pressure-warap-name {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
margin: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.date {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.date-top {
|
||||
display: flex;
|
||||
|
||||
.date-title {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin: 0px 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.date-bottom {
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.editTimeTitle {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.crontab-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.title {
|
||||
font-size: 14px;
|
||||
width: 150px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.near-time {
|
||||
margin-top: 20px;
|
||||
padding: 20px 10px;
|
||||
border: solid #dcdfe6 1px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
9
test-ui/src/views/test/performance/report.vue
Normal file
9
test-ui/src/views/test/performance/report.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<p>报告</p>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -35,7 +35,7 @@ module.exports = {
|
||||
proxy: {
|
||||
// detail: https://cli.vuejs.org/config/#devserver-proxy
|
||||
[process.env.VUE_APP_BASE_API]: {
|
||||
target: `http://localhost:8080`,
|
||||
target: `http://ah.qyyh.net:1371/prod-api/`,
|
||||
changeOrigin: true,
|
||||
pathRewrite: {
|
||||
['^' + process.env.VUE_APP_BASE_API]: ''
|
||||
|
||||
Reference in New Issue
Block a user