feat(ti): 完善触控交互执行与报告页面
增加项目执行、重新执行和执行过程入口。 按执行实例展示检测统计、结果、异常信息及录像。
This commit is contained in:
parent
1b744ff0c5
commit
622c22ff89
@ -55,11 +55,10 @@ export const projectExecute = (data) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const executeProject = (data) => {
|
export const queryExecuteInstId = (projectId) => {
|
||||||
return request({
|
return request({
|
||||||
url: '/ti/project/evaluation',
|
url: `/ti/project/queryExecuteInstId/${projectId}`,
|
||||||
method: 'post',
|
method: 'get'
|
||||||
data
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,9 +67,10 @@ export const executeProject = (data) => {
|
|||||||
* @param {*} projectId
|
* @param {*} projectId
|
||||||
* @returns
|
* @returns
|
||||||
*/
|
*/
|
||||||
export function getValuationProject(projectId) {
|
export function getValuationProject(projectId, instId) {
|
||||||
return request({
|
return request({
|
||||||
url: '/ti/project/evaluation/' + projectId,
|
url: '/ti/project/evaluation/' + projectId,
|
||||||
method: 'get'
|
method: 'get',
|
||||||
|
params: instId ? { instId } : undefined
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -77,8 +77,7 @@ import {
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
drawer: Boolean,
|
drawer: Boolean,
|
||||||
projectId: String,
|
projectId: String
|
||||||
functionIds: Array
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const emits = defineEmits(["close"]);
|
const emits = defineEmits(["close"]);
|
||||||
@ -137,10 +136,10 @@ const confirm = () => {
|
|||||||
})
|
})
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
ruleFormRef.value.resetFields();
|
ruleFormRef.value.resetFields();
|
||||||
ElMessage.success('执行成功')
|
ElMessage.success(`任务已启动,执行实例:${res.data}`)
|
||||||
emits("close");
|
emits("close");
|
||||||
} else {
|
} else {
|
||||||
ElMessage.success(res.msg)
|
ElMessage.error(res.msg)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
|||||||
@ -1,75 +1,122 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<Info :infoData="projectData" />
|
<Info :infoData="projectData" />
|
||||||
|
|
||||||
<div class="test-container">
|
<div class="test-container">
|
||||||
<div class="title">测试结果</div>
|
<div class="title">测试结果</div>
|
||||||
<div class="report-container">
|
<div class="report-container">
|
||||||
<div class="report-item">成功率 :{{ reportData?.successRate || '' }}</div>
|
<div class="report-item">总数:{{ reportData?.totalCount ?? '--' }}</div>
|
||||||
<div class="report-item">成功率得分 :{{ reportData?.successRateScore || '' }}</div>
|
<div class="report-item report-item--success">通过:{{ reportData?.successCount ?? '--' }}</div>
|
||||||
<!-- <el-button @click="handler('重庆')">生成报告</el-button>
|
<div class="report-item report-item--failed">失败:{{ reportData?.failureCount ?? '--' }}</div>
|
||||||
<el-button>导出结果</el-button> -->
|
<div class="report-item">
|
||||||
|
成功率:{{ reportData?.successRate == null ? '--' : `${reportData.successRate}%` }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table :data="tableData" style="width: 100%">
|
<el-descriptions v-if="reportData" :column="2" border class="execution-summary">
|
||||||
|
<el-descriptions-item label="执行实例">{{ reportData.instId }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="执行状态">
|
||||||
|
{{ executionStatusText[reportData.executionStatus] || reportData.executionStatus }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="开始时间">{{ formatTime(reportData.startTime) }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="结束时间">{{ formatTime(reportData.endTime) }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="tableData" style="width: 100%">
|
||||||
<el-table-column prop="aeId" width="150" show-overflow-tooltip label="结果ID" />
|
<el-table-column prop="aeId" width="150" show-overflow-tooltip label="结果ID" />
|
||||||
<el-table-column prop="content" label="功能" width="260" show-overflow-tooltip />
|
<el-table-column prop="content" label="功能" width="220" show-overflow-tooltip />
|
||||||
<el-table-column prop="status" label="状态" show-overflow-tooltip>
|
<el-table-column prop="status" label="状态" width="110">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-tag :type="statusType[row.status] || 'info'">{{ statusTypeContext[row.status] }}</el-tag>
|
<el-tag :type="statusType[row.status] || 'info'">
|
||||||
|
{{ statusTypeContext[row.status] || '未知' }}
|
||||||
|
</el-tag>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="videoPath" label="视频路径" show-overflow-tooltip>
|
<el-table-column prop="videoPath" label="录像" width="110">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-text class="mx-1" type="primary" @click="openVideoDialog(row)">播放视频</el-text>
|
<el-text
|
||||||
|
v-if="row.videoPath"
|
||||||
|
class="video-link"
|
||||||
|
type="primary"
|
||||||
|
@click="openVideoDialog(row)"
|
||||||
|
>
|
||||||
|
播放视频
|
||||||
|
</el-text>
|
||||||
|
<span v-else>--</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="itemResponseTime" label="响应时间" />
|
<el-table-column prop="itemResponseTime" label="响应时间(ms)" width="130">
|
||||||
<el-table-column prop="failureReason" label="失败原因"></el-table-column>
|
<template #default="{ row }">{{ row.itemResponseTime ?? '--' }}</template>
|
||||||
<el-table-column prop="executionDetail" label="执行详情" />
|
</el-table-column>
|
||||||
|
<el-table-column prop="failureReason" label="失败原因" min-width="180" show-overflow-tooltip>
|
||||||
|
<template #default="{ row }">{{ row.failureReason || '--' }}</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="executionDetail" label="执行详情" min-width="260" show-overflow-tooltip />
|
||||||
</el-table>
|
</el-table>
|
||||||
|
|
||||||
<el-dialog v-model="centerDialogVisible" title="播放视频" width="800" center>
|
<el-dialog v-model="centerDialogVisible" title="播放视频" width="800" center destroy-on-close>
|
||||||
<Video :src="videoSrc" />
|
<Video :src="videoSrc" />
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import Info from './info.vue';
|
import { ElMessage } from 'element-plus'
|
||||||
|
import Info from './info.vue'
|
||||||
import { getValuationProject } from '@/api/is/project'
|
import { getValuationProject } from '@/api/is/project'
|
||||||
import Video from '@/components/Video/index.vue'
|
import Video from '@/components/Video/index.vue'
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute()
|
||||||
|
|
||||||
const projectData = route.query.data
|
let projectData = {}
|
||||||
? JSON.parse(route.query.data)
|
try {
|
||||||
: {};
|
projectData = route.query.data ? JSON.parse(route.query.data) : {}
|
||||||
|
} catch {
|
||||||
|
projectData = {}
|
||||||
|
}
|
||||||
|
|
||||||
const tableData = ref([]);
|
const tableData = ref([])
|
||||||
const reportData = ref(null)
|
const reportData = ref(null)
|
||||||
const getListScheme = async () => {
|
const loading = ref(false)
|
||||||
const res = await getValuationProject(projectData.projectId)
|
|
||||||
if (res.code === 200) {
|
|
||||||
reportData.value = {
|
|
||||||
successRate: res.data.successRate,
|
|
||||||
successRateScore: res.data.successRateScore
|
|
||||||
}
|
|
||||||
tableData.value = res.data.itemDetails
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusType = {
|
const statusType = {
|
||||||
0: 'info',
|
0: 'info',
|
||||||
1: 'conductedState',
|
1: 'warning',
|
||||||
2: 'successfulState',
|
2: 'success',
|
||||||
3: 'danger'
|
3: 'danger',
|
||||||
|
4: 'danger'
|
||||||
}
|
}
|
||||||
|
|
||||||
const statusTypeContext = {
|
const statusTypeContext = {
|
||||||
0: '待评估',
|
0: '待执行',
|
||||||
1: '评估中',
|
1: '执行中',
|
||||||
2: '评估成功',
|
2: '通过',
|
||||||
3: '评估失败'
|
3: '失败',
|
||||||
|
4: '失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
const executionStatusText = {
|
||||||
|
0: '已完成',
|
||||||
|
1: '执行中',
|
||||||
|
2: '执行失败',
|
||||||
|
3: '已暂停',
|
||||||
|
4: '已终止'
|
||||||
|
}
|
||||||
|
|
||||||
|
const getReport = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await getValuationProject(projectData.projectId, route.query.instId)
|
||||||
|
if (res.code === 200) {
|
||||||
|
reportData.value = res.data
|
||||||
|
tableData.value = res.data?.itemDetails || []
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error.message || '加载测试结果失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const centerDialogVisible = ref(false)
|
const centerDialogVisible = ref(false)
|
||||||
@ -79,9 +126,13 @@ const openVideoDialog = (row) => {
|
|||||||
centerDialogVisible.value = true
|
centerDialogVisible.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
const formatTime = (value) => {
|
||||||
getListScheme()
|
if (!value) return '--'
|
||||||
})
|
const date = new Date(value)
|
||||||
|
return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false })
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(getReport)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@ -89,17 +140,12 @@ onMounted(() => {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
|
||||||
.btn-container {
|
|
||||||
background: #fff;
|
|
||||||
margin: 4px 0;
|
|
||||||
padding: 4px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.test-container {
|
.test-container {
|
||||||
height: 40px;
|
min-height: 48px;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
@ -108,54 +154,15 @@ onMounted(() => {
|
|||||||
|
|
||||||
.report-container {
|
.report-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 12px;
|
flex-wrap: wrap;
|
||||||
}
|
gap: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.context-container {
|
.report-item--success { color: #67c23a; }
|
||||||
height: calc(100% - 200px);
|
.report-item--failed { color: #f56c6c; }
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
width: 300px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
background: #fff;
|
|
||||||
padding-left: 12px;
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
margin-bottom: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.schemaList {
|
.execution-summary { margin-bottom: 16px; }
|
||||||
height: calc(100% - 40px);
|
.video-link { cursor: pointer; }
|
||||||
overflow-y: auto;
|
|
||||||
background: #fff;
|
|
||||||
border: 1px solid #e4e7ed;
|
|
||||||
|
|
||||||
.schema {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: #f0ecec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.activeItem {
|
|
||||||
background: #f0ecec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.right {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -113,7 +113,7 @@
|
|||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
align="center"
|
align="center"
|
||||||
width="320px"
|
width="380px"
|
||||||
class-name="small-padding fixed-width"
|
class-name="small-padding fixed-width"
|
||||||
fixed="right"
|
fixed="right"
|
||||||
>
|
>
|
||||||
@ -121,16 +121,23 @@
|
|||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
type="text"
|
type="text"
|
||||||
v-if="row.status == '0'"
|
v-if="row.status != '1'"
|
||||||
@click="handleExecute(row)"
|
@click="handleExecute(row)"
|
||||||
>执行
|
>{{ row.status == '0' ? '执行' : '重新执行' }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-if="row.status != '0'"
|
||||||
|
size="mini"
|
||||||
|
type="text"
|
||||||
|
@click="handleOpenExecution(row)"
|
||||||
|
>执行过程
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.status == '2'"
|
v-if="row.status == '2'"
|
||||||
size="mini"
|
size="mini"
|
||||||
type="text"
|
type="text"
|
||||||
@click="handleEvaluate(row)"
|
@click="handleEvaluate(row)"
|
||||||
>评价
|
>查看报告
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
size="mini"
|
size="mini"
|
||||||
@ -329,11 +336,40 @@
|
|||||||
</template>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
<el-drawer
|
||||||
|
v-model="executionOpen"
|
||||||
|
:destroy-on-close="true"
|
||||||
|
:size="580"
|
||||||
|
:title="`${currentProject.projectName || ''}的执行过程`"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<el-alert
|
||||||
|
v-if="latestInstId"
|
||||||
|
:title="`执行实例:${latestInstId}`"
|
||||||
|
type="info"
|
||||||
|
:closable="false"
|
||||||
|
style="margin-bottom: 12px"
|
||||||
|
/>
|
||||||
|
<el-card
|
||||||
|
v-for="item in executionFlows"
|
||||||
|
:key="item.id"
|
||||||
|
shadow="hover"
|
||||||
|
style="width: 100%; margin-bottom: 10px"
|
||||||
|
>
|
||||||
|
<div class="flex align-center" style="cursor: pointer" @click="handleFlowView(item)">
|
||||||
|
<div>{{ item.name }}</div>
|
||||||
|
<div class="flex align-center" style="margin-left: 10px">
|
||||||
|
<el-icon><View /></el-icon>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
<el-empty v-if="!executionFlows.length" description="暂无执行流程" />
|
||||||
|
</el-drawer>
|
||||||
|
|
||||||
<Run
|
<Run
|
||||||
:drawer="executeOpen"
|
:drawer="executeOpen"
|
||||||
@close="closeRun"
|
@close="closeRun"
|
||||||
:projectId="executeProjectId"
|
:projectId="executeProjectId"
|
||||||
:functionIds="functionIds"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -345,8 +381,16 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
|||||||
import { getTreeDataApi } from '@/api/is/func/index'
|
import { getTreeDataApi } from '@/api/is/func/index'
|
||||||
import { getChildrenByParentIdWithLevelLimitApi } from "@/api/evaluate/indicator.js";
|
import { getChildrenByParentIdWithLevelLimitApi } from "@/api/evaluate/indicator.js";
|
||||||
import { getFunction } from "@/api/is/func/index.js";
|
import { getFunction } from "@/api/is/func/index.js";
|
||||||
import { addProject, getProjectList, updateProject, deleteProject } from "@/api/is/project/index.js";
|
import {
|
||||||
|
addProject,
|
||||||
|
getProjectList,
|
||||||
|
updateProject,
|
||||||
|
deleteProject,
|
||||||
|
getExecuteRunParams,
|
||||||
|
queryExecuteInstId,
|
||||||
|
} from "@/api/is/project/index.js";
|
||||||
import Run from "@/views/is/project/evaluate/Run.vue";
|
import Run from "@/views/is/project/evaluate/Run.vue";
|
||||||
|
import { appendParamsToPath } from "@/utils/fn.js";
|
||||||
|
|
||||||
const topContainerRef = ref();
|
const topContainerRef = ref();
|
||||||
const containerHeight = useContainerHeight(topContainerRef);
|
const containerHeight = useContainerHeight(topContainerRef);
|
||||||
@ -618,19 +662,9 @@ const getFuncList = (value) => {
|
|||||||
|
|
||||||
const executeOpen = ref(false);
|
const executeOpen = ref(false);
|
||||||
const executeProjectId = ref(null);
|
const executeProjectId = ref(null);
|
||||||
const functionIds = ref([])
|
|
||||||
const handleExecute = (row) => {
|
const handleExecute = (row) => {
|
||||||
executeOpen.value = true;
|
executeOpen.value = true;
|
||||||
executeProjectId.value = row.projectId;
|
executeProjectId.value = row.projectId;
|
||||||
// 后端临时应付检查添加,后续可删除
|
|
||||||
let arr = []
|
|
||||||
try {
|
|
||||||
arr = JSON.parse(row.testFuncIds);
|
|
||||||
} catch {
|
|
||||||
arr = []
|
|
||||||
} finally {
|
|
||||||
functionIds.value = arr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@ -644,6 +678,47 @@ const handleEvaluate = (row) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const executionOpen = ref(false)
|
||||||
|
const currentProject = ref({})
|
||||||
|
const executionFlows = ref([])
|
||||||
|
const latestInstId = ref('')
|
||||||
|
|
||||||
|
const handleOpenExecution = async (row) => {
|
||||||
|
currentProject.value = row
|
||||||
|
executionOpen.value = true
|
||||||
|
latestInstId.value = ''
|
||||||
|
executionFlows.value = []
|
||||||
|
try {
|
||||||
|
const [flowRes, instRes] = await Promise.all([
|
||||||
|
getExecuteRunParams({ projectId: row.projectId }),
|
||||||
|
queryExecuteInstId(row.projectId),
|
||||||
|
])
|
||||||
|
latestInstId.value = instRes.data || ''
|
||||||
|
executionFlows.value = (flowRes.data || []).map((item) => ({
|
||||||
|
id: item.itemId,
|
||||||
|
name: item.itemName || '触控交互流程',
|
||||||
|
}))
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(error.message || '加载执行过程失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleFlowView = (item) => {
|
||||||
|
if (!latestInstId.value) {
|
||||||
|
ElMessage.warning('未找到执行实例')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const params = {
|
||||||
|
instId: latestInstId.value,
|
||||||
|
taskId: currentProject.value.projectId,
|
||||||
|
itemId: item.id,
|
||||||
|
name: item.name,
|
||||||
|
state: '0',
|
||||||
|
isLog: true,
|
||||||
|
}
|
||||||
|
window.open(appendParamsToPath('/flow', params), '_blank')
|
||||||
|
}
|
||||||
|
|
||||||
const closeRun = () => {
|
const closeRun = () => {
|
||||||
executeOpen.value = false
|
executeOpen.value = false
|
||||||
getList()
|
getList()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user