auto-sync: 2026-05-17 06:16:22
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
// AI Briefing 页面
|
||||
|
||||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import * as api from '../api';
|
||||
import type { Task, Project } from '../types';
|
||||
|
||||
export function Briefing() {
|
||||
const [projects, setProjects] = useState<Project[]>([]);
|
||||
const [selectedProject, setSelectedProject] = useState<string>('');
|
||||
const [tasks, setTasks] = useState<Task[]>([]);
|
||||
const [briefing, setBriefing] = useState<string>('');
|
||||
|
||||
const loadProjects = useCallback(async () => {
|
||||
try {
|
||||
const data = await api.listProjects();
|
||||
setProjects(data);
|
||||
if (data.length > 0 && !selectedProject) {
|
||||
setSelectedProject(data[0].id);
|
||||
}
|
||||
} catch { /* ignore */ }
|
||||
}, [selectedProject]);
|
||||
|
||||
useEffect(() => { loadProjects(); }, [loadProjects]);
|
||||
|
||||
const generateBriefing = useCallback(async () => {
|
||||
if (!selectedProject) return;
|
||||
try {
|
||||
const data = await api.listTasks(selectedProject);
|
||||
setTasks(data);
|
||||
|
||||
// 本地生成简要汇报
|
||||
const done = data.filter(t => t.status === 'done');
|
||||
const working = data.filter(t => t.status === 'working');
|
||||
const failed = data.filter(t => t.status === 'failed');
|
||||
const pending = data.filter(t => t.status === 'pending');
|
||||
|
||||
const lines = [
|
||||
`📊 项目日报`,
|
||||
``,
|
||||
`总任务数: ${data.length}`,
|
||||
`✅ 已完成: ${done.length}`,
|
||||
`🔄 进行中: ${working.length}`,
|
||||
`⏳ 待处理: ${pending.length}`,
|
||||
`❌ 失败: ${failed.length}`,
|
||||
``,
|
||||
];
|
||||
|
||||
if (working.length > 0) {
|
||||
lines.push(`## 进行中的任务`);
|
||||
working.forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title} (${t.assignee || '未分配'})`));
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (failed.length > 0) {
|
||||
lines.push(`## 失败的任务`);
|
||||
failed.forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title}`));
|
||||
lines.push('');
|
||||
}
|
||||
|
||||
if (done.length > 0) {
|
||||
lines.push(`## 最近完成`);
|
||||
done.slice(-5).forEach(t => lines.push(`- [${t.id.substring(0, 8)}] ${t.title}`));
|
||||
}
|
||||
|
||||
setBriefing(lines.join('\n'));
|
||||
} catch { /* ignore */ }
|
||||
}, [selectedProject]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h2 className="page-title">AI Briefing</h2>
|
||||
|
||||
<div style={{ display: 'flex', gap: 8, marginBottom: 16 }}>
|
||||
<select
|
||||
value={selectedProject}
|
||||
onChange={e => setSelectedProject(e.target.value)}
|
||||
style={{
|
||||
padding: '6px 10px',
|
||||
borderRadius: 'var(--radius)',
|
||||
border: '1px solid var(--line)',
|
||||
background: 'var(--bg)',
|
||||
color: 'var(--fg)',
|
||||
}}
|
||||
>
|
||||
<option value="">选择项目</option>
|
||||
{projects.map(p => (
|
||||
<option key={p.id} value={p.id}>{p.name}</option>
|
||||
))}
|
||||
</select>
|
||||
<button className="btn btn-primary" onClick={generateBriefing}>
|
||||
生成汇报
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{briefing && (
|
||||
<div className="card">
|
||||
<pre style={{ whiteSpace: 'pre-wrap', fontFamily: 'var(--font)', lineHeight: 1.6 }}>
|
||||
{briefing}
|
||||
</pre>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!briefing && (
|
||||
<div className="card">
|
||||
<p style={{ color: 'var(--muted)' }}>选择项目并点击"生成汇报"查看项目状态概览。</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user