import { reactive, computed } from 'vue' import { listen } from '@tauri-apps/api/event' import { createProjectsStore } from './project/projects' import { createTasksStore } from './project/tasks' import { createIdeasStore } from './project/ideas' import { createWorkflowStore } from './project/workflow' import { state, clearError } from './project/state' import type { DfDataChangedPayload, TaskQuery } from '@/api/types' // ── barrel 组合器(零行为变更,纯重构) ── // // 原 project.ts 是上帝 store(项目/任务/灵感/工作流多领域 + 越层 invoke 审批 IPC)。 // 已拆为 src/stores/project/{projects,tasks,ideas,workflow}.ts 四子 store, // 共享 src/stores/project/state.ts 单例 state。 // // 本文件改为组合器:内部实例化四子 store + re-export 所有成员, // 使现有 import { useProjectStore } 的 view 零改动。 // 越层 invoke(approve_human_approval / cancel_human_node) 已下沉到 src/api/workflow.ts。 function createStore() { const projectsStore = createProjectsStore() const tasksStore = createTasksStore() const ideasStore = createIdeasStore() const workflowStore = createWorkflowStore() // ── AR-11(方案A):数据变更联动刷新 ── // 后端 AI 工具(create/update/delete/restore/purge/bind_directory)执行成功后 emit // "df-data-changed" { entity, action },前端按 entity 调对应 load 刷新列表, // 取代工具执行后需手动刷新。 // // CR-260615-26(R-1): task 分支不再无参 loadTasks(会把 store.tasks 冲成全量,破坏 // B-260615-29「store.tasks 反映当前筛选」契约)。改为读 _activeTaskProject: // - undefined: Tasks 视图未挂载/从未设过筛选 → 不触发 task load(避免破坏筛选, // 让 B-29 watch/handle 主导;此时改动由用户下次进 Tasks 自然刷新) // - 'all' : 全量拉取 // - 具体 id : 按项目拉取 // 由 Tasks.vue 经 setActiveTaskProject 在 onMounted/watch 更新本变量。 // // F-260621-02(P1 status 下沉):新增 _activeTaskStatus 追踪当前 status 筛选,数据变更 // 联动刷新时一并下沉到后端 query(保 B-29「store.tasks 反映当前筛选」契约, // 避免刷新把已选 status 冲掉)。 let _activeTaskProject: string | undefined = undefined let _activeTaskStatus: string | undefined = undefined function setActiveTaskProject(projectKey: string | undefined) { _activeTaskProject = projectKey } function setActiveTaskStatus(statusKey: string | undefined) { _activeTaskStatus = statusKey } // 构造当前 task 视图筛选对应的 query(供数据变更 listener 复用同一筛选拉取) function buildActiveTaskQuery(): TaskQuery | undefined { if (_activeTaskProject === undefined) return undefined // 视图未挂载,不触发 const query: TaskQuery = {} if (_activeTaskProject !== 'all') query.project_id = _activeTaskProject if (_activeTaskStatus && _activeTaskStatus !== 'all') query.status = _activeTaskStatus return query } let _dataChangedUnlisten: (() => void) | null = null async function startDataChangedListener() { if (_dataChangedUnlisten) return _dataChangedUnlisten _dataChangedUnlisten = await listen('df-data-changed', (event) => { const { entity } = event.payload // 按 entity 调对应 load 刷新(CR-260615-19:action 当前未消费,为未来 delete 本地移除等差异化刷新预留,统一 load 全量拉取) if (entity === 'project') { void projectsStore.loadProjects() } else if (entity === 'task') { // 无筛选状态(_activeTaskProject undefined)时不触发,保 B-29 筛选契约; // 有筛选则按当前 activeProject + activeStatus 构造 query 下沉后端拉取 const q = buildActiveTaskQuery() if (q !== undefined) { void tasksStore.loadTasks(q) } } else if (entity === 'idea') { void ideasStore.loadIdeas() } }) return _dataChangedUnlisten } function stopDataChangedListener() { if (_dataChangedUnlisten) { try { _dataChangedUnlisten() } catch (e) { console.error('停止数据变更监听失败:', e) } _dataChangedUnlisten = 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 }, get deletedProjects() { return state.deletedProjects }, clearError, // project actions loadProjects: projectsStore.loadProjects, createProject: projectsStore.createProject, importProject: projectsStore.importProject, updateProject: projectsStore.updateProject, deleteProject: projectsStore.deleteProject, relocateProjectPath: projectsStore.relocateProjectPath, loadDeletedProjects: projectsStore.loadDeletedProjects, restoreProject: projectsStore.restoreProject, purgeProject: projectsStore.purgeProject, // task actions loadTasks: tasksStore.loadTasks, createTask: tasksStore.createTask, updateTask: tasksStore.updateTask, deleteTask: tasksStore.deleteTask, // idea actions loadIdeas: ideasStore.loadIdeas, createIdea: ideasStore.createIdea, updateIdea: ideasStore.updateIdea, deleteIdea: ideasStore.deleteIdea, evaluateIdea: ideasStore.evaluateIdea, promoteIdea: ideasStore.promoteIdea, relateIdeas: ideasStore.relateIdeas, // workflow actions runWorkflow: workflowStore.runWorkflow, loadWorkflowExecutions: workflowStore.loadWorkflowExecutions, startEventListener: workflowStore.startEventListener, stopEventListener: workflowStore.stopEventListener, clearLiveEvents: workflowStore.clearLiveEvents, approveHumanApproval: workflowStore.approveHumanApproval, cancelHumanApproval: workflowStore.cancelHumanApproval, // AR-11(方案A):数据变更联动刷新监听(后端工具执行后 emit df-data-changed) startDataChangedListener, stopDataChangedListener, // CR-260615-26(R-1): Tasks 视图登记当前 activeProject 筛选,供 listener 按筛选拉取 setActiveTaskProject, // F-260621-02(P1 status 下沉):Tasks 视图登记当前 status 筛选,供 listener 复用同一筛选拉取 setActiveTaskStatus, 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 }