0928
This commit is contained in:
@ -64,16 +64,40 @@
|
||||
<p class="stat-label">本月完成试验</p>
|
||||
<p class="stat-value">
|
||||
{{ statData.completed
|
||||
}}<span class="stat-change" :class="statData.completedGrowth >= 0 ? 'up' : 'down'">
|
||||
较上月 {{ statData.completedGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.completedGrowth) }}%
|
||||
}}<span
|
||||
class="stat-change"
|
||||
:class="{
|
||||
'green': statData.completedGrowth > 0,
|
||||
'gray': Math.abs(statData.completedGrowth - 100) < 0.01,
|
||||
'red': statData.completedGrowth < 0
|
||||
}"
|
||||
>
|
||||
较上月
|
||||
{{
|
||||
Math.abs(statData.completedGrowth - 100) < 0.01
|
||||
? '无增长'
|
||||
: (statData.completedGrowth >= 0 ? '↑' : '↓') + Math.abs(statData.completedGrowth) + '%'
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<p class="stat-label">试验通过率</p>
|
||||
<p class="stat-value">
|
||||
{{ statData.passRate }}%<span class="stat-change" :class="statData.passRateGrowth >= 0 ? 'up' : 'down'">
|
||||
较上月 {{ statData.passRateGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.passRateGrowth) }}%
|
||||
{{ statData.passRate }}%<span
|
||||
class="stat-change"
|
||||
:class="{
|
||||
'green': statData.passRateGrowth > 0,
|
||||
'gray': Math.abs(statData.passRateGrowth - 100) < 0.01,
|
||||
'red': statData.passRateGrowth < 0
|
||||
}"
|
||||
>
|
||||
较上月
|
||||
{{
|
||||
Math.abs(statData.passRateGrowth - 100) < 0.01
|
||||
? '无增长'
|
||||
: (statData.passRateGrowth >= 0 ? '↑' : '↓') + Math.abs(statData.passRateGrowth) + '%'
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -85,8 +109,20 @@
|
||||
<p class="stat-label">平均试验时长</p>
|
||||
<p class="stat-value">
|
||||
{{ statData.avgDuration
|
||||
}}<span class="stat-change" :class="statData.avgDurationGrowth >= 0 ? 'down' : 'up'">
|
||||
较上月 {{ statData.avgDurationGrowth >= 0 ? '↑' : '↓' }}{{ Math.abs(statData.avgDurationGrowth) }}分钟
|
||||
}}<span
|
||||
class="stat-change"
|
||||
:class="{
|
||||
'green': statData.avgDurationGrowth > 100, // 数据大于100(上升)时显示绿色
|
||||
'gray': Math.abs(statData.avgDurationGrowth - 100) < 0.01,
|
||||
'red': statData.avgDurationGrowth < 100 // 数据小于100(下降)时显示红色
|
||||
}"
|
||||
>
|
||||
较上月
|
||||
{{
|
||||
Math.abs(statData.avgDurationGrowth - 100) < 0.01
|
||||
? '无增长'
|
||||
: (statData.avgDurationGrowth <= 0 ? '↓' : '↑') + Math.abs(statData.avgDurationGrowth) + '%'
|
||||
}}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
@ -134,11 +170,11 @@
|
||||
<!-- 试验结果 -->
|
||||
<div class="test-result" :class="{ 'failure-analysis': record.status === 'failed' }">
|
||||
<h4 class="result-title">
|
||||
{{ record.status === 'failed' ? '失败原因分析' : '试验结果' }}
|
||||
{{ record.status === '3' ? '失败原因分析' : '试验结果' }}
|
||||
</h4>
|
||||
|
||||
<p class="result-content">
|
||||
{{ record.status === 'failed' ? record.failReason || '未提供失败原因' : record.testFinal || '试验完成,未提供详细结果' }}
|
||||
{{ record.status === '3' ? record.failReason || '未提供失败原因' : record.testFinal || '试验未完成,未提供详细结果' }}
|
||||
</p>
|
||||
|
||||
<p class="result-details" v-if="record.status !== 'failed'">
|
||||
@ -435,6 +471,9 @@ const getStatisticsData = async () => {
|
||||
// 处理增长率数据
|
||||
statData.value.completedGrowth = parseInt(apiData.finishCountAdd) || 0;
|
||||
statData.value.passRateGrowth = parseFloat(apiData.passValueAdd) || 0;
|
||||
|
||||
// 对于平均试验时长,时长减少是好的,所以我们需要反转逻辑
|
||||
// 这里直接使用从API获取的增长率值,但在显示时根据正负来判断样式
|
||||
statData.value.avgDurationGrowth = parseFloat(apiData.averageTestTimeAdd) || 0;
|
||||
} else {
|
||||
console.warn('获取统计数据失败或返回格式不正确:', response);
|
||||
@ -470,8 +509,14 @@ const formatDateTime = (dateTimeString) => {
|
||||
|
||||
// 12. 辅助方法:获取节点状态类名
|
||||
const getNodeStatusClass = (nodeStatus, recordStatus) => {
|
||||
// 节点状态: 2-未完成, 其他假设为已完成
|
||||
// 节点状态: 2-未完成, 3-失败, 其他假设为已完成
|
||||
// 记录状态: 'failed'-失败, 'completed'-完成, 其他为进行中
|
||||
|
||||
// 如果节点本身状态为3(失败),直接返回failed类名
|
||||
if (nodeStatus === '3') {
|
||||
return 'failed';
|
||||
}
|
||||
|
||||
if (recordStatus === 'failed') {
|
||||
// 如果记录失败,找到失败阶段的节点
|
||||
return nodeStatus === '2' ? 'failed' : 'active';
|
||||
@ -486,6 +531,12 @@ const getNodeStatusClass = (nodeStatus, recordStatus) => {
|
||||
// 13. 辅助方法:获取进度线状态类名
|
||||
const getLineStatusClass = (index, nodes, recordStatus) => {
|
||||
// 如果记录失败,找到第一个未完成的节点前的线为active
|
||||
|
||||
// 检查当前节点状态是否为3(失败)
|
||||
if (nodes[index].status === '3') {
|
||||
return 'failed';
|
||||
}
|
||||
|
||||
if (recordStatus === 'failed') {
|
||||
return nodes[index].status !== '2' ? 'active' : 'failed';
|
||||
} else if (recordStatus === 'completed') {
|
||||
@ -962,6 +1013,21 @@ onMounted(async () => {
|
||||
color: #ff7d00;
|
||||
}
|
||||
|
||||
.stat-change.green {
|
||||
background-color: #e6ffed;
|
||||
color: #00b42a;
|
||||
}
|
||||
|
||||
.stat-change.red {
|
||||
background-color: #fff1f0;
|
||||
color: #f5222d;
|
||||
}
|
||||
|
||||
.stat-change.gray {
|
||||
background-color: #f5f5f5;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
/* 13. 试验记录样式 */
|
||||
.test-records {
|
||||
display: flex;
|
||||
@ -1053,17 +1119,17 @@ onMounted(async () => {
|
||||
}
|
||||
|
||||
.progress-step.active .step-number {
|
||||
background-color: #165dff;
|
||||
background-color: #00b42a;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.progress-step.active .step-name {
|
||||
color: #165dff;
|
||||
color: #00b42a;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.progress-line.active {
|
||||
background-color: #165dff;
|
||||
background-color: #00b42a;
|
||||
}
|
||||
|
||||
.progress-step.failed .step-number {
|
||||
|
||||
@ -72,6 +72,28 @@
|
||||
</div>
|
||||
|
||||
<div class="task-details">
|
||||
<!-- 失败卡片特殊展示 -->
|
||||
<div v-if="task.status === '3'" class="failed-task-details">
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">失败时间</span>
|
||||
<span class="detail-value">{{ task.failTime || '未记录' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">试验阶段</span>
|
||||
<span class="detail-value">{{ task.testStage || '未记录' }}</span>
|
||||
</div>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">执行人</span>
|
||||
<span class="detail-value">{{ task.executor }}</span>
|
||||
</div>
|
||||
<div class="detail-item failed-reason-item">
|
||||
<span class="detail-label">失败原因</span>
|
||||
<span class="detail-value failed-reason">{{ task.originalData?.failReason || '未填写' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 其他状态的卡片展示 -->
|
||||
<div v-else>
|
||||
<div class="detail-item">
|
||||
<span class="detail-label">计划时间</span>
|
||||
<span class="detail-value">{{ task.planTime }}</span>
|
||||
@ -103,19 +125,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 已完成/失败结果 -->
|
||||
<div v-if="task.status === '5' || task.status === '3'" class="task-result">
|
||||
<!-- 已完成结果 -->
|
||||
<div v-if="task.status === '5'" class="task-result">
|
||||
<span class="detail-label">结果</span>
|
||||
<span class="detail-value" :class="task.resultClass">{{ task.result }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-actions">
|
||||
<!-- 失败卡片的特殊操作按钮 -->
|
||||
<div v-if="task.status === '3'" class="failed-task-actions">
|
||||
<el-button type="text" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
|
||||
<el-button type="primary" :class="task.actionClass" @click="handleAction(task)">
|
||||
{{ task.actionText }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<!-- 其他状态的操作按钮 -->
|
||||
<div v-else>
|
||||
<el-button type="text" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
|
||||
<el-button type="primary" :class="task.actionClass" @click="handleAction(task)">
|
||||
{{ task.actionText }}
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -372,6 +406,29 @@
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 日志弹窗 -->
|
||||
<el-dialog v-model="logsDialogVisible" title="任务执行日志" width="700px" :close-on-click-modal="false">
|
||||
<div v-if="!logsLoading" class="logs-container">
|
||||
<div v-if="logsData.length > 0" class="logs-list">
|
||||
<div v-for="(log, index) in logsData" :key="index" class="log-item">
|
||||
<div class="log-time">{{ log.timestamp || '-' }}</div>
|
||||
<div class="log-content">{{ log.content || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="no-logs">
|
||||
<el-empty description="暂无执行日志" />
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="loading-logs">
|
||||
<el-skeleton :count="5" class="log-skeleton" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="logsDialogVisible = false">关闭</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -385,7 +442,12 @@ import { shiyanlist } from '@/api/zhinengxunjian/shiyan';
|
||||
import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian/index';
|
||||
import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
|
||||
// 引入Element Plus组件(提示/空状态/骨架屏/弹窗)
|
||||
import { ElMessage, ElEmpty, ElSkeleton, ElForm, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage, ElEmpty, ElSkeleton, ElForm, ElMessageBox, ElDialog } from 'element-plus';
|
||||
|
||||
// 日志弹窗相关变量
|
||||
const logsDialogVisible = ref(false);
|
||||
const logsData = ref([]);
|
||||
const logsLoading = ref(false);
|
||||
|
||||
/**
|
||||
* 根据任务ID获取完整的任务详情数据
|
||||
@ -696,9 +758,9 @@ const mapApiToView = (apiData) => {
|
||||
},
|
||||
'3': {
|
||||
statusText: '失败',
|
||||
cardClass: 'card-delayed',
|
||||
tagClass: 'tag-delayed',
|
||||
actionText: '重试',
|
||||
cardClass: 'card-failed',
|
||||
tagClass: 'tag-failed',
|
||||
actionText: '重新执行',
|
||||
actionClass: 'reschedule-btn',
|
||||
result: '失败',
|
||||
resultClass: 'result-abnormal'
|
||||
@ -755,6 +817,46 @@ const mapApiToView = (apiData) => {
|
||||
executorName = getUserById(apiData.person);
|
||||
}
|
||||
|
||||
// 格式化失败时间
|
||||
const formatFailTime = (timeStr) => {
|
||||
if (timeStr) {
|
||||
const date = new Date(timeStr);
|
||||
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String(
|
||||
date.getHours()
|
||||
).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
}
|
||||
return '未记录';
|
||||
};
|
||||
|
||||
// 生成试验阶段信息
|
||||
const getTestStage = () => {
|
||||
try {
|
||||
// 优先查找nodes数组中status为2的第一条数据
|
||||
if (apiData && apiData.nodes && Array.isArray(apiData.nodes)) {
|
||||
const firstStatusTwoNode = apiData.nodes.find((node) => {
|
||||
// 确保node存在且有status属性
|
||||
if (!node || node.status === undefined) return false;
|
||||
// 处理status可能是字符串或数字的情况
|
||||
return node.status === '2' || node.status === 2;
|
||||
});
|
||||
if (firstStatusTwoNode && firstStatusTwoNode.name) {
|
||||
return firstStatusTwoNode.name;
|
||||
}
|
||||
}
|
||||
// 如果没有找到符合条件的nodes数据,检查是否有明确的试验阶段信息
|
||||
if (apiData && apiData.testStage) {
|
||||
return apiData.testStage;
|
||||
}
|
||||
// 如果没有明确的阶段信息,尝试从关联计划中获取
|
||||
if (apiData && apiData.testPlan && apiData.testPlan.stage) {
|
||||
return apiData.testPlan.stage;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取试验阶段信息失败:', error);
|
||||
}
|
||||
return '未记录';
|
||||
};
|
||||
|
||||
return {
|
||||
id: apiData.id, // 任务ID(v-for的key,唯一标识)
|
||||
title: apiData.taskName || '未命名任务', // 任务名称
|
||||
@ -774,7 +876,10 @@ const mapApiToView = (apiData) => {
|
||||
actionText: statusConfig.actionText,
|
||||
actionClass: statusConfig.actionClass,
|
||||
testFinal: apiData.testFinal, // 结果(用于详情页)
|
||||
originalData: apiData // 保存原始数据,用于后续操作
|
||||
originalData: apiData, // 保存原始数据,用于后续操作
|
||||
// 失败卡片特有字段
|
||||
failTime: formatFailTime(apiData.failTime),
|
||||
testStage: getTestStage()
|
||||
};
|
||||
};
|
||||
|
||||
@ -880,11 +985,44 @@ const handleAction = async (task) => {
|
||||
resultType = 'normal'; // 现在在外部作用域中定义
|
||||
} catch (error) {
|
||||
if (error === 'cancel') {
|
||||
// 用户点击取消(异常)
|
||||
updateParams.status = '5';
|
||||
updateParams.progress = 100;
|
||||
// 用户点击取消(异常),弹出失败原因输入框
|
||||
try {
|
||||
const failReasonResult = await ElMessageBox.prompt('请输入失败原因', '试验异常', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
inputPlaceholder: '请详细描述失败原因...',
|
||||
inputValidator: (value) => {
|
||||
if (!value || value.trim() === '') {
|
||||
return '失败原因不能为空';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 用户输入了失败原因并确认
|
||||
updateParams.status = '3';
|
||||
updateParams.progress = '';
|
||||
updateParams.testFinal = '异常';
|
||||
resultType = 'abnormal'; // 现在在外部作用域中定义
|
||||
updateParams.failReason = failReasonResult.value; // 绑定失败原因参数
|
||||
updateParams.failTime = formatLocalDateTime(new Date()); // 记录失败时间
|
||||
resultType = 'abnormal';
|
||||
|
||||
// 将第一条未完成的步骤状态改为3(失败)
|
||||
if (taskDetails.nodes && Array.isArray(taskDetails.nodes)) {
|
||||
const firstUnfinishedNode = taskDetails.nodes.find((node) => {
|
||||
return node.status === '2' || node.status === 2;
|
||||
});
|
||||
if (firstUnfinishedNode) {
|
||||
firstUnfinishedNode.status = '3';
|
||||
// 确保更新到updateParams中
|
||||
updateParams.nodes = taskDetails.nodes;
|
||||
}
|
||||
}
|
||||
} catch (innerError) {
|
||||
// 用户取消了失败原因输入
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
// 关闭弹窗,不执行操作
|
||||
return;
|
||||
@ -895,50 +1033,51 @@ const handleAction = async (task) => {
|
||||
switch (task.status) {
|
||||
case '1': // 待执行 → 开始执行(状态改为4)
|
||||
updateParams.status = '4';
|
||||
updateParams.progress = 10; // 初始进度10%
|
||||
// 设置开始时间为当前时间
|
||||
updateParams.planBeginTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
updateParams.progress = 0; // 初始进度10%
|
||||
// 设置开始时间为当前时间(使用本地时间而非UTC时间)
|
||||
updateParams.planBeginTime = formatLocalDateTime(new Date());
|
||||
break;
|
||||
case '2': // 已延期 → 重新安排(状态改为1,重置时间)
|
||||
updateParams.status = '1';
|
||||
updateParams.beginTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
updateParams.beginTime = formatLocalDateTime(new Date());
|
||||
break;
|
||||
case '3': // 失败 → 重试(状态改为1)
|
||||
updateParams.status = '1';
|
||||
// 清空失败相关字段,使用适合各字段数据类型的默认值
|
||||
updateParams.failReason = '';
|
||||
updateParams.failTime = ''; // 时间类型字段使用null
|
||||
updateParams.failPhase = ''; // 整数类型字段使用0
|
||||
|
||||
// 将失败的步骤状态改回2(未完成)
|
||||
if (taskDetails.nodes && Array.isArray(taskDetails.nodes)) {
|
||||
taskDetails.nodes.forEach((node) => {
|
||||
if (node.status === '3' || node.status === 3) {
|
||||
node.status = '2';
|
||||
}
|
||||
});
|
||||
// 确保更新到updateParams中
|
||||
updateParams.nodes = taskDetails.nodes;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 调用更新接口
|
||||
// 对于执行中状态('4')的任务,预先设置好时间字段
|
||||
if (task.status === '4') {
|
||||
// 根据结果类型设置相应的时间(使用本地时间而非UTC时间)
|
||||
if (resultType === 'normal') {
|
||||
updateParams.planFinishTime = formatLocalDateTime(new Date());
|
||||
} else if (resultType === 'abnormal') {
|
||||
updateParams.failTime = formatLocalDateTime(new Date());
|
||||
}
|
||||
}
|
||||
|
||||
// 调用更新接口(只调用一次)
|
||||
const response = await updatesyrenwu(updateParams);
|
||||
if (response.code === 200) {
|
||||
ElMessage.success(`任务${task.actionText}成功`);
|
||||
|
||||
// 只有在接口调用成功后才设置时间
|
||||
if (task.status === '4') {
|
||||
// 获取最新的任务详情,确保包含所有字段
|
||||
const latestTaskDetails = await getTaskDetails(task.id);
|
||||
if (latestTaskDetails) {
|
||||
// 创建包含所有字段的新参数对象
|
||||
const timeUpdateParams = {
|
||||
...latestTaskDetails,
|
||||
id: task.id
|
||||
};
|
||||
|
||||
// 根据结果类型设置相应的时间(现在resultType已在作用域内)
|
||||
if (resultType === 'normal') {
|
||||
timeUpdateParams.planFinishTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
} else if (resultType === 'abnormal') {
|
||||
timeUpdateParams.failTime = new Date().toISOString().slice(0, 16).replace('T', ' ');
|
||||
}
|
||||
|
||||
// 再次调用接口更新时间
|
||||
await updatesyrenwu(timeUpdateParams);
|
||||
}
|
||||
}
|
||||
|
||||
getTaskList(); // 刷新任务列表
|
||||
} else {
|
||||
ElMessage.error(`任务${task.actionText}失败:` + response.msg);
|
||||
@ -948,6 +1087,20 @@ const handleAction = async (task) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 格式化本地日期时间为 'YYYY-MM-DD HH:mm' 格式
|
||||
* @param {Date} date - 日期对象
|
||||
* @returns {string} 格式化后的日期时间字符串
|
||||
*/
|
||||
const formatLocalDateTime = (date) => {
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* 打开创建任务弹窗
|
||||
*/
|
||||
@ -1049,7 +1202,7 @@ const handleSaveTask = async () => {
|
||||
progress: 0, // 初始进度0%
|
||||
failReason: '',
|
||||
failTime: '', // 失败时间(新增时为空)
|
||||
failPhase: 0,
|
||||
failPhase: '',
|
||||
faileAnalyze: '',
|
||||
faileTips: '',
|
||||
testLongTime: 0,
|
||||
@ -1267,6 +1420,10 @@ const getTaskStatusClass = (status) => {
|
||||
box-shadow: 0 4px 16px rgba(82, 196, 26, 0.15);
|
||||
}
|
||||
|
||||
.card-failed {
|
||||
box-shadow: 0 4px 16px rgba(255, 77, 79, 0.15);
|
||||
}
|
||||
|
||||
/* 左侧状态线颜色 */
|
||||
.card-pending::before {
|
||||
background-color: #1677ff;
|
||||
@ -1280,6 +1437,9 @@ const getTaskStatusClass = (status) => {
|
||||
.card-completed::before {
|
||||
background-color: #52c41a;
|
||||
}
|
||||
.card-failed::before {
|
||||
background-color: #ff4d4f;
|
||||
}
|
||||
|
||||
/* 卡片悬停效果 */
|
||||
.task-card:hover {
|
||||
@ -1336,6 +1496,12 @@ const getTaskStatusClass = (status) => {
|
||||
border-color: #b7eb8f;
|
||||
}
|
||||
|
||||
.tag-failed {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
border-color: #ffccc7;
|
||||
}
|
||||
|
||||
.task-details {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@ -1419,6 +1585,28 @@ const getTaskStatusClass = (status) => {
|
||||
color: #165dff;
|
||||
}
|
||||
|
||||
/* 失败卡片特殊样式 */
|
||||
.failed-task-details {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.failed-reason-item {
|
||||
padding-top: 8px;
|
||||
border-top: 1px dashed #f0f2f5;
|
||||
}
|
||||
|
||||
.failed-reason {
|
||||
color: #f53f3f;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.failed-task-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* 分页区域样式 */
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
@ -1472,6 +1660,46 @@ const getTaskStatusClass = (status) => {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
/* 日志弹窗样式 */
|
||||
.logs-container {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.logs-list {
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.log-item {
|
||||
padding: 12px 0;
|
||||
border-bottom: 1px solid #f0f2f5;
|
||||
}
|
||||
|
||||
.log-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.log-time {
|
||||
font-size: 12px;
|
||||
color: #86909c;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.log-content {
|
||||
font-size: 14px;
|
||||
color: #1d2129;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.no-logs {
|
||||
text-align: center;
|
||||
padding: 60px 0;
|
||||
}
|
||||
|
||||
.log-skeleton {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
/* 任务详情弹窗样式 */
|
||||
.task-detail-container {
|
||||
max-height: 600px;
|
||||
|
||||
Reference in New Issue
Block a user