import { reactive, computed } from 'vue' import { invoke } from '@tauri-apps/api/core' import { projectApi, taskApi, ideaApi, workflowApi } from '../api' import type { ProjectRecord, TaskRecord, IdeaRecord, WorkflowRecord, WorkflowEventPayload } from '../api/types' // ── 全局响应式状态(单例) ── const state = reactive({ projects: [] as ProjectRecord[], deletedProjects: [] as ProjectRecord[], tasks: [] as TaskRecord[], ideas: [] as IdeaRecord[], workflowExecutions: [] as WorkflowRecord[], liveEvents: [] as WorkflowEventPayload[], pendingApproval: null as { execution_id: string node_id: string title: string description: string options: string[] } | null, loading: false, error: null as string | null, }) let _eventUnlisten: (() => void) | null = null function createStore() { // ── 项目 CRUD ── async function loadProjects() { state.loading = true state.error = null try { state.projects = await projectApi.list() } catch (e: any) { state.error = e?.toString() ?? '加载项目失败' } finally { state.loading = false } } // 清除错误状态(供 toast 显示后重置,允许连续同值错误再次触发 watch) function clearError() { state.error = null } async function createProject(name: string, description = '', ideaId?: string, path?: string, stack?: string) { try { const record = await projectApi.create({ name, description, idea_id: ideaId, path, stack }) state.projects.push(record) return record } catch (e: any) { state.error = e?.toString() ?? '创建项目失败' return null } } async function updateProject(id: string, field: string, value: string) { await projectApi.update(id, field, value) const idx = state.projects.findIndex(p => p.id === id) if (idx >= 0) { (state.projects[idx] as any)[field] = value } } /** 重定位项目目录(后端重探测 stack,返回最新记录并更新本地状态) */ async function relocateProjectPath(id: string, newPath: string) { const record = await projectApi.relocatePath(id, newPath) const idx = state.projects.findIndex(p => p.id === id) if (idx >= 0) state.projects[idx] = record return record } async function deleteProject(id: string) { try { await projectApi.delete(id) // 软删 → 回收站(可恢复) state.projects = state.projects.filter(p => p.id !== id) } catch (e: any) { state.error = e?.toString() ?? '删除项目失败' } } async function loadDeletedProjects() { try { state.deletedProjects = await projectApi.listDeleted() } catch (e: any) { state.error = e?.toString() ?? '加载回收站失败' } } async function restoreProject(id: string) { try { await projectApi.restore(id) state.deletedProjects = state.deletedProjects.filter(p => p.id !== id) await loadProjects() // 恢复后刷新活跃列表 } catch (e: any) { state.error = e?.toString() ?? '恢复项目失败' } } async function purgeProject(id: string) { try { await projectApi.purge(id) // 彻底删(级联物理删,不可恢复) state.deletedProjects = state.deletedProjects.filter(p => p.id !== id) } catch (e: any) { state.error = e?.toString() ?? '彻底删除失败' } } // ── 任务 CRUD ── async function loadTasks(projectId?: string) { try { state.tasks = await taskApi.list(projectId) } catch (e: any) { state.error = e?.toString() ?? '加载任务失败' } } async function createTask(input: { project_id: string; title: string; description?: string; priority?: number; branch_name?: string; assignee?: string }) { try { const record = await taskApi.create(input) state.tasks.push(record) return record } catch (e: any) { state.error = e?.toString() ?? '创建任务失败' return null } } async function updateTask(id: string, field: string, value: string) { await taskApi.update(id, field, value) const idx = state.tasks.findIndex(t => t.id === id) if (idx >= 0) { (state.tasks[idx] as any)[field] = value } } async function deleteTask(id: string) { try { await taskApi.delete(id) state.tasks = state.tasks.filter(t => t.id !== id) } catch (e: any) { state.error = e?.toString() ?? '删除任务失败' } } // ── 灵感 CRUD ── async function loadIdeas() { try { state.ideas = await ideaApi.list() } catch (e: any) { state.error = e?.toString() ?? '加载灵感失败' } } async function createIdea(input: { title: string; description?: string; priority?: number; tags?: string; source?: string }) { try { const record = await ideaApi.create(input) state.ideas.push(record) return record } catch (e: any) { state.error = e?.toString() ?? '创建灵感失败' return null } } async function updateIdea(id: string, field: string, value: string) { await ideaApi.update(id, field, value) const idx = state.ideas.findIndex(i => i.id === id) if (idx >= 0) { (state.ideas[idx] as any)[field] = value } } async function deleteIdea(id: string) { try { await ideaApi.delete(id) state.ideas = state.ideas.filter(i => i.id !== id) } catch (e: any) { state.error = e?.toString() ?? '删除灵感失败' } } async function evaluateIdea(id: string) { const record = await ideaApi.evaluate(id) const idx = state.ideas.findIndex(i => i.id === id) if (idx >= 0) state.ideas[idx] = record return record } async function promoteIdea(id: string) { const res = await ideaApi.promote(id) await loadIdeas() // 后端已回写 status=promoted/promoted_to,刷新列表 return res } // ── 工作流 ── async function runWorkflow(name: string, dag: unknown, config?: Record) { return await workflowApi.run(name, dag, config) } async function loadWorkflowExecutions() { try { state.workflowExecutions = await workflowApi.listExecutions() } catch (e: any) { state.error = e?.toString() ?? '加载工作流记录失败' } } async function startEventListener() { if (_eventUnlisten) return _eventUnlisten _eventUnlisten = await workflowApi.onEvent((payload) => { try { state.liveEvents.push(payload) // 处理人工审批请求 if (payload.event?.type === 'HumanApprovalRequest') { state.pendingApproval = payload.event.data as typeof state.pendingApproval } } catch (e) { console.error('处理工作流事件失败:', e, payload) } }) return _eventUnlisten } function clearLiveEvents() { state.liveEvents = [] } async function approveHumanApproval(decision: string, comment?: string) { if (!state.pendingApproval) return try { // 调用 IPC 命令发送审批响应 await invoke('approve_human_approval', { execution_id: state.pendingApproval.execution_id, node_id: state.pendingApproval.node_id, decision, comment }) // 清除待审批状态 state.pendingApproval = null } catch (error) { console.error('审批失败:', error) } } /** 取消当前待审批节点(调 cancel_workflow_node IPC 置节点 Cancelled) */ async function cancelHumanApproval() { if (!state.pendingApproval) return try { await invoke('cancel_workflow_node', { execution_id: state.pendingApproval.execution_id, node_id: state.pendingApproval.node_id, }) // 清除待审批状态 state.pendingApproval = null } catch (error) { console.error('取消审批失败:', error) } } function stopEventListener() { if (_eventUnlisten) { try { _eventUnlisten() } catch (e) { console.error('停止事件监听失败:', e) } _eventUnlisten = null } } // ── 统计 ── const stats = computed(() => ({ ideas: state.ideas.length, projects: state.projects.length, activeTasks: state.tasks.filter(t => t.status === 'in_progress').length, drafts: state.ideas.filter(i => i.status === 'draft').length, })) return reactive({ // 状态用 getter 实时读 state —— 直接 ideas: state.ideas 会快照引用, // loadIdeas 等重新赋值 state.ideas 时返回对象的 ideas 属性不跟随,刷新后视图为空 get projects() { return state.projects }, get tasks() { return state.tasks }, get ideas() { return state.ideas }, get workflowExecutions() { return state.workflowExecutions }, get liveEvents() { return state.liveEvents }, get loading() { return state.loading }, get error() { return state.error }, clearError, get deletedProjects() { return state.deletedProjects }, // project actions loadProjects, createProject, updateProject, deleteProject, relocateProjectPath, loadDeletedProjects, restoreProject, purgeProject, // task actions loadTasks, createTask, updateTask, deleteTask, // idea actions loadIdeas, createIdea, updateIdea, deleteIdea, evaluateIdea, promoteIdea, // workflow actions runWorkflow, loadWorkflowExecutions, startEventListener, stopEventListener, clearLiveEvents, approveHumanApproval, cancelHumanApproval, pendingApproval: computed(() => state.pendingApproval), // computed stats, }) } type ProjectStore = ReturnType let _storeInstance: ProjectStore | null = null /** 项目/任务/灵感/工作流 全局状态(单例,多组件复用同一 reactive 包装) */ export function useProjectStore(): ProjectStore { if (_storeInstance) return _storeInstance _storeInstance = createStore() return _storeInstance }