@@ -852,10 +760,6 @@ const companyAgents = ref([])
const pipelines = ref([])
const pipelineRuns = ref([])
const selectedPipeline = ref(null)
-const goals = ref([])
-const selectedGoalId = ref(null)
-const selectedGoalDetail = ref(null)
-const selectedGoalProgress = ref(null)
const templates = ref([])
const activeTab = ref('overview')
const showTemplates = ref(false)
@@ -1039,7 +943,6 @@ async function loadCompanyData(id, tab) {
if (activeTab.value === 'overview') await loadCompanyStatus(id)
if (activeTab.value === 'agents') { await loadCompanyAgents(id); await loadOrgGraph(id) }
if (activeTab.value === 'pipelines') await loadPipelines(id)
- if (activeTab.value === 'goals') await loadGoals(id)
if (activeTab.value === 'files') await loadCompanyFiles(id)
if (activeTab.value === 'messages') await loadMessages(id)
if (activeTab.value === 'skills') await loadSkillsList(id)
@@ -1064,6 +967,11 @@ function goToGraphConsole() {
router.push(`/${selectedCompany.value.id}/graph-console`)
}
+function goToTaskBoard() {
+ if (!selectedCompany.value) return
+ router.push(`/${selectedCompany.value.id}/task-board`)
+}
+
watch(() => route?.params, async (params) => {
if (!params) return
if (route.name === 'CompanyDetail' && params.id) {
@@ -1216,54 +1124,6 @@ async function retryPipelineRun(runId) {
} catch (e) { alert(`❌ 重试失败: ${e.message}`) }
}
-async function loadGoals(id) {
- try {
- const { data } = await axios.get(`/api/company/${id}/okr`)
- goals.value = data
- // Auto-select first goal
- if (data.length > 0 && !selectedGoalId.value) {
- selectGoalDetail(data[0].id)
- }
- } catch (e) { console.error(e) }
-}
-
-async function selectGoalDetail(goalId) {
- if (!selectedCompany.value) return
- selectedGoalId.value = goalId
- try {
- const [goalRes, progRes] = await Promise.all([
- axios.get(`/api/company/${selectedCompany.value.id}/okr/${goalId}`),
- axios.get(`/api/company/${selectedCompany.value.id}/okr/${goalId}/progress`)
- ])
- selectedGoalDetail.value = goalRes.data
- selectedGoalProgress.value = progRes.data
- } catch (e) { console.error(e) }
-}
-
-function okrStatusLabel(status) {
- const map = { active: '运转中', in_progress: '进行中', pending: '待开始', done: '已完成', failed: '失败', blocked: '已阻塞' }
- return map[status] || status
-}
-
-function okrLevelName(level) {
- const names = ['Goal', 'Objective', 'Key Result', 'Sub-Task', 'Micro-Task']
- return names[Math.min(level - 1, 4)]
-}
-
-const okrSortedNodes = computed(() => {
- if (!selectedGoalDetail.value?.nodes) return []
- const nodes = selectedGoalDetail.value.nodes
- const keys = Object.keys(nodes).sort((a, b) => {
- const partsA = a.replace('G-','').split('.').map(Number)
- const partsB = b.replace('G-','').split('.').map(Number)
- for (let i = 0; i < Math.max(partsA.length, partsB.length); i++) {
- if ((partsA[i] || 0) !== (partsB[i] || 0)) return (partsA[i] || 0) - (partsB[i] || 0)
- }
- return 0
- })
- return keys.map(id => ({ id, ...nodes[id], level: id.split('.').length }))
-})
-
async function loadCompanyStatus(id) {
try {
const { data } = await axios.get(`/api/company/${id}/status`)
@@ -1848,84 +1708,6 @@ function closeDropdown() {
.run-meta { display: flex; gap: 16px; font-size: 12px; color: var(--text-muted); }
.run-actions { margin-top: 8px; }
-/* ═══ OKR Full Layout ═══ */
-.okr-layout { display: flex; height: calc(100vh - 140px); overflow: hidden; }
-.okr-sidebar { width: 280px; min-width: 260px; border-right: 1px solid var(--border-primary); background: var(--bg-overlay); display: flex; flex-direction: column; }
-.okr-sidebar-header { padding: 12px 16px; font-size: 13px; font-weight: 700; color: var(--text-secondary); border-bottom: 1px solid var(--border-primary); display: flex; align-items: center; justify-content: space-between; background: var(--bg-secondary); }
-.okr-count { background: var(--bg-surface-active); color: var(--accent); padding: 2px 8px; border-radius: 12px; font-size: 11px; }
-.okr-goal-list { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 8px; }
-.okr-goal-card { padding: 12px 14px; border-radius: 10px; cursor: pointer; background: var(--bg-surface); border: 1px solid var(--border-primary); transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1); }
-.okr-goal-card:hover { transform: translateY(-2px); border-color: var(--accent-light); box-shadow: 0 4px 12px rgba(16,185,129,0.08); }
-.okr-goal-card.active { background: var(--accent-bg); border-color: var(--border-active); box-shadow: 0 0 0 1px var(--accent) inset; }
-.okr-goal-top { display: flex; justify-content: space-between; margin-bottom: 6px; }
-.okr-gid { font-size: 11px; font-family: 'JetBrains Mono', monospace; font-weight: 600; color: var(--text-tertiary); }
-.okr-gstatus { font-size: 10px; font-weight: 600; padding: 2px 6px; border-radius: 4px; }
-.okr-gstatus.active { background: rgba(59,130,246,0.15); color: #60a5fa; }
-.okr-gstatus.done { background: rgba(16,185,129,0.15); color: #34d399; }
-.okr-gtitle { font-size: 13px; font-weight: 600; margin-bottom: 10px; color: var(--text-primary); line-height: 1.4; }
-.okr-gprogress { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
-.okr-pbar { flex: 1; height: 6px; background: var(--bg-input-focus); border-radius: 3px; overflow: hidden; }
-.okr-pfill { height: 100%; background: linear-gradient(90deg, var(--accent), var(--accent-light)); border-radius: 3px; transition: width 0.5s ease; }
-.okr-ppct { font-size: 11px; font-weight: 600; color: var(--text-secondary); width: 32px; text-align: right; }
-.okr-gmeta { display: flex; justify-content: space-between; font-size: 10px; color: var(--text-faint); }
-
-/* OKR Detail Panel */
-.okr-detail { flex: 1; overflow-y: auto; background: var(--bg-primary); display: flex; flex-direction: column; }
-.okr-detail.okr-empty { align-items: center; justify-content: center; background: var(--bg-overlay); }
-.okr-detail.okr-empty h3 { font-size: 20px; font-weight: 600; margin-bottom: 8px; color: var(--text-secondary); }
-.okr-detail.okr-empty p { color: var(--text-muted); font-size: 13px; }
-
-.okr-hero { padding: 30px 40px; border-bottom: 1px solid var(--border-primary); background: linear-gradient(180deg, var(--accent-bg) 0%, transparent 100%); }
-.okr-hero-row { display: flex; align-items: center; gap: 12px; margin-bottom: 12px; }
-.okr-hero-id { font-size: 14px; font-family: 'JetBrains Mono', monospace; font-weight: 700; color: var(--accent-light); }
-.okr-hero-status { font-size: 12px; font-weight: 600; padding: 3px 8px; border-radius: 6px; }
-.okr-hero-status.active { background: rgba(59,130,246,0.2); color: #60a5fa; border: 1px solid rgba(59,130,246,0.3); }
-.okr-hero-status.done { background: rgba(16,185,129,0.2); color: #34d399; border: 1px solid rgba(16,185,129,0.3); }
-.okr-hero-owner { font-size: 12px; font-weight: 500; color: var(--text-secondary); margin-left: auto; background: var(--bg-surface); padding: 4px 10px; border-radius: 12px; border: 1px solid var(--border-primary); }
-.okr-hero-title { font-size: 26px; font-weight: 800; margin: 0 0 10px; letter-spacing: -0.5px; }
-.okr-hero-desc { font-size: 14px; color: var(--text-tertiary); line-height: 1.6; max-width: 800px; margin: 0; }
-
-.okr-hero-progress { margin-top: 24px; max-width: 600px; background: var(--bg-surface); padding: 16px 20px; border-radius: 12px; border: 1px solid var(--border-primary); }
-.okr-hp-top { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 10px; font-size: 13px; font-weight: 600; color: var(--text-secondary); }
-.okr-hp-pct { font-size: 24px; font-weight: 800; color: var(--accent-light); line-height: 1; }
-.okr-hp-bar { height: 8px; background: var(--bg-input-focus); border-radius: 4px; overflow: hidden; margin-bottom: 12px; }
-.okr-hp-fill { height: 100%; background: linear-gradient(90deg, #10b981, #34d399); border-radius: 4px; transition: width 0.8s cubic-bezier(0.16, 1, 0.3, 1); }
-.okr-hp-stats { display: flex; gap: 16px; font-size: 11px; font-weight: 500; color: var(--text-tertiary); }
-.okr-hp-stats .stat-d { color: #34d399; }
-.okr-hp-stats .stat-p { color: #60a5fa; }
-.okr-hp-stats .stat-w { color: var(--text-muted); }
-
-/* OKR Tree */
-.okr-tree-area { padding: 30px 40px; max-width: 1000px; }
-.okr-tree-title { font-size: 18px; font-weight: 600; margin: 0 0 20px; color: var(--text-primary); }
-.okr-tree { display: flex; flex-direction: column; gap: 6px; }
-.okr-tnode-line { display: flex; width: 100%; }
-.okr-tnode-indent { position: relative; }
-.okr-tnode-indent::before { content: ''; position: absolute; top: -16px; bottom: 50%; left: -14px; width: 14px; border-left: 1.5px solid var(--border-active); border-bottom: 1.5px solid var(--border-active); border-bottom-left-radius: 6px; z-index: 1; opacity: 0.3; }
-.okr-tnode-card { flex: 1; background: var(--bg-surface); border: 1px solid var(--border-primary); border-radius: 8px; padding: 12px 16px; transition: all 0.2s; }
-.okr-tnode-card:hover { border-color: rgba(255,255,255,0.1); background: var(--bg-surface-hover); }
-.okr-tnode-card.okr-lv-2 { background: rgba(59,130,246,0.03); border-color: rgba(59,130,246,0.1); }
-.okr-tnode-card.okr-lv-3 { background: rgba(16,185,129,0.03); border-color: rgba(16,185,129,0.1); }
-.okr-tnode-hdr { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
-.okr-nid { font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; color: var(--text-tertiary); }
-.okr-ntype { font-size: 9px; font-weight: 600; padding: 2px 6px; border-radius: 10px; background: rgba(255,255,255,0.05); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
-.okr-nstatus { font-size: 10px; font-weight: 600; padding: 2px 6px; border-radius: 4px; border: 1px solid transparent; }
-.okr-nstatus.done { background: rgba(16,185,129,0.1); color: #34d399; border-color: rgba(16,185,129,0.2); }
-.okr-nstatus.in_progress { background: rgba(59,130,246,0.1); color: #60a5fa; border-color: rgba(59,130,246,0.2); animation: okr-pulse 2s infinite; }
-.okr-nstatus.pending { background: rgba(255,255,255,0.05); color: var(--text-muted); border-color: var(--border-primary); }
-@keyframes okr-pulse { 0% { box-shadow: 0 0 0 0 rgba(96,165,250,0.2); } 70% { box-shadow: 0 0 0 4px rgba(96,165,250,0); } 100% { box-shadow: 0 0 0 0 rgba(96,165,250,0); } }
-.okr-ntitle { font-size: 14px; font-weight: 600; color: var(--text-primary); margin-bottom: 8px; line-height: 1.4; }
-.okr-nfooter { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 4px; }
-.okr-nowner { font-size: 11px; font-weight: 500; color: var(--accent-light); display: flex; align-items: center; gap: 4px; background: var(--accent-bg); padding: 2px 8px; border-radius: 10px; }
-.okr-nprio { font-size: 10px; font-weight: 700; font-family: 'JetBrains Mono', monospace; }
-.okr-nprio.P0 { color: #ef4444; }
-.okr-nprio.P1 { color: #f59e0b; }
-.okr-nprio.P2 { color: var(--text-muted); }
-.okr-ntask { font-size: 10px; font-family: 'JetBrains Mono', monospace; color: var(--blue-text); background: rgba(59,130,246,0.1); padding: 2px 6px; border-radius: 4px; }
-.okr-nsummary { margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border-primary); font-size: 11px; color: var(--text-tertiary); line-height: 1.5; }
-.okr-nsummary strong { color: var(--text-secondary); }
-
-.empty-state-large { text-align: center; color: var(--text-muted); }
/* ═══ Modal ═══ */
.modal-overlay {
diff --git a/web/src/views/OkrDashboard.vue b/web/src/views/OkrDashboard.vue
deleted file mode 100644
index ac5dd47..0000000
--- a/web/src/views/OkrDashboard.vue
+++ /dev/null
@@ -1,352 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
- {{ selectedGoal.id }}
- {{ getDisplayStatus(selectedGoal.status) }}
- 👤 负责人: {{ selectedGoal.owner }}
-
-
{{ selectedGoal.title }}
-
{{ selectedGoal.description || '暂无详细描述...' }}
-
-
- 目标总体进度
- {{ Math.round(selectedGoalProgress.progress * 100) }}%
-
-
-
- 总叶子节点: {{ selectedGoalProgress.total }}
- 已完成: {{ selectedGoalProgress.done }}
- 进行中: {{ selectedGoalProgress.inProgress }}
- 待开始: {{ selectedGoalProgress.pending }}
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ node.title }}
-
-
- 摘要: {{ node.summary }}
-
-
- 产出: {{ node.output }}
-
-
-
-
-
-
-
-
-
-
-
🎯
-
OKR 目标看板
-
请从左侧列表选择一个目标查看详细拆解树与进度
-
-
-
-
-
-
-
-
-
diff --git a/web/src/views/TaskBoard.vue b/web/src/views/TaskBoard.vue
new file mode 100644
index 0000000..de7e3fd
--- /dev/null
+++ b/web/src/views/TaskBoard.vue
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+