auto-sync: 2026-05-19 22:57:51
This commit is contained in:
@@ -90,8 +90,13 @@ function SectionLabel({ icon, title, count }: { icon: string; title: string; cou
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function StatusButtons({ status }: { status: string }) {
|
function StatusButtons({ status, taskId, onAction }: { status: string; taskId: string; onAction?: () => void }) {
|
||||||
|
const toast = useStore((s) => s.toast);
|
||||||
|
const loadAll = useStore((s) => s.loadAll);
|
||||||
|
const loadV2TaskDetail = useStore((s) => s.loadV2TaskDetail);
|
||||||
|
const selectedProjectId = useStore((s) => s.selectedProjectId);
|
||||||
const transitions = VALID_TRANSITIONS[status] || [];
|
const transitions = VALID_TRANSITIONS[status] || [];
|
||||||
|
const [loading, setLoading] = useState<string | null>(null);
|
||||||
const btnMap: Record<string, { label: string; icon: string; bg: string; color: string; border: string }> = {
|
const btnMap: Record<string, { label: string; icon: string; bg: string; color: string; border: string }> = {
|
||||||
claimed: { label: '认领任务', icon: '👤', bg: '#a07aff22', color: '#a07aff', border: '#a07aff44' },
|
claimed: { label: '认领任务', icon: '👤', bg: '#a07aff22', color: '#a07aff', border: '#a07aff44' },
|
||||||
working: { label: '开始执行', icon: '⚔️', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
|
working: { label: '开始执行', icon: '⚔️', bg: '#2ecc8a22', color: '#2ecc8a', border: '#2ecc8a44' },
|
||||||
@@ -105,6 +110,25 @@ function StatusButtons({ status }: { status: string }) {
|
|||||||
cancelled: { label: '取消任务', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
|
cancelled: { label: '取消任务', icon: '🚫', bg: '#6b728022', color: '#6b7280', border: '#6b728044' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClick = async (targetStatus: string) => {
|
||||||
|
setLoading(targetStatus);
|
||||||
|
try {
|
||||||
|
const result = await api.taskStatusUpdate(taskId, targetStatus);
|
||||||
|
if (result.ok || result.task_id) {
|
||||||
|
toast(`${targetStatus} 操作成功`, 'ok');
|
||||||
|
loadV2TaskDetail(taskId);
|
||||||
|
loadAll();
|
||||||
|
onAction?.();
|
||||||
|
} else {
|
||||||
|
toast(result.error || '操作失败', 'err');
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
toast(e.message || '⚠️ 操作失败', 'err');
|
||||||
|
} finally {
|
||||||
|
setLoading(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
if (transitions.length === 0) return null;
|
if (transitions.length === 0) return null;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -112,11 +136,13 @@ function StatusButtons({ status }: { status: string }) {
|
|||||||
{transitions.map(t => {
|
{transitions.map(t => {
|
||||||
const b = btnMap[t];
|
const b = btnMap[t];
|
||||||
if (!b) return null;
|
if (!b) return null;
|
||||||
|
const isLoading = loading === t;
|
||||||
return (
|
return (
|
||||||
<button key={t} style={{
|
<button key={t} onClick={() => handleClick(t)} disabled={!!loading} style={{
|
||||||
padding: '5px 12px', borderRadius: 6, fontSize: 11, cursor: 'pointer',
|
padding: '5px 12px', borderRadius: 6, fontSize: 11, cursor: loading ? 'not-allowed' : 'pointer',
|
||||||
background: b.bg, color: b.color, border: `1px solid ${b.border}`, fontWeight: 600,
|
background: b.bg, color: b.color, border: `1px solid ${b.border}`, fontWeight: 600,
|
||||||
}}>{b.icon} {b.label}</button>
|
opacity: isLoading ? 0.6 : 1,
|
||||||
|
}}>{isLoading ? '⏳' : b.icon} {b.label}</button>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user