Files
DevFlow/src/stores/project.ts
绝尘 65c475b549 优化: AR-10 想法→灵感文案统一(13 文件)
用户可见文案 + 后端错误信息 + LLM 工具描述/系统提示词 + store toast + 注释统一改灵感:
i18n zh-CN(ideas/aiTool/projectDetail) + idea.rs 错误 + tool_registry/prompt LLM + stores/project toast + api/views 注释
en 版待定(用 Ideas/Idea)+ docs/crates 注释低优先本轮略;cargo check/vue-tsc 0 error
2026-06-14 16:26:32 +08:00

318 lines
9.7 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 { 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<string, unknown>) {
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<typeof createStore>
let _storeInstance: ProjectStore | null = null
/** 项目/任务/灵感/工作流 全局状态(单例,多组件复用同一 reactive 包装) */
export function useProjectStore(): ProjectStore {
if (_storeInstance) return _storeInstance
_storeInstance = createStore()
return _storeInstance
}