WorkspaceOverview.vue 3.92 KB
<script setup lang="ts">
import MetricCard from '@/components/common/MetricCard.vue'

interface WorkspaceMetric {
  label: string
  value: string
  detail: string
  tone?: 'neutral' | 'success' | 'running' | 'danger'
}

interface WorkspaceSection {
  id: string
  label: string
  description: string
}

defineProps<{
  metrics: ReadonlyArray<WorkspaceMetric>
  sections: ReadonlyArray<WorkspaceSection>
}>()
</script>

<template>
  <section class="overview-shell">
    <div class="overview-board">
      <div class="overview-copy">
        <p class="overview-kicker">Research Flow</p>
        <h2 class="overview-title">把调研任务、采集、分析与交付放在同一条可追踪链路里。</h2>
        <p class="overview-description">
          工作台按照场馆运营研究的真实节奏组织,每一步都保留状态、历史和上下文,
          方便在高分辨率屏幕上持续工作,也方便在刷新后快速回到上一次进度。
        </p>
      </div>

      <div class="overview-metrics">
        <MetricCard
          v-for="item in metrics"
          :key="item.label"
          :label="item.label"
          :value="item.value"
          :detail="item.detail"
          :tone="item.tone"
        />
      </div>
    </div>

    <aside class="overview-nav">
      <div class="nav-head">
        <p class="overview-kicker">Navigator</p>
        <h3>快速跳转</h3>
      </div>
      <a
        v-for="section in sections"
        :key="section.id"
        class="nav-link"
        :href="`#${section.id}`"
      >
        <strong>{{ section.label }}</strong>
        <span>{{ section.description }}</span>
      </a>
    </aside>
  </section>
</template>

<style scoped>
.overview-shell {
  display: grid;
  grid-template-columns: minmax(0, 1.7fr) minmax(280px, 0.72fr);
  gap: 20px;
  margin: 24px 0;
}

.overview-board,
.overview-nav {
  padding: 24px;
  border-radius: 30px;
  border: 1px solid var(--border-strong);
  background:
    linear-gradient(180deg, rgba(255, 252, 247, 0.92), rgba(249, 243, 235, 0.88));
  box-shadow: var(--panel-shadow);
}

.overview-board {
  display: grid;
  gap: 24px;
}

.overview-copy {
  display: grid;
  gap: 12px;
}

.overview-kicker {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--text-muted);
}

.overview-title {
  margin: 0;
  max-width: 18ch;
  font-family: var(--font-display);
  font-size: clamp(28px, 2.6vw, 42px);
  line-height: 1.06;
}

.overview-description {
  margin: 0;
  max-width: 72ch;
  color: var(--text-muted);
  line-height: 1.85;
}

.overview-metrics {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 14px;
}

.overview-nav {
  position: sticky;
  top: 24px;
  align-self: start;
  display: grid;
  gap: 12px;
}

.nav-head {
  display: grid;
  gap: 8px;
  padding-bottom: 8px;
  border-bottom: 1px solid rgba(23, 27, 24, 0.08);
}

.nav-head h3 {
  margin: 0;
  font-size: 20px;
}

.nav-link {
  display: grid;
  gap: 6px;
  padding: 14px 16px;
  border-radius: 18px;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.62);
  color: inherit;
  text-decoration: none;
  transition: transform 0.18s ease, border-color 0.18s ease, background-color 0.18s ease;
}

.nav-link strong {
  font-size: 14px;
  color: var(--text-strong);
}

.nav-link span {
  font-size: 13px;
  line-height: 1.7;
  color: var(--text-muted);
}

.nav-link:hover {
  transform: translateY(-1px);
  border-color: rgba(40, 94, 77, 0.18);
  background: rgba(40, 94, 77, 0.06);
}

@media (max-width: 1520px) {
  .overview-shell {
    grid-template-columns: 1fr;
  }

  .overview-nav {
    position: static;
  }
}

@media (max-width: 1240px) {
  .overview-metrics {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .overview-board,
  .overview-nav {
    padding: 20px;
    border-radius: 24px;
  }

  .overview-metrics {
    grid-template-columns: 1fr;
  }
}
</style>