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({
|
||||
url: '/ti/project/evaluation',
|
||||
method: 'post',
|
||||
data
|
||||
url: `/ti/project/queryExecuteInstId/${projectId}`,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@ -68,9 +67,10 @@ export const executeProject = (data) => {
|
||||
* @param {*} projectId
|
||||
* @returns
|
||||
*/
|
||||
export function getValuationProject(projectId) {
|
||||
export function getValuationProject(projectId, instId) {
|
||||
return request({
|
||||
url: '/ti/project/evaluation/' + projectId,
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: instId ? { instId } : undefined
|
||||
})
|
||||
}
|
||||
@ -77,8 +77,7 @@ import {
|
||||
|
||||
const props = defineProps({
|
||||
drawer: Boolean,
|
||||
projectId: String,
|
||||
functionIds: Array
|
||||
projectId: String
|
||||
});
|
||||
|
||||
const emits = defineEmits(["close"]);
|
||||
@ -137,10 +136,10 @@ const confirm = () => {
|
||||
})
|
||||
if (res.code === 200) {
|
||||
ruleFormRef.value.resetFields();
|
||||
ElMessage.success('执行成功')
|
||||
ElMessage.success(`任务已启动,执行实例:${res.data}`)
|
||||
emits("close");
|
||||
} else {
|
||||
ElMessage.success(res.msg)
|
||||
ElMessage.error(res.msg)
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
||||
@ -1,75 +1,122 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<Info :infoData="projectData" />
|
||||
|
||||
<div class="test-container">
|
||||
<div class="title">测试结果</div>
|
||||
<div class="report-container">
|
||||
<div class="report-item">成功率 :{{ reportData?.successRate || '' }}</div>
|
||||
<div class="report-item">成功率得分 :{{ reportData?.successRateScore || '' }}</div>
|
||||
<!-- <el-button @click="handler('重庆')">生成报告</el-button>
|
||||
<el-button>导出结果</el-button> -->
|
||||
<div class="report-item">总数:{{ reportData?.totalCount ?? '--' }}</div>
|
||||
<div class="report-item report-item--success">通过:{{ reportData?.successCount ?? '--' }}</div>
|
||||
<div class="report-item report-item--failed">失败:{{ reportData?.failureCount ?? '--' }}</div>
|
||||
<div class="report-item">
|
||||
成功率:{{ reportData?.successRate == null ? '--' : `${reportData.successRate}%` }}
|
||||
</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="content" label="功能" width="260" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" show-overflow-tooltip>
|
||||
<el-table-column prop="content" label="功能" width="220" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="110">
|
||||
<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>
|
||||
</el-table-column>
|
||||
<el-table-column prop="videoPath" label="视频路径" show-overflow-tooltip>
|
||||
<el-table-column prop="videoPath" label="录像" width="110">
|
||||
<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>
|
||||
</el-table-column>
|
||||
<el-table-column prop="itemResponseTime" label="响应时间" />
|
||||
<el-table-column prop="failureReason" label="失败原因"></el-table-column>
|
||||
<el-table-column prop="executionDetail" label="执行详情" />
|
||||
<el-table-column prop="itemResponseTime" label="响应时间(ms)" width="130">
|
||||
<template #default="{ row }">{{ row.itemResponseTime ?? '--' }}</template>
|
||||
</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-dialog v-model="centerDialogVisible" title="播放视频" width="800" center>
|
||||
<el-dialog v-model="centerDialogVisible" title="播放视频" width="800" center destroy-on-close>
|
||||
<Video :src="videoSrc" />
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Info from './info.vue';
|
||||
import { ElMessage } from 'element-plus'
|
||||
import Info from './info.vue'
|
||||
import { getValuationProject } from '@/api/is/project'
|
||||
import Video from '@/components/Video/index.vue'
|
||||
|
||||
const route = useRoute();
|
||||
const route = useRoute()
|
||||
|
||||
const projectData = route.query.data
|
||||
? JSON.parse(route.query.data)
|
||||
: {};
|
||||
|
||||
const tableData = ref([]);
|
||||
const reportData = ref(null)
|
||||
const getListScheme = async () => {
|
||||
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
|
||||
}
|
||||
let projectData = {}
|
||||
try {
|
||||
projectData = route.query.data ? JSON.parse(route.query.data) : {}
|
||||
} catch {
|
||||
projectData = {}
|
||||
}
|
||||
|
||||
const tableData = ref([])
|
||||
const reportData = ref(null)
|
||||
const loading = ref(false)
|
||||
|
||||
const statusType = {
|
||||
0: 'info',
|
||||
1: 'conductedState',
|
||||
2: 'successfulState',
|
||||
3: 'danger'
|
||||
1: 'warning',
|
||||
2: 'success',
|
||||
3: 'danger',
|
||||
4: 'danger'
|
||||
}
|
||||
|
||||
const statusTypeContext = {
|
||||
0: '待评估',
|
||||
1: '评估中',
|
||||
2: '评估成功',
|
||||
3: '评估失败'
|
||||
0: '待执行',
|
||||
1: '执行中',
|
||||
2: '通过',
|
||||
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)
|
||||
@ -79,9 +126,13 @@ const openVideoDialog = (row) => {
|
||||
centerDialogVisible.value = true
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getListScheme()
|
||||
})
|
||||
const formatTime = (value) => {
|
||||
if (!value) return '--'
|
||||
const date = new Date(value)
|
||||
return Number.isNaN(date.getTime()) ? value : date.toLocaleString('zh-CN', { hour12: false })
|
||||
}
|
||||
|
||||
onMounted(getReport)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -89,17 +140,12 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
.btn-container {
|
||||
background: #fff;
|
||||
margin: 4px 0;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
.test-container {
|
||||
height: 40px;
|
||||
min-height: 48px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 16px;
|
||||
|
||||
.title {
|
||||
font-size: 20px;
|
||||
@ -108,54 +154,15 @@ onMounted(() => {
|
||||
|
||||
.report-container {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
}
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.context-container {
|
||||
height: calc(100% - 200px);
|
||||
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;
|
||||
.report-item--success { color: #67c23a; }
|
||||
.report-item--failed { color: #f56c6c; }
|
||||
}
|
||||
|
||||
.schemaList {
|
||||
height: calc(100% - 40px);
|
||||
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;
|
||||
}
|
||||
}
|
||||
.execution-summary { margin-bottom: 16px; }
|
||||
.video-link { cursor: pointer; }
|
||||
}
|
||||
</style>
|
||||
@ -113,7 +113,7 @@
|
||||
<el-table-column
|
||||
label="操作"
|
||||
align="center"
|
||||
width="320px"
|
||||
width="380px"
|
||||
class-name="small-padding fixed-width"
|
||||
fixed="right"
|
||||
>
|
||||
@ -121,16 +121,23 @@
|
||||
<el-button
|
||||
size="mini"
|
||||
type="text"
|
||||
v-if="row.status == '0'"
|
||||
v-if="row.status != '1'"
|
||||
@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
|
||||
v-if="row.status == '2'"
|
||||
size="mini"
|
||||
type="text"
|
||||
@click="handleEvaluate(row)"
|
||||
>评价
|
||||
>查看报告
|
||||
</el-button>
|
||||
<el-button
|
||||
size="mini"
|
||||
@ -329,11 +336,40 @@
|
||||
</template>
|
||||
</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
|
||||
:drawer="executeOpen"
|
||||
@close="closeRun"
|
||||
:projectId="executeProjectId"
|
||||
:functionIds="functionIds"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -345,8 +381,16 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { getTreeDataApi } from '@/api/is/func/index'
|
||||
import { getChildrenByParentIdWithLevelLimitApi } from "@/api/evaluate/indicator.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 { appendParamsToPath } from "@/utils/fn.js";
|
||||
|
||||
const topContainerRef = ref();
|
||||
const containerHeight = useContainerHeight(topContainerRef);
|
||||
@ -618,19 +662,9 @@ const getFuncList = (value) => {
|
||||
|
||||
const executeOpen = ref(false);
|
||||
const executeProjectId = ref(null);
|
||||
const functionIds = ref([])
|
||||
const handleExecute = (row) => {
|
||||
executeOpen.value = true;
|
||||
executeProjectId.value = row.projectId;
|
||||
// 后端临时应付检查添加,后续可删除
|
||||
let arr = []
|
||||
try {
|
||||
arr = JSON.parse(row.testFuncIds);
|
||||
} catch {
|
||||
arr = []
|
||||
} finally {
|
||||
functionIds.value = arr
|
||||
}
|
||||
}
|
||||
|
||||
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 = () => {
|
||||
executeOpen.value = false
|
||||
getList()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user