diff --git a/server_admin/src/views/DatasetDetail.vue b/server_admin/src/views/DatasetDetail.vue
index 43f8447..9428c35 100644
--- a/server_admin/src/views/DatasetDetail.vue
+++ b/server_admin/src/views/DatasetDetail.vue
@@ -10,6 +10,36 @@ const router = useRouter()
const datasetId = computed(() => Number(route.params.id))
const dataset = ref(null)
+// 顶部状态汇总:各档位训练状态依次拼(双档 2026-09-03:s 高识别 / n 高性能 各一条最新记录);
+// running 带进度,queued 提示排队;无训练记录回退数据集状态
+const variantLabel = { s: 's高识别', n: 'n高性能' }
+const trainBanner = computed(() => {
+ const d = dataset.value
+ if (!d) return { text: '', type: 'info' }
+ const trs = d.trains || []
+ if (!trs.length) {
+ const fallback = { building: ['建设中', 'info'], labeled: ['已标注', 'success'], synced: ['已同步', 'primary'] }
+ const [text, type] = fallback[d.status] || ['', 'info']
+ return { text, type }
+ }
+ const parts = []
+ for (const t of trs) {
+ const lab = variantLabel[t.variant] || t.variant
+ if (t.status === 'running') parts.push(`${lab} 训练中 ${t.currentEpoch || 0}/${t.totalEpochs || '-'} 轮`)
+ else if (t.status === 'queued') parts.push(`${lab} 排队中`)
+ else if (t.status === 'success') parts.push(`${lab} ${t.published ? '已训练·已发布' : '已训练'}`)
+ else if (t.status === 'failed') parts.push(`${lab} 训练失败`)
+ }
+ const type = trs.some((t) => t.status === 'running')
+ ? 'primary'
+ : trs.some((t) => t.status === 'queued')
+ ? 'warning'
+ : trs.some((t) => t.status === 'failed')
+ ? 'danger'
+ : 'success'
+ return { text: parts.join(' / ') || '—', type }
+})
+
// ---------- 数据 ----------
const detail = ref(null) // 工作台数据:images[{filename,url,width,height,boxes,labeled}]
const imageMeta = ref({}) // filename -> {id, source, cleanExcluded}(来自 /datasets/images,行内展示用)
@@ -910,13 +940,7 @@ onBeforeUnmount(() => {
← 返回数据集
{{ dataset?.name || '数据训练' }}
-
- {{ dataset.trainingStatus === 'running' ? `训练中 ${dataset.trainingCurrentEpoch || 0}/${dataset.trainingTotalEpochs || '-'}` : (dataset.trainingStatus === 'success' ? '训练完成' : (dataset.trainingStatus === 'failed' ? '训练失败' : (dataset.status === 'labeled' ? '已标注' : '建设中'))) }}
-
+ {{ trainBanner.text }}
-
- 发布模型
-
-
-
-
-
-
- 训练中 {{ row.trainingCurrentEpoch || 0 }}/{{ row.trainingTotalEpochs || '-' }} 轮
-
-
-
-
-
- {{ trainStatusMap[row.trainingStatus] || row.trainingStatus }}
-
+
+
+
+ {{ variantMeta[t.variant]?.label || t.variant }}
+
+
+ {{ t.currentEpoch || 0 }}/{{ t.totalEpochs || '-' }} 轮
+
+
+ {{ trainStatusMap[t.status] }}
-
- {{ trainStatusMap[row.trainingStatus] || row.trainingStatus }}
+
+ {{ trainStatusMap[t.status] || t.status }}
-
+
+ 已训练
+ 发布模型
+ 已发布
+
+
+ {{ t.status === 'queued' ? '取消排队' : '终止' }}
+
+
@@ -449,6 +510,28 @@ onBeforeUnmount(() => {
/>
+
+
+ 数据集:{{ startRow?.name }}
+
+
+ s 高识别
+ yolov8s @1280 · 精度优先,默认档
+
+
+ n 高性能
+ yolov8n @704 · 速度优先
+
+
+
+ 训练机 GPU 独占:已有任务时本次训练自动排队(queued),按发起顺序逐个执行;该档位已有进行中任务时会提示错误。排队的任务可在卡片上取消。
+
+
+ 取消
+ 开始训练
+
+
+
@@ -605,11 +688,17 @@ onBeforeUnmount(() => {
}
.ds-train {
display: flex;
- align-items: center;
- gap: 10px;
+ flex-direction: column;
+ gap: 6px;
padding: 8px 12px 12px;
border-top: 1px dashed #ebeef5;
}
+.ds-train-line {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ min-width: 0;
+}
.ds-train-bar {
flex: 1;
}
@@ -618,6 +707,43 @@ onBeforeUnmount(() => {
color: #606266;
white-space: nowrap;
}
+.ds-pub {
+ margin-left: auto;
+}
+.ds-cancel {
+ margin-left: auto;
+}
+.start-dataset {
+ font-size: 14px;
+ font-weight: 600;
+ margin-bottom: 12px;
+}
+.start-opts {
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.start-opt {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 10px;
+ border: 1px solid #ebeef5;
+ border-radius: 6px;
+}
+.start-opt.is-act {
+ background: #f5f7fa;
+}
+.start-tip {
+ font-size: 12px;
+ color: #909399;
+}
+.start-note {
+ font-size: 12px;
+ color: #909399;
+ line-height: 1.6;
+ margin: 12px 0 0;
+}
.ds-body {
padding: 10px 12px 6px;
}