ReportTaskTable.vue 3.8 KB
<script setup lang="ts">
import { computed } from 'vue'

import type { ReportTask } from '@/types'
import { formatTimeLabel, statusTone } from '@/utils/format'

const props = defineProps<{
  tasks: ReportTask[]
  selectedTaskId: string
  loading: boolean
}>()

defineEmits<{
  select: [taskId: string]
  download: [kind: 'html' | 'md' | 'pdf', taskId: string]
}>()

const rows = computed(() => props.tasks.map((item) => ({
  ...item,
  updatedLabel: formatTimeLabel(item.updated_at),
})))
</script>

<template>
  <div class="task-table">
    <div class="task-table__head">
      <div>
        <h3 class="archive-card-title">报告任务列表</h3>
        <p class="archive-copy">查看当前任务进度,并在完成后直接下载 HTML、Markdown 或 PDF。</p>
      </div>
      <span class="archive-chip">
        <span class="archive-chip__dot" />
        {{ tasks.length }} Reports
      </span>
    </div>

    <div v-if="loading" class="archive-empty">
      <p>报告任务载入中…</p>
    </div>

    <div v-else-if="rows.length === 0" class="archive-empty">
      <p>暂无报告任务。</p>
    </div>

    <div v-else class="report-list">
      <button
        v-for="row in rows"
        :key="row.task_id"
        class="report-item"
        :class="{ 'report-item--active': row.task_id === selectedTaskId }"
        type="button"
        @click="$emit('select', row.task_id)"
      >
        <div class="report-item__head">
          <div>
            <strong>{{ row.query || '未命名报告任务' }}</strong>
            <span>{{ row.updatedLabel }}</span>
          </div>
          <span class="archive-chip" :data-tone="statusTone(row.status)">
            <span class="archive-chip__dot" />
            {{ row.status }}
          </span>
        </div>

        <div class="report-item__progress">
          <div class="archive-progress">
            <div class="archive-progress__bar" :style="{ width: `${row.progress}%` }" />
          </div>
          <small>{{ row.progress }}%</small>
        </div>

        <div v-if="row.status === 'completed'" class="report-item__actions">
          <button class="archive-button--quiet" type="button" @click.stop="$emit('download', 'html', row.task_id)">HTML</button>
          <button class="archive-button--quiet" type="button" @click.stop="$emit('download', 'md', row.task_id)">MD</button>
          <button class="archive-button--quiet" type="button" @click.stop="$emit('download', 'pdf', row.task_id)">PDF</button>
        </div>
      </button>
    </div>
  </div>
</template>

<style scoped>
.task-table,
.report-list {
  display: grid;
  gap: 16px;
}

.task-table__head,
.report-item__head,
.report-item__progress {
  display: flex;
  justify-content: space-between;
  gap: 14px;
  align-items: flex-start;
}

.task-table__head {
  align-items: center;
}

.report-item {
  display: grid;
  gap: 14px;
  padding: 18px;
  border: 1px solid rgba(24, 35, 31, 0.08);
  border-radius: 22px;
  background: rgba(255, 255, 255, 0.68);
  box-shadow: var(--shadow-soft);
  text-align: left;
  cursor: pointer;
}

.report-item--active {
  border-color: rgba(141, 103, 56, 0.24);
  background: linear-gradient(135deg, rgba(255, 255, 255, 0.94), rgba(248, 241, 232, 0.92));
}

.report-item strong {
  display: block;
  color: var(--primary);
}

.report-item span,
.report-item small {
  color: var(--text-muted);
}

.report-item span:not(.archive-chip) {
  display: block;
  margin-top: 6px;
  line-height: 1.7;
}

.report-item__progress {
  align-items: center;
}

.report-item__progress .archive-progress {
  flex: 1;
}

.report-item__progress small {
  font-family: var(--font-mono);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.report-item__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.report-item__actions .archive-button--quiet {
  min-height: 34px;
  padding-inline: 12px;
}
</style>