auto-sync: 2026-05-20 22:56:37

This commit is contained in:
cfdaily
2026-05-20 22:56:37 +08:00
parent 5afb4df21d
commit 681b1151fe
+56
View File
@@ -606,3 +606,59 @@ export default function TaskModal() {
</div>
);
}
// 项目归属选择器
function ProjectSelector({ taskId, currentProject, projects }: {
taskId: string; currentProject: string | null; projects: Record<string, any>;
}) {
const [moving, setMoving] = useState(false);
const pid = currentProject || '';
const projName = projects[pid]?.name || pid || '未知项目';
const handleMove = async (targetPid: string) => {
if (targetPid === pid || !pid) return;
setMoving(true);
try {
const res = await fetch(`/api/projects/${pid}/tasks/${taskId}/move`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ target_project_id: targetPid }),
});
if (res.ok) {
const toast = useStore.getState().toast;
toast(`✅ 已移动到 ${projects[targetPid]?.name || targetPid}`);
useStore.getState().loadV2Tasks();
} else {
const d = await res.json();
useStore.getState().toast(d.detail || '移动失败', 'err');
}
} catch {
useStore.getState().toast('网络错误', 'err');
}
setMoving(false);
};
const otherProjects = Object.entries(projects).filter(([p]) => p !== pid && !p.startsWith('_'));
return (
<span style={{ position: 'relative', display: 'inline-block' }}>
<span style={{ cursor: 'pointer', borderBottom: '1px dashed var(--muted)' }} title="点击切换项目归属">
📁 {projName}
</span>
<select
disabled={moving}
onChange={(e) => { if (e.target.value) handleMove(e.target.value); }}
defaultValue=""
style={{
position: 'absolute', inset: 0, opacity: 0, cursor: 'pointer',
fontSize: 12, border: 'none', background: 'transparent',
}}
>
<option value="" disabled>...</option>
{otherProjects.map(([p, info]) => (
<option key={p} value={p}>{info.name || p}</option>
))}
</select>
</span>
);
}