Files
DevFlow/src/stores/project/projects.ts
T
lxy a644e93604 修复: 项目详情页三态分离与反馈一致性
- notFound/已归档/加载失败三态分离:单查兜底+归档恢复入口+加载失败重试钮
- 反馈一致性六项:同步守卫与完成提示/首屏并行加载/筛选空态文案/计数改过滤口径/路径勾选 seq 守卫/删除确认危险态
- 文件列表滚动条对齐全局范式(scrollbar-width:thin 致 Chromium 忽略定制样式)
- 删除失败不再跳转(store 返布尔)/幽灵勾选清理/折叠阈值与 CSS 对齐
2026-10-15 14:27:32 +08:00

137 lines
4.9 KiB
TypeScript

import { projectApi } from '@/api'
import type { ImportProjectInput, ProjectQuery } from '@/api/project'
import { t } from '@/i18n/i18n-helpers'
import { runWithCatch } from '@/composables/useStoreAction'
import { state, clearError } from './state'
/** 项目 CRUD 子 store(共享全局 state) */
export function createProjectsStore() {
/**
* 加载未删除项目。
* 可选 query 透传(关键词/排序/分页)。不传 = 当前全量行为(向后兼容)。
*/
async function loadProjects(query?: ProjectQuery) {
state.loading = true
state.error = null
await runWithCatch(state, t('projects.err.loadFailed'), async () => {
state.projects = await projectApi.list(query)
})
state.loading = false
}
async function createProject(name: string, description = '', ideaId?: string, path?: string, stack?: string) {
const record = await runWithCatch(state, t('projects.err.createFailed'), async () => {
const r = await projectApi.create({ name, description, idea_id: ideaId, path, stack })
state.projects.push(r)
return r
})
return record ?? null
}
/** 导入历史项目(选已存在目录,后端创建+绑定+探测栈+读 README 首段一步完成) */
async function importProject(input: ImportProjectInput) {
const record = await runWithCatch(state, t('projects.err.importFailed'), async () => {
const r = await projectApi.importProject(input)
await loadProjects() // 刷新列表(后端已 insert,统一走 load 避免本地数组与后端不一致)
return r
})
return record ?? 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): Promise<boolean> {
// 返成功布尔(失败时 runWithCatch 已置 state.error),详情页删除据此阻断失败后的误跳转
const ok = await runWithCatch(state, t('projects.err.deleteFailed'), async () => {
await projectApi.delete(id) // 软删 → 回收站(可恢复)
state.projects = state.projects.filter(p => p.id !== id)
return true
})
return ok === true
}
async function loadDeletedProjects() {
await runWithCatch(state, t('projects.err.loadTrashFailed'), async () => {
state.deletedProjects = await projectApi.listDeleted()
})
}
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() ?? t('projects.err.restoreFailed')
}
}
// ── 归档功能 P2(V48):归档箱三动作,对标 tasks/ideas 子 store 同款 ──
/** 归档/取消归档。归档=退出主列表;取消归档=从归档箱移回主列表(两端各刷一份) */
async function archiveProject(id: string, archived: boolean) {
await runWithCatch(state, t('projects.err.archiveFailed'), async () => {
await projectApi.archive(id, archived)
if (archived) {
// 从主列表移出,归档箱按需懒加载(打开归档箱时 loadArchivedProjects)
state.projects = state.projects.filter(p => p.id !== id)
} else {
state.archivedProjects = state.archivedProjects.filter(p => p.id !== id)
await loadProjects() // 取消归档回主列表
}
})
}
/** 加载归档项目(归档箱 Tab 数据源) */
async function loadArchivedProjects() {
await runWithCatch(state, t('projects.err.loadArchivedFailed'), async () => {
state.archivedProjects = await projectApi.listArchived()
})
}
/** 归档箱内恢复到主列表(取消归档语义的 UI 别名,动线用词更自然) */
async function unarchiveProject(id: string) {
await archiveProject(id, false)
}
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() ?? t('projects.err.purgeFailed')
}
}
return {
loadProjects,
createProject,
importProject,
updateProject,
deleteProject,
relocateProjectPath,
loadDeletedProjects,
restoreProject,
purgeProject,
archiveProject,
loadArchivedProjects,
unarchiveProject,
// clearError 来自 state(供 barrel re-export)
clearError,
}
}