Tauri 2 + Vue 3 + Vite 6 桌面应用,Rust workspace 含 13 个 crate (df-ai / df-storage / df-workflow / df-core / df-execute 等)。 核心能力:AI 聊天 agentic 循环(工具调用+人工审批)、工作流引擎、 任务/想法/项目/阶段管理、可追溯性,及配套前端组件。
206 lines
5.9 KiB
TypeScript
206 lines
5.9 KiB
TypeScript
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[],
|
|
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
|
|
|
|
export function useProjectStore() {
|
|
// ── 项目 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
|
|
}
|
|
}
|
|
|
|
async function createProject(name: string, description = '', ideaId?: string) {
|
|
const record = await projectApi.create({ name, description, idea_id: ideaId })
|
|
state.projects.push(record)
|
|
return record
|
|
}
|
|
|
|
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
|
|
}
|
|
}
|
|
|
|
async function deleteProject(id: string) {
|
|
await projectApi.delete(id)
|
|
state.projects = state.projects.filter(p => p.id !== id)
|
|
}
|
|
|
|
// ── 任务 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 }) {
|
|
const record = await taskApi.create(input)
|
|
state.tasks.push(record)
|
|
return record
|
|
}
|
|
|
|
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) {
|
|
await taskApi.delete(id)
|
|
state.tasks = state.tasks.filter(t => t.id !== id)
|
|
}
|
|
|
|
// ── 想法 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 }) {
|
|
const record = await ideaApi.create(input)
|
|
state.ideas.push(record)
|
|
return record
|
|
}
|
|
|
|
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) {
|
|
await ideaApi.delete(id)
|
|
state.ideas = state.ideas.filter(i => i.id !== id)
|
|
}
|
|
|
|
// ── 工作流 ──
|
|
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) => {
|
|
state.liveEvents.push(payload)
|
|
|
|
// 处理人工审批请求
|
|
if (payload.event.type === 'HumanApprovalRequest') {
|
|
state.pendingApproval = payload.event.data as typeof state.pendingApproval
|
|
}
|
|
})
|
|
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)
|
|
}
|
|
}
|
|
|
|
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({
|
|
// reactive state — 直接引用 state 属性,已经是响应式的
|
|
projects: state.projects,
|
|
tasks: state.tasks,
|
|
ideas: state.ideas,
|
|
workflowExecutions: state.workflowExecutions,
|
|
liveEvents: state.liveEvents,
|
|
loading: state.loading,
|
|
error: state.error,
|
|
// project actions
|
|
loadProjects, createProject, updateProject, deleteProject,
|
|
// task actions
|
|
loadTasks, createTask, updateTask, deleteTask,
|
|
// idea actions
|
|
loadIdeas, createIdea, updateIdea, deleteIdea,
|
|
// workflow actions
|
|
runWorkflow, loadWorkflowExecutions, startEventListener, stopEventListener, clearLiveEvents,
|
|
approveHumanApproval, pendingApproval: computed(() => state.pendingApproval),
|
|
// computed
|
|
stats,
|
|
})
|
|
}
|