- 实现补数任务面板组件,支持监测点ID输入、时间范围选择和时间步长设置 - 添加任务状态卡片组件,实时展示任务执行进度和结果统计 - 集成参数规则表格组件,显示后端配置的模板规则信息 - 实现补数API接口服务,包括预估写入量、创建任务和查询状态功能 - 添加磁盘监控策略对话框组件,支持全局监控配置管理 - 完成补数功能页面布局设计,集成左右双栏界面结构 - 实现任务轮询机制,自动更新任务执行状态直到完成 - 添加表单验证逻辑,确保输入参数符合业务规则要求
194 lines
5.4 KiB
Vue
194 lines
5.4 KiB
Vue
<template>
|
||
<section class="card add-data-card">
|
||
<div class="card-header">
|
||
<div>
|
||
<div class="section-title">任务状态</div>
|
||
<div class="section-description">创建任务后自动轮询状态,直到任务成功或失败。</div>
|
||
</div>
|
||
|
||
<el-tag v-if="status" :type="statusMeta.type" effect="light">{{ statusMeta.label }}</el-tag>
|
||
</div>
|
||
|
||
<div v-loading="loading" class="card-body">
|
||
<div v-if="status" class="status-content">
|
||
<el-descriptions :column="2" border size="small">
|
||
<el-descriptions-item label="任务 ID">{{ taskId || status.taskId || '--' }}</el-descriptions-item>
|
||
<el-descriptions-item label="当前状态">{{ statusMeta.label }}</el-descriptions-item>
|
||
<el-descriptions-item label="当前表名">{{ status.currentTableName || '--' }}</el-descriptions-item>
|
||
<el-descriptions-item label="当前批次">{{ status.currentBatchInfo || '--' }}</el-descriptions-item>
|
||
<el-descriptions-item label="已写入数量">{{ status.insertedCount }}</el-descriptions-item>
|
||
<el-descriptions-item label="已跳过数量">{{ status.skippedCount }}</el-descriptions-item>
|
||
<el-descriptions-item label="失败数量">{{ status.failedCount }}</el-descriptions-item>
|
||
<el-descriptions-item label="失败原因">
|
||
<span class="failure-text">{{ status.failureReason || '--' }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="开始时间">{{ status.startTime || '--' }}</el-descriptions-item>
|
||
<el-descriptions-item label="结束时间">{{ status.endTime || '--' }}</el-descriptions-item>
|
||
</el-descriptions>
|
||
|
||
<div class="hourly-block">
|
||
<div class="hourly-title">业务时刻</div>
|
||
<div v-if="status.hourlyTimeResults.length" class="hourly-scroll">
|
||
<div class="hourly-list">
|
||
<el-tag v-for="item in status.hourlyTimeResults" :key="item" effect="plain" type="info">
|
||
{{ item }}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
<div v-else class="hourly-empty">当前接口未返回业务时刻。</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div v-else class="empty-block">暂无补数任务,创建任务后会在这里持续展示执行进度。</div>
|
||
</div>
|
||
</section>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed } from 'vue'
|
||
import type { AddData } from '@/api/tools/addData/interface'
|
||
|
||
defineOptions({
|
||
name: 'AddDataTaskStatusCard'
|
||
})
|
||
|
||
const props = defineProps<{
|
||
status: AddData.NormalizedTaskStatus | null
|
||
taskId: string
|
||
loading: boolean
|
||
}>()
|
||
|
||
const statusMeta = computed(() => {
|
||
const status = props.status?.status
|
||
|
||
if (status === 'SUCCESS') {
|
||
return { label: '成功', type: 'success' as const }
|
||
}
|
||
|
||
if (status === 'FAILED') {
|
||
return { label: '失败', type: 'danger' as const }
|
||
}
|
||
|
||
if (status === 'RUNNING') {
|
||
return { label: '执行中', type: 'warning' as const }
|
||
}
|
||
|
||
if (status === 'WAITING') {
|
||
return { label: '等待中', type: 'info' as const }
|
||
}
|
||
|
||
return {
|
||
label: status || '未知状态',
|
||
type: 'info' as const
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.add-data-card {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
background: var(--el-bg-color);
|
||
border: 1px solid var(--el-border-color-light);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
gap: 16px;
|
||
padding: 16px 16px 12px;
|
||
border-bottom: 1px solid var(--el-border-color-lighter);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: var(--el-text-color-primary);
|
||
}
|
||
|
||
.section-description {
|
||
margin-top: 6px;
|
||
font-size: 13px;
|
||
line-height: 1.7;
|
||
color: var(--el-text-color-regular);
|
||
}
|
||
|
||
.card-body {
|
||
display: flex;
|
||
flex: 1;
|
||
min-height: 0;
|
||
padding: 16px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.status-content {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
gap: 12px;
|
||
min-height: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
.failure-text {
|
||
word-break: break-all;
|
||
color: var(--el-color-danger);
|
||
}
|
||
|
||
.hourly-block {
|
||
display: flex;
|
||
flex: 1;
|
||
flex-direction: column;
|
||
min-height: 0;
|
||
padding: 12px;
|
||
background: var(--cn-color-canvas-bg);
|
||
border: 1px dashed var(--el-border-color);
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.hourly-title {
|
||
margin-bottom: 10px;
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: var(--el-text-color-primary);
|
||
}
|
||
|
||
.hourly-scroll {
|
||
flex: 1;
|
||
min-height: 0;
|
||
overflow-y: auto;
|
||
padding-right: 4px;
|
||
}
|
||
|
||
.hourly-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-content: flex-start;
|
||
gap: 8px;
|
||
}
|
||
|
||
.hourly-empty {
|
||
font-size: 12px;
|
||
color: var(--el-text-color-secondary);
|
||
}
|
||
|
||
.empty-block {
|
||
display: flex;
|
||
flex: 1;
|
||
align-items: center;
|
||
justify-content: center;
|
||
width: 100%;
|
||
min-height: 0;
|
||
padding: 16px;
|
||
color: var(--el-text-color-secondary);
|
||
background: var(--cn-color-canvas-bg);
|
||
border: 1px dashed var(--el-border-color);
|
||
border-radius: 4px;
|
||
text-align: center;
|
||
}
|
||
</style>
|