feat: implement task management store with IPC integration
- Added a new task store in `src-react/stores/task.ts` to manage tasks and their statuses. - Implemented functions for creating, executing, and retrying tasks, along with handling task progress and completion. - Introduced persistence for tasks using IPC. - Created utility functions for normalizing room types and building subtasks. - Added a new CSS file for global styles in `src-react/styles.css`. - Created runtime types in `src-react/types/runtime.ts` and exported them. - Updated the main entry points for Vue and React applications to support dynamic framework loading. - Refactored chat model interfaces and utility functions into `src/shared/chat-model.ts`. - Updated TypeScript configuration to include paths for React components and types. - Enhanced Vite configuration to support both Vue and React frameworks.
This commit is contained in:
114
src-react/components/chat/TaskBoard.tsx
Normal file
114
src-react/components/chat/TaskBoard.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import type { TaskItem } from './types';
|
||||
|
||||
type TaskBoardProps = {
|
||||
activeTab: 'pending' | 'completed';
|
||||
pendingItems: TaskItem[];
|
||||
completedItems: TaskItem[];
|
||||
onTabChange: (tab: 'pending' | 'completed') => void;
|
||||
onRemoveTask?: (taskId: string) => void;
|
||||
onRetryTask?: (taskId: string) => void;
|
||||
currentDateLabel?: string;
|
||||
currentTime?: string;
|
||||
};
|
||||
|
||||
export default function TaskBoard({
|
||||
activeTab,
|
||||
pendingItems,
|
||||
completedItems,
|
||||
onTabChange,
|
||||
onRemoveTask,
|
||||
onRetryTask,
|
||||
currentDateLabel,
|
||||
currentTime,
|
||||
}: TaskBoardProps) {
|
||||
const items = activeTab === 'pending' ? pendingItems : completedItems;
|
||||
|
||||
return (
|
||||
<aside className="h-full min-h-0 w-full flex-none lg:w-[288px]">
|
||||
<div className="flex h-full min-h-0 flex-col rounded-[20px] bg-white p-3 shadow-[0_10px_30px_rgba(15,23,42,0.06)] dark:bg-[#1b1b1d]">
|
||||
<div className="flex rounded-[10px] border border-[#BEDBFF] bg-[#EFF6FF] p-1 dark:border-[#2a2a2d] dark:bg-[#1f1f22]">
|
||||
<button
|
||||
type="button"
|
||||
className={[
|
||||
'flex-1 rounded-[8px] px-3 py-2 text-sm transition-colors',
|
||||
activeTab === 'pending'
|
||||
? 'bg-white text-[#2B7FFF] shadow-sm dark:bg-[#1f1f22]'
|
||||
: 'text-[#525866] dark:text-gray-300',
|
||||
].join(' ')}
|
||||
onClick={() => onTabChange('pending')}
|
||||
>
|
||||
待处理{pendingItems.length > 0 ? `(${pendingItems.length})` : ''}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={[
|
||||
'flex-1 rounded-[8px] px-3 py-2 text-sm transition-colors',
|
||||
activeTab === 'completed'
|
||||
? 'bg-white text-[#2B7FFF] shadow-sm dark:bg-[#1f1f22]'
|
||||
: 'text-[#525866] dark:text-gray-300',
|
||||
].join(' ')}
|
||||
onClick={() => onTabChange('completed')}
|
||||
>
|
||||
已处理{completedItems.length > 0 ? `(${completedItems.length})` : ''}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{(currentDateLabel || currentTime) ? (
|
||||
<div className="flex items-center justify-between px-1 pb-3 pt-3 text-[13px] text-[#99A0AE] dark:text-gray-500">
|
||||
<span>{currentDateLabel || '执行时段'}</span>
|
||||
<span>{currentTime || '--'}</span>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 overflow-y-auto">
|
||||
{items.length > 0 ? (
|
||||
items.map((item) => (
|
||||
<article
|
||||
key={item.id}
|
||||
className="relative flex gap-3 rounded-[12px] border border-[#dfeaf6] bg-white p-3.5 transition-colors hover:bg-[#F5F7FA] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:hover:bg-[#2a2a2d]"
|
||||
>
|
||||
{(onRemoveTask || onRetryTask) ? (
|
||||
<div className="absolute right-2 top-2 flex items-center gap-2">
|
||||
{onRetryTask && (item.status === 'failed' || item.status === 'partial_failed') ? (
|
||||
<button
|
||||
type="button"
|
||||
className="text-[11px] text-[#2B7FFF] transition-colors hover:text-[#1d4ed8]"
|
||||
onClick={() => onRetryTask(item.id)}
|
||||
>
|
||||
重试
|
||||
</button>
|
||||
) : null}
|
||||
{onRemoveTask ? (
|
||||
<button
|
||||
type="button"
|
||||
className="text-[11px] text-[#99A0AE] transition-colors hover:text-[#ef4444]"
|
||||
onClick={() => onRemoveTask(item.removeTaskId || item.id)}
|
||||
>
|
||||
移除
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="flex h-11 w-11 items-center justify-center rounded-lg border border-dashed border-[#9fc0e8] bg-[#EFF6FF] text-[23px] text-[#3b82f6] dark:border-gray-700 dark:bg-[#222225]">
|
||||
•
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-sm font-semibold text-[#171717] dark:text-gray-100">{item.title}</div>
|
||||
<div className="mt-1.5 text-[13px] leading-6 text-[#9aa5b1] dark:text-gray-400">{item.description}</div>
|
||||
{item.meta ? (
|
||||
<div className="mt-1 text-[11px] text-[#99A0AE] dark:text-gray-500">{item.meta}</div>
|
||||
) : null}
|
||||
<div className="mt-2 text-xs font-medium text-[#2B7FFF]">{item.status}</div>
|
||||
</div>
|
||||
</article>
|
||||
))
|
||||
) : (
|
||||
<div className="rounded-[12px] border border-dashed border-[#dfeaf6] bg-[#f8fbff] px-4 py-6 text-sm text-[#99A0AE] dark:border-[#2a2a2d] dark:bg-[#1f1f22] dark:text-gray-400">
|
||||
当前分类下暂无任务卡片。
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user