新增: Phase2 阶段收尾(Sprint 1-20)

重构:删 5 零引用 crate(df-evolve/plugin/stages/task/traceability)+ 清死模块、ai.rs 拆 11 子 module、ai.ts 拆 6 composable、i18n 拆目录
功能:知识库全栈(df-project/scan + CRUD + 时间线 + 前端)、Settings 拆分、appSettings KV 迁移、模型池、LLM 并发 Semaphore
修复:审批持久化根治、ConditionEngine 默认拒绝、NodeRegistry unimplemented 清除、promote 补偿删除、工具结果截断 50KB、路径校验防 symlink 逃逸
文档:B-03 人工审批设计、决策记录三分档、规格契约自检、经验记录、todo 看板、PROGRESS 更新

详见 PROGRESS.md。src-tauri/儿童每日打卡应用/ 与本项目无关,已排除。
This commit is contained in:
2026-06-14 14:08:20 +08:00
parent 98393b4908
commit cf017f81e2
167 changed files with 19549 additions and 6886 deletions

View File

@@ -7,6 +7,7 @@ import type { ProjectRecord, TaskRecord, IdeaRecord, WorkflowRecord, WorkflowEve
const state = reactive({
projects: [] as ProjectRecord[],
deletedProjects: [] as ProjectRecord[],
tasks: [] as TaskRecord[],
ideas: [] as IdeaRecord[],
workflowExecutions: [] as WorkflowRecord[],
@@ -24,7 +25,7 @@ const state = reactive({
let _eventUnlisten: (() => void) | null = null
export function useProjectStore() {
function createStore() {
// ── 项目 CRUD ──
async function loadProjects() {
state.loading = true
@@ -38,10 +39,18 @@ export function useProjectStore() {
}
}
async function createProject(name: string, description = '', ideaId?: string) {
const record = await projectApi.create({ name, description, idea_id: ideaId })
state.projects.push(record)
return record
// 清除错误状态(供 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) {
@@ -52,9 +61,48 @@ export function useProjectStore() {
}
}
/** 重定位项目目录(后端重探测 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) {
await projectApi.delete(id)
state.projects = state.projects.filter(p => p.id !== id)
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 ──
@@ -67,9 +115,14 @@ export function useProjectStore() {
}
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
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) {
@@ -81,8 +134,12 @@ export function useProjectStore() {
}
async function deleteTask(id: string) {
await taskApi.delete(id)
state.tasks = state.tasks.filter(t => t.id !== id)
try {
await taskApi.delete(id)
state.tasks = state.tasks.filter(t => t.id !== id)
} catch (e: any) {
state.error = e?.toString() ?? '删除任务失败'
}
}
// ── 想法 CRUD ──
@@ -95,9 +152,14 @@ export function useProjectStore() {
}
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
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) {
@@ -109,8 +171,25 @@ export function useProjectStore() {
}
async function deleteIdea(id: string) {
await ideaApi.delete(id)
state.ideas = state.ideas.filter(i => i.id !== id)
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
}
// ── 工作流 ──
@@ -129,11 +208,14 @@ export function useProjectStore() {
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
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
@@ -182,20 +264,24 @@ export function useProjectStore() {
}))
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,
// 状态用 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,
loadProjects, createProject, updateProject, deleteProject, relocateProjectPath,
loadDeletedProjects, restoreProject, purgeProject,
// task actions
loadTasks, createTask, updateTask, deleteTask,
// idea actions
loadIdeas, createIdea, updateIdea, deleteIdea,
loadIdeas, createIdea, updateIdea, deleteIdea, evaluateIdea, promoteIdea,
// workflow actions
runWorkflow, loadWorkflowExecutions, startEventListener, stopEventListener, clearLiveEvents,
approveHumanApproval, pendingApproval: computed(() => state.pendingApproval),
@@ -203,3 +289,13 @@ export function useProjectStore() {
stats,
})
}
type ProjectStore = ReturnType<typeof createStore>
let _storeInstance: ProjectStore | null = null
/** 项目/任务/想法/工作流 全局状态(单例,多组件复用同一 reactive 包装) */
export function useProjectStore(): ProjectStore {
if (_storeInstance) return _storeInstance
_storeInstance = createStore()
return _storeInstance
}