修复: CR-26 AR-11 listener 按 B-29 筛选契约+Tasks 空态兜底
project.ts listener task 分支改读 _activeTaskProject 闭包 var(undefined 跳过保筛选/all 全量/具体 id 按项目),加 setActiveTaskProject 暴露由 Tasks 注册,保 B-29 store.tasks 反映当前筛选契约(原无参 loadTasks 冲成全量破坏筛选);Tasks.vue 加 v-if loading/error/empty 兜底+setActiveTaskProject onMounted/watch 更新。批8,vue-tsc 0
This commit is contained in:
@@ -26,6 +26,19 @@ function createStore() {
|
||||
// 后端 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() {
|
||||
@@ -36,7 +49,10 @@ function createStore() {
|
||||
if (entity === 'project') {
|
||||
void projectsStore.loadProjects()
|
||||
} else if (entity === 'task') {
|
||||
void tasksStore.loadTasks()
|
||||
// 无筛选状态(undefined)时不触发,保 B-29 筛选契约;有筛选则按当前 activeProject 拉
|
||||
if (_activeTaskProject !== undefined) {
|
||||
void tasksStore.loadTasks(_activeTaskProject === 'all' ? undefined : _activeTaskProject)
|
||||
}
|
||||
} else if (entity === 'idea') {
|
||||
void ideasStore.loadIdeas()
|
||||
}
|
||||
@@ -108,6 +124,8 @@ function createStore() {
|
||||
// AR-11(方案A):数据变更联动刷新监听(后端工具执行后 emit df-data-changed)
|
||||
startDataChangedListener,
|
||||
stopDataChangedListener,
|
||||
// CR-260615-26(R-1): Tasks 视图登记当前 activeProject 筛选,供 listener 按筛选拉取
|
||||
setActiveTaskProject,
|
||||
pendingApproval: computed(() => state.pendingApproval),
|
||||
// computed
|
||||
stats,
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
|
||||
<!-- 按项目分组 -->
|
||||
<div class="task-groups">
|
||||
<!-- Y-2: loading/error/empty 兜底(参考 Projects.vue 空态写法) -->
|
||||
<div v-if="loading" class="empty-state">{{ $t('common.loading') }}</div>
|
||||
<div v-else-if="store.error" class="empty-state">{{ store.error }}</div>
|
||||
<div v-else-if="filteredGroups.length === 0" class="empty-state">暂无任务</div>
|
||||
<template v-else>
|
||||
<section
|
||||
v-for="group in filteredGroups"
|
||||
:key="group.projectName"
|
||||
@@ -68,6 +73,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 新建任务模态框 -->
|
||||
@@ -115,6 +121,8 @@ const { t } = useI18n()
|
||||
|
||||
const activeProject = ref('all')
|
||||
const activeStatus = ref('all')
|
||||
// Y-2: 本视图加载态(loadTasks 不翻转全局 state.loading,故用局部 ref 精确反映任务加载)
|
||||
const loading = ref(false)
|
||||
|
||||
// ── 新建任务模态框 ──
|
||||
const showCreateModal = ref(false)
|
||||
@@ -186,7 +194,12 @@ const filteredGroups = computed(() => {
|
||||
// statusLabel / priorityLabel / priorityClass 由 ../constants/project 提供(与 ProjectDetail 文案统一)
|
||||
|
||||
async function refresh() {
|
||||
await store.loadTasks()
|
||||
loading.value = true
|
||||
try {
|
||||
await store.loadTasks(activeProject.value === 'all' ? undefined : activeProject.value)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreateModal() {
|
||||
@@ -212,12 +225,22 @@ async function confirmCreate() {
|
||||
}
|
||||
|
||||
// B-260615-29: 切换项目筛选时按 projectId 重载(避免仅前端 filter,跨项目视图不同步)
|
||||
// CR-260615-26(R-1): 同步登记当前筛选到 store,供 AR-11 listener 按筛选拉取
|
||||
watch(activeProject, (key) => {
|
||||
store.loadTasks(key === 'all' ? undefined : key)
|
||||
store.setActiveTaskProject(key)
|
||||
loading.value = true
|
||||
store.loadTasks(key === 'all' ? undefined : key).finally(() => { loading.value = false })
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// R-1: 登记初始筛选(all=全量),让数据变更 listener 知道视图已挂载、按全量拉取
|
||||
store.setActiveTaskProject(activeProject.value)
|
||||
loading.value = true
|
||||
try {
|
||||
await Promise.all([store.loadProjects(), store.loadTasks()])
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -384,6 +407,12 @@ onMounted(async () => {
|
||||
.status-done { background: rgba(100,255,218,0.15); color: var(--df-success); }
|
||||
.status-abandoned { background: rgba(255,107,107,0.2); color: var(--df-danger); }
|
||||
|
||||
/* ===== 空状态(Y-2: loading/error/empty 兜底,样式参考 Projects.vue) ===== */
|
||||
.empty-state {
|
||||
text-align: center; padding: 60px 20px;
|
||||
font-size: 14px; color: var(--df-text-dim);
|
||||
}
|
||||
|
||||
/* ===== 模态框 ===== */
|
||||
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.5); display: flex; align-items: center; justify-content: center; z-index: 100; }
|
||||
.modal-box { background: var(--df-bg-card); border: 0.5px solid var(--df-border); border-radius: var(--df-radius-lg); padding: 24px; min-width: 360px; max-width: 90vw; }
|
||||
|
||||
Reference in New Issue
Block a user