Files
DevFlow/src/stores/project.ts
绝尘 5c539fe764 修复: 前端 TS 阻断 + G1/G2 单测 + 归档已完成项
- 修复 vue-tsc 3 阻断: i18n 重复 err key 删除 + store 补 relateIdeas
- G1: extract_pinned_goal 单测 10 条(mention剥离/截断/空输入/全角冒号)
- G2: is_empty_tool_result 单测 11 条(JSON标记/中文/英文/非空判定)
- 归档: todo.md/docs/todo.md/待决策.md/待审查.md 已完成项迁入归档
2026-06-27 22:35:37 +08:00

165 lines
7.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<DfDataChangedPayload>('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<typeof createStore>
let _storeInstance: ProjectStore | null = null
/** 项目/任务/灵感/工作流 全局状态(单例,多组件复用同一 reactive 包装) */
export function useProjectStore(): ProjectStore {
if (_storeInstance) return _storeInstance
_storeInstance = createStore()
return _storeInstance
}