ReportPreview.vue 2.84 KB
<script setup lang="ts">
import type { ReportTask } from '@/types'
import { statusTone } from '@/utils/format'

defineProps<{
  selectedTask: ReportTask | null
  previewUrl: string
}>()
</script>

<template>
  <div class="preview-shell">
    <div class="preview-head archive-panel">
      <div>
        <p class="archive-kicker">Preview Canvas</p>
        <h3 class="archive-card-title">{{ selectedTask?.query || '等待选择报告任务' }}</h3>
        <p class="archive-copy">
          {{ selectedTask?.error_message || '生成完成后,会在这里以纸面化的形式展示当前报告内容。' }}
        </p>
      </div>
      <span v-if="selectedTask" class="archive-chip" :data-tone="statusTone(selectedTask.status)">
        <span class="archive-chip__dot" />
        {{ selectedTask.status }} · {{ selectedTask.progress }}%
      </span>
    </div>

    <div class="preview-paper">
      <div class="preview-paper__header">
        <div>
          <p class="archive-kicker">Internal Operations Report</p>
          <strong>{{ selectedTask?.query || 'Venue Operations Report' }}</strong>
        </div>
        <span>{{ selectedTask?.task_id || 'N/A' }}</span>
      </div>

      <iframe v-if="previewUrl" class="preview-frame" :src="previewUrl" title="报告预览" />

      <div v-else class="preview-empty">
        <strong>暂无可预览的报告</strong>
        <p class="archive-copy">生成完成后会自动显示在这里,历史任务也可以随时重新打开。</p>
      </div>
    </div>
  </div>
</template>

<style scoped>
.preview-shell {
  display: grid;
  gap: 18px;
  min-width: 0;
}

.preview-head {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  align-items: flex-start;
}

.preview-paper {
  min-height: 920px;
  padding: 22px;
  border: 1px solid rgba(24, 35, 31, 0.08);
  border-radius: 28px;
  background:
    linear-gradient(180deg, rgba(244, 238, 228, 0.88), rgba(248, 243, 236, 0.82));
  box-shadow: var(--shadow-soft);
}

.preview-paper__header {
  display: flex;
  justify-content: space-between;
  gap: 18px;
  align-items: end;
  padding: 0 6px 18px;
  border-bottom: 3px solid rgba(22, 52, 45, 0.18);
}

.preview-paper__header strong {
  display: block;
  margin-top: 6px;
  font-family: var(--font-display);
  font-size: clamp(28px, 3vw, 40px);
  color: var(--primary);
}

.preview-paper__header span {
  font-family: var(--font-mono);
  color: var(--text-soft);
  letter-spacing: 0.12em;
  text-transform: uppercase;
}

.preview-frame {
  width: 100%;
  min-height: 820px;
  margin-top: 20px;
  border: 0;
  border-radius: 18px;
  background: #fff;
}

.preview-empty {
  display: grid;
  place-items: center;
  gap: 10px;
  min-height: 820px;
  padding: 32px;
  text-align: center;
}

@media (max-width: 820px) {
  .preview-head,
  .preview-paper__header {
    flex-direction: column;
    align-items: flex-start;
  }
}
</style>