DashboardHeader.vue 2.61 KB
<template>
  <header class="dl-header">
    <div class="left">
      <router-link to="/devloop" class="back">← 项目列表</router-link>
      <h1>DevLoop · {{ pid }}</h1>
      <span class="status-badge" :class="status">{{ status }}</span>
    </div>
    <div class="right">
      <button class="btn primary" @click="$emit('new-requirement')" :disabled="terminal">+ 新增需求</button>
      <button class="btn signoff" @click="$emit('signoff')" :disabled="status !== 'shipped'">Sign Off</button>
      <button class="btn abandon" @click="$emit('abandon')" :disabled="terminal">Abandon</button>
      <span class="sep">|</span>
      <button class="btn link" @click="$emit('ops', 'bugs')">Ops →</button>
    </div>
  </header>
</template>

<script setup>
import { computed } from 'vue';
const props = defineProps({
  pid: { type: String, required: true },
  state: { type: Object, default: null },
});
defineEmits(['new-requirement', 'signoff', 'abandon', 'ops']);
const status = computed(() => props.state?.status || props.state?.project?.status || 'unknown');
const terminal = computed(() => status.value === 'shipped-final' || status.value === 'abandoned');
</script>

<style scoped>
.dl-header {
  display:flex; align-items:center; justify-content:space-between;
  padding:12px 0; border-bottom:1px solid #eee;
}
.left { display:flex; align-items:center; gap:14px; }
.right { display:flex; align-items:center; gap:8px; }
.back { color:#888; text-decoration:none; font-size:13px; }
.back:hover { color:#222; }
h1 { font-size:18px; font-weight:600; margin:0; font-family: ui-monospace, monospace; }
.status-badge {
  font-size:11px; padding:3px 10px; border-radius:12px; text-transform:uppercase; font-weight:600;
}
.status-badge.draft         { background:#eee; color:#666; }
.status-badge.running       { background:#cce5ff; color:#004085; }
.status-badge.shipped       { background:#d4edda; color:#155724; }
.status-badge.shipped-final { background:#0a6;    color:#fff; }
.status-badge.abandoned     { background:#222;    color:#fff; }
.status-badge.l2_blocked    { background:#fff3cd; color:#856404; }
.status-badge.l2_exhausted  { background:#f8d7da; color:#721c24; }
.status-badge.unknown       { background:#fafafa; color:#999; border:1px solid #eee; }
.btn { padding:6px 14px; border:none; border-radius:4px; cursor:pointer; font-size:13px; }
.btn:disabled { background:#ddd; color:#999; cursor:not-allowed; }
.btn.primary  { background:#222; color:#fff; }
.btn.signoff  { background:#0a6; color:#fff; }
.btn.abandon  { background:#822; color:#fff; }
.btn.link     { background:transparent; color:#0a6; padding:6px 0; }
.sep { color:#ccc; }
</style>