P0 修复:
- B-260615-37: shell.rs 补 Stdio::piped() 修复 run_command stdout/stderr 恒空
(tokio 默认 inherit 导致 wait_with_output 读不到 pipe)
P1 功能:
- B-260615-36: 路由加 /ideas/:id + Ideas.vue 接 route.params.id + watch
(修复项目详情点「来源灵感」跳空白页)
- F-260615-08: 新增 file_info 工具(元信息: exists/size/lines/binary/dir)
- F-260615-09: 新增 append_file 工具(追加写入, RiskLevel::Medium)
- F-260615-12: 新增 search_files 工具(文件名 glob 搜索,递归,限50条)
+ search_files_recursive 辅助函数 + i18n zh/en 3工具×2命名空间
DRY/重构:
- CR-260615-09: .ai-md 样式5份→全局 src/styles/ai-md.css(75行)
AiChat.vue 删71行重复 + main.ts 引入 + typo修正(.a-md→.ai-md)
- 全量 import 路径统一为 @/ 别名(stores/composables/views ~28文件)
vite.config.ts 加 resolve.alias.{ '@': '/src' }
- i18n 批量改进: store error fallback 11处中文→t() / ToolCard ARG_LABEL
/ useAiSend queue文案 / Dashboard 空态 / Ideas.vue null守卫
- cargo check 0 error / vue-tsc 0 error
144 lines
6.2 KiB
TypeScript
144 lines
6.2 KiB
TypeScript
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 } 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 更新本变量。
|
||
let _activeTaskProject: string | undefined = undefined
|
||
function setActiveTaskProject(projectKey: string | undefined) {
|
||
_activeTaskProject = projectKey
|
||
}
|
||
|
||
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') {
|
||
// 无筛选状态(undefined)时不触发,保 B-29 筛选契约;有筛选则按当前 activeProject 拉
|
||
if (_activeTaskProject !== undefined) {
|
||
void tasksStore.loadTasks(_activeTaskProject === 'all' ? undefined : _activeTaskProject)
|
||
}
|
||
} 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,
|
||
// 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,
|
||
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
|
||
}
|