DashboardHeader.vue
2.61 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
<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>