新增: 三实体列表查询维度补全(status/关键词/排序/分页下沉后端)
任务/项目/灵感三实体新增 list_by_query 动态 WHERE(累积式 where_clauses
+params_vec 收口)+ order_by 白名单防注入 + limit/offset 钳制。命令层
list_{tasks,projects,ideas} 吃 Option<XxxQuery> 双参向后兼容(旧无参/单参
路径等价全量)。前端 Tasks/Ideas status/keyword 筛选下沉后端 query。
F-260621-02
This commit is contained in:
@@ -1,10 +1,21 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import type { IdeaRecord, CreateIdeaInput, PromotionResult, IdeaEvaluationRecord } from './types'
|
||||
import type { IdeaRecord, CreateIdeaInput, PromotionResult, IdeaEvaluationRecord, IdeaQuery } from './types'
|
||||
|
||||
export const ideaApi = {
|
||||
/** 列出灵感,可选按 status 过滤(draft/pending_review/promoted 等) */
|
||||
list(status?: string): Promise<IdeaRecord[]> {
|
||||
return invoke('list_ideas', { status: status ?? null })
|
||||
/**
|
||||
* 列出灵感。
|
||||
*
|
||||
* **双签名向后兼容**(F-260621-02):
|
||||
* - `list(status?)`:旧签名,仅状态过滤(等价 `list({ status })`)。
|
||||
* - `list(query?)`:新签名,多条件(status/keyword/order_by/limit/offset)走后端 WHERE。
|
||||
*
|
||||
* 参数为空 → 后端返回全量(created_at DESC,与旧 list_all 等价)。
|
||||
*/
|
||||
list(queryOrStatus?: IdeaQuery | string): Promise<IdeaRecord[]> {
|
||||
if (typeof queryOrStatus === 'string') {
|
||||
return invoke('list_ideas', { status: queryOrStatus ?? null, query: null })
|
||||
}
|
||||
return invoke('list_ideas', { status: null, query: queryOrStatus ?? null })
|
||||
},
|
||||
|
||||
create(input: CreateIdeaInput): Promise<IdeaRecord> {
|
||||
|
||||
@@ -46,9 +46,29 @@ export interface ImportBatchResult {
|
||||
items: ImportBatchItemResult[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 项目列表查询条件(F-260621-02 P2/P3)。
|
||||
* 全可选;不传(或全 undefined)= 当前全量行为(deleted_at IS NULL + created_at DESC)。
|
||||
* 对齐后端 df_storage::crud::ProjectQuery。
|
||||
*/
|
||||
export interface ProjectQuery {
|
||||
/** 关键词:name/description LIKE %kw%(trim 后空字符串视为无关键词) */
|
||||
keyword?: string
|
||||
/** 排序:格式 "col" / "col asc" / "col desc",默认 created_at desc(列名白名单) */
|
||||
order_by?: string
|
||||
/** 返回上限(后端钳制 [0,200],undefined = 不加 LIMIT 全量) */
|
||||
limit?: number
|
||||
/** 偏移量(undefined = 0,配合 limit 分页) */
|
||||
offset?: number
|
||||
}
|
||||
|
||||
export const projectApi = {
|
||||
list(): Promise<ProjectRecord[]> {
|
||||
return invoke('list_projects')
|
||||
/**
|
||||
* 列出未删除项目。
|
||||
* F-260621-02:可选 query(关键词/排序/分页)。不传 = 当前全量行为(向后兼容)。
|
||||
*/
|
||||
list(query?: ProjectQuery): Promise<ProjectRecord[]> {
|
||||
return invoke('list_projects', { query })
|
||||
},
|
||||
|
||||
create(input: CreateProjectInput): Promise<ProjectRecord> {
|
||||
|
||||
@@ -1,9 +1,21 @@
|
||||
import { invoke } from '@tauri-apps/api/core'
|
||||
import type { TaskRecord, CreateTaskInput } from './types'
|
||||
import type { TaskRecord, CreateTaskInput, TaskQuery } from './types'
|
||||
|
||||
export const taskApi = {
|
||||
list(projectId?: string): Promise<TaskRecord[]> {
|
||||
return invoke('list_tasks', { projectId: projectId ?? null })
|
||||
/**
|
||||
* 列出任务。
|
||||
*
|
||||
* **双签名向后兼容**(F-260621-02):
|
||||
* - `list(projectId?)`:旧签名,仅项目过滤(等价 `list({ project_id: projectId })`)。
|
||||
* - `list(query?)`:新签名,多条件(status/keyword/project_id/priority/assignee/...)走后端 WHERE。
|
||||
*
|
||||
* 参数为空 → 后端返回全量未删任务(created_at DESC,等价改造前 list_active)。
|
||||
*/
|
||||
list(queryOrProjectId?: TaskQuery | string): Promise<TaskRecord[]> {
|
||||
if (typeof queryOrProjectId === 'string') {
|
||||
return invoke('list_tasks', { projectId: queryOrProjectId ?? null, query: null })
|
||||
}
|
||||
return invoke('list_tasks', { projectId: null, query: queryOrProjectId ?? null })
|
||||
},
|
||||
|
||||
get(id: string): Promise<TaskRecord> {
|
||||
|
||||
@@ -34,6 +34,23 @@ export interface CreateIdeaInput {
|
||||
source?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 灵感多条件查询入参(F-260621-02,对齐后端 IdeaQuery)。
|
||||
*
|
||||
* 所有字段可选;全空 → 后端返回全量(created_at DESC)。
|
||||
* - `status`:状态精确匹配(后端 WHERE,取代前端 filter)
|
||||
* - `keyword`:title/description LIKE %kw%(后端 LIKE,取代前端 filter)
|
||||
* - `order_by`:白名单 created_at/updated_at/priority/status/score(后端白名单校验防注入)
|
||||
* - `limit`/`offset`:分页(limit 钳制上限 200)
|
||||
*/
|
||||
export interface IdeaQuery {
|
||||
status?: string | null
|
||||
keyword?: string | null
|
||||
order_by?: string | null
|
||||
limit?: number | null
|
||||
offset?: number | null
|
||||
}
|
||||
|
||||
/**
|
||||
* 灵感评估历史记录(后端 list_idea_evaluations 返回)。
|
||||
* 每次评估(启发式/LLM/降级)落一行,version 递增,按 version DESC 返回。
|
||||
@@ -152,6 +169,32 @@ export interface CreateTaskInput {
|
||||
idea_id?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 任务多条件查询入参(F-260621-02,对齐后端 TaskQuery)。
|
||||
*
|
||||
* 所有字段可选;全空 → 后端返回全量未删任务(created_at DESC,等价改造前 list_active)。
|
||||
* - `project_id`:项目精确匹配(命中 idx_tasks_project_id)
|
||||
* - `status`:状态精确匹配(P1 下沉,命中 idx_tasks_status,取代前端 filter)
|
||||
* - `priority`:优先级精确匹配(0=critical..3=low)
|
||||
* - `assignee`:负责人精确匹配
|
||||
* - `keyword`:title/description LIKE %kw%(P2,取代前端 filter)
|
||||
* - `order_by`:白名单 created_at/updated_at/priority/status(后端白名单校验防注入),恒 DESC
|
||||
* - `limit`/`offset`:分页(limit 钳制上限 500)
|
||||
*
|
||||
* 注:project_id/priority/assignee/order_by/limit/offset 为基建就绪,当前视图仅用
|
||||
* status(P1)+keyword(P2);P3 排序分页 UI 待数据量增长。
|
||||
*/
|
||||
export interface TaskQuery {
|
||||
project_id?: string | null
|
||||
status?: string | null
|
||||
priority?: number | null
|
||||
assignee?: string | null
|
||||
keyword?: string | null
|
||||
order_by?: string | null
|
||||
limit?: number | null
|
||||
offset?: number | null
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 工作流
|
||||
// ============================================================
|
||||
|
||||
@@ -5,7 +5,7 @@ import { createTasksStore } from './project/tasks'
|
||||
import { createIdeasStore } from './project/ideas'
|
||||
import { createWorkflowStore } from './project/workflow'
|
||||
import { state, clearError } from './project/state'
|
||||
import type { DfDataChangedPayload } from '@/api/types'
|
||||
import type { DfDataChangedPayload, TaskQuery } from '@/api/types'
|
||||
|
||||
// ── barrel 组合器(零行为变更,纯重构) ──
|
||||
//
|
||||
@@ -34,10 +34,26 @@ function createStore() {
|
||||
// - 'all' : 全量拉取
|
||||
// - 具体 id : 按项目拉取
|
||||
// 由 Tasks.vue 经 setActiveTaskProject 在 onMounted/watch 更新本变量。
|
||||
//
|
||||
// F-260621-02(P1 status 下沉):新增 _activeTaskStatus 追踪当前 status 筛选,数据变更
|
||||
// 联动刷新时一并下沉到后端 query(保 B-29「store.tasks 反映当前筛选」契约,
|
||||
// 避免刷新把已选 status 冲掉)。
|
||||
let _activeTaskProject: string | undefined = undefined
|
||||
let _activeTaskStatus: string | undefined = undefined
|
||||
function setActiveTaskProject(projectKey: string | undefined) {
|
||||
_activeTaskProject = projectKey
|
||||
}
|
||||
function setActiveTaskStatus(statusKey: string | undefined) {
|
||||
_activeTaskStatus = statusKey
|
||||
}
|
||||
// 构造当前 task 视图筛选对应的 query(供数据变更 listener 复用同一筛选拉取)
|
||||
function buildActiveTaskQuery(): TaskQuery | undefined {
|
||||
if (_activeTaskProject === undefined) return undefined // 视图未挂载,不触发
|
||||
const query: TaskQuery = {}
|
||||
if (_activeTaskProject !== 'all') query.project_id = _activeTaskProject
|
||||
if (_activeTaskStatus && _activeTaskStatus !== 'all') query.status = _activeTaskStatus
|
||||
return query
|
||||
}
|
||||
|
||||
let _dataChangedUnlisten: (() => void) | null = null
|
||||
|
||||
@@ -49,9 +65,11 @@ function createStore() {
|
||||
if (entity === 'project') {
|
||||
void projectsStore.loadProjects()
|
||||
} else if (entity === 'task') {
|
||||
// 无筛选状态(undefined)时不触发,保 B-29 筛选契约;有筛选则按当前 activeProject 拉
|
||||
if (_activeTaskProject !== undefined) {
|
||||
void tasksStore.loadTasks(_activeTaskProject === 'all' ? undefined : _activeTaskProject)
|
||||
// 无筛选状态(_activeTaskProject undefined)时不触发,保 B-29 筛选契约;
|
||||
// 有筛选则按当前 activeProject + activeStatus 构造 query 下沉后端拉取
|
||||
const q = buildActiveTaskQuery()
|
||||
if (q !== undefined) {
|
||||
void tasksStore.loadTasks(q)
|
||||
}
|
||||
} else if (entity === 'idea') {
|
||||
void ideasStore.loadIdeas()
|
||||
@@ -126,6 +144,8 @@ function createStore() {
|
||||
stopDataChangedListener,
|
||||
// CR-260615-26(R-1): Tasks 视图登记当前 activeProject 筛选,供 listener 按筛选拉取
|
||||
setActiveTaskProject,
|
||||
// F-260621-02(P1 status 下沉):Tasks 视图登记当前 status 筛选,供 listener 复用同一筛选拉取
|
||||
setActiveTaskStatus,
|
||||
pendingApproval: computed(() => state.pendingApproval),
|
||||
// computed
|
||||
stats,
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import { ideaApi } from '@/api'
|
||||
import type { IdeaQuery } from '@/api/types'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import { state } from './state'
|
||||
|
||||
/** 灵感 CRUD 子 store(共享全局 state) */
|
||||
export function createIdeasStore() {
|
||||
async function loadIdeas() {
|
||||
/**
|
||||
* 加载灵感列表。
|
||||
*
|
||||
* F-260621-02:status/keyword/order_by 下沉后端 WHERE,取代前端 computed filter。
|
||||
* - 传 `query` → 多条件查询(状态/关键词/排序/分页)。
|
||||
* - 不传 → 全量(created_at DESC,等价旧行为)。
|
||||
*/
|
||||
async function loadIdeas(query?: IdeaQuery) {
|
||||
try {
|
||||
state.ideas = await ideaApi.list()
|
||||
state.ideas = await ideaApi.list(query)
|
||||
} catch (e: any) {
|
||||
state.error = e?.toString() ?? t('ideas.err.loadFailed')
|
||||
}
|
||||
|
||||
@@ -1,15 +1,19 @@
|
||||
import { projectApi } from '@/api'
|
||||
import type { ImportProjectInput } from '@/api/project'
|
||||
import type { ImportProjectInput, ProjectQuery } from '@/api/project'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import { state, clearError } from './state'
|
||||
|
||||
/** 项目 CRUD 子 store(共享全局 state) */
|
||||
export function createProjectsStore() {
|
||||
async function loadProjects() {
|
||||
/**
|
||||
* 加载未删除项目。
|
||||
* F-260621-02:可选 query 透传(关键词/排序/分页)。不传 = 当前全量行为(向后兼容)。
|
||||
*/
|
||||
async function loadProjects(query?: ProjectQuery) {
|
||||
state.loading = true
|
||||
state.error = null
|
||||
try {
|
||||
state.projects = await projectApi.list()
|
||||
state.projects = await projectApi.list(query)
|
||||
} catch (e: any) {
|
||||
state.error = e?.toString() ?? t('projects.err.loadFailed')
|
||||
} finally {
|
||||
|
||||
@@ -1,12 +1,23 @@
|
||||
import { taskApi } from '@/api'
|
||||
import type { TaskQuery } from '@/api/types'
|
||||
import { t } from '@/i18n/i18n-helpers'
|
||||
import { state } from './state'
|
||||
|
||||
/** 任务 CRUD 子 store(共享全局 state) */
|
||||
export function createTasksStore() {
|
||||
async function loadTasks(projectId?: string) {
|
||||
/**
|
||||
* 加载任务列表。
|
||||
*
|
||||
* **双签名向后兼容**(F-260621-02):
|
||||
* - `loadTasks(projectId?)`:旧签名,仅项目过滤(等价 `loadTasks({ project_id: projectId })`)。
|
||||
* - `loadTasks(query?)`:新签名,多条件(status/keyword/...)走后端 WHERE。
|
||||
*
|
||||
* 不传 → 全量未删任务(created_at DESC,等价改造前 list_active)。
|
||||
* status/keyword 下沉后端 WHERE(P1/P2),取代 Tasks.vue 旧的前端内存 filter。
|
||||
*/
|
||||
async function loadTasks(queryOrProjectId?: TaskQuery | string) {
|
||||
try {
|
||||
state.tasks = await taskApi.list(projectId)
|
||||
state.tasks = await taskApi.list(queryOrProjectId)
|
||||
} catch (e: any) {
|
||||
state.error = e?.toString() ?? t('tasks.err.loadFailed')
|
||||
}
|
||||
|
||||
@@ -126,8 +126,7 @@ import { useRouter, useRoute } from 'vue-router'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import { useProjectStore } from '../stores/project'
|
||||
import type { IdeaStatus } from '../api/types'
|
||||
import { parseTags } from '../stores/knowledge'
|
||||
import type { IdeaStatus, IdeaQuery } from '../api/types'
|
||||
import { formatDate } from '../utils/time'
|
||||
import { stripMd } from '../utils/markdown'
|
||||
import ConfirmDialog from '../components/ConfirmDialog.vue'
|
||||
@@ -181,38 +180,61 @@ function statusLabelKey(status: IdeaStatus): string {
|
||||
return statusOptions.find(s => s.value === status)?.labelKey ?? status
|
||||
}
|
||||
|
||||
// ── 后端查询构造(F-260621-02:status/keyword/order_by 下沉后端 WHERE) ──
|
||||
//
|
||||
// status 下沉:单一状态(all→不限 / promoted→promoted)走后端精确匹配;
|
||||
// hot(按 score≥80)与 pending(多状态 draft+pending_review)无法映射单一后端 status,
|
||||
// 仍在前端 filter(轻量,数据量小;后端 status 仅单值,扩多值属过度设计)。
|
||||
// keyword/title+description LIKE、order_by(排序)全下沉后端,避免前端全表扫描/重排。
|
||||
function buildIdeaQuery(): IdeaQuery {
|
||||
const q: IdeaQuery = {}
|
||||
// 排序下沉后端(score/time)
|
||||
q.order_by = sortMode.value === 'score' ? 'score' : 'created_at'
|
||||
// keyword 下沉后端(title/description LIKE)
|
||||
if (searchQuery.value.trim()) {
|
||||
q.keyword = searchQuery.value.trim()
|
||||
}
|
||||
// 单一状态映射:promoted → status=promoted;all/hot/pending 不走单一 status
|
||||
if (activeFilter.value === 'promoted') {
|
||||
q.status = 'promoted'
|
||||
}
|
||||
return q
|
||||
}
|
||||
|
||||
// 按当前筛选/搜索/排序触发后端加载(防抖仅对 keyword 输入,筛选/排序即时)。
|
||||
// 让 store.loading 驱动「加载中」空态显示(对齐 Tasks.vue 三态)。
|
||||
let keywordDebounce: ReturnType<typeof setTimeout> | null = null
|
||||
async function reloadIdeas(immediate = false) {
|
||||
if (keywordDebounce) {
|
||||
clearTimeout(keywordDebounce)
|
||||
keywordDebounce = null
|
||||
}
|
||||
const run = () => store.loadIdeas(buildIdeaQuery())
|
||||
if (immediate) {
|
||||
await run()
|
||||
} else {
|
||||
keywordDebounce = setTimeout(() => { void run() }, 300)
|
||||
}
|
||||
}
|
||||
|
||||
// 筛选/排序变化即时重载;keyword 输入防抖重载
|
||||
watch(activeFilter, () => void reloadIdeas(true))
|
||||
watch(sortMode, () => void reloadIdeas(true))
|
||||
watch(searchQuery, () => void reloadIdeas(false))
|
||||
|
||||
const filteredIdeas = computed(() => {
|
||||
let ideas = store.ideas
|
||||
|
||||
// 应用状态过滤
|
||||
// hot(按 score≥80)/pending(多状态 draft+pending_review)保留前端 filter:
|
||||
// 后端 status 仅单值,无法表达 OR,扩多值属过度设计;数据量小,前端 filter 轻量。
|
||||
switch (activeFilter.value) {
|
||||
case 'hot': ideas = ideas.filter(i => (i.score ?? 0) >= 80); break
|
||||
case 'pending': ideas = ideas.filter(i => i.status === 'pending_review' || i.status === 'draft'); break
|
||||
case 'promoted': ideas = ideas.filter(i => i.status === 'promoted'); break
|
||||
default: break // 不过滤
|
||||
default: break // all / promoted:后端已过滤,前端不再 filter
|
||||
}
|
||||
|
||||
// 应用搜索过滤
|
||||
if (searchQuery.value.trim()) {
|
||||
const query = searchQuery.value.toLowerCase().trim()
|
||||
ideas = ideas.filter(i =>
|
||||
i.title.toLowerCase().includes(query) ||
|
||||
(i.description && i.description.toLowerCase().includes(query)) ||
|
||||
parseTags(i.tags).some((tag: string) => tag.toLowerCase().includes(query))
|
||||
)
|
||||
}
|
||||
|
||||
// 排序:在过滤+搜索之后、return 之前对最终数组排序
|
||||
// [...ideas] 浅拷贝避免改原数组(store.ideas)
|
||||
const sorted = [...ideas].sort((a, b) => {
|
||||
if (sortMode.value === 'score') {
|
||||
return (b.score ?? 0) - (a.score ?? 0) // 高分在前
|
||||
}
|
||||
// time:created_at 为毫秒字符串,字符串比较即可(同位数字典序等价数值序)
|
||||
return (b.created_at ?? '').localeCompare(a.created_at ?? '') // 新在前
|
||||
})
|
||||
|
||||
return sorted
|
||||
// 排序与 keyword 已下沉后端(buildIdeaQuery),前端不再重排/重过滤。
|
||||
return ideas
|
||||
})
|
||||
|
||||
const currentIdea = computed(() => {
|
||||
@@ -279,7 +301,7 @@ async function promoteToProject() {
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
await store.loadIdeas()
|
||||
await store.loadIdeas(buildIdeaQuery())
|
||||
}
|
||||
|
||||
const evaluating = ref(false)
|
||||
@@ -318,7 +340,7 @@ async function onUpdateRelated(ids: string[]) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await store.loadIdeas()
|
||||
await store.loadIdeas(buildIdeaQuery())
|
||||
// 从路由参数恢复选中灵感(修复 /ideas/:id 直接访问空白页 B-260615-36)
|
||||
if (route.params.id) {
|
||||
const id = route.params.id as string
|
||||
|
||||
@@ -114,7 +114,7 @@ import { useI18n } from 'vue-i18n'
|
||||
import { useProjectStore } from '@/stores/project'
|
||||
import { formatRelative } from '@/utils/time'
|
||||
import { taskStatusLabel as statusLabel, taskStatusClass, priorityLabel, priorityClass } from '../constants/project'
|
||||
import type { TaskRecord } from '@/api/types'
|
||||
import type { TaskRecord, TaskQuery } from '@/api/types'
|
||||
|
||||
const router = useRouter()
|
||||
const store = useProjectStore()
|
||||
@@ -169,14 +169,14 @@ const projectIcons: Record<string, string> = {
|
||||
'flux': '⚡',
|
||||
}
|
||||
|
||||
// F-260621-02(P1 status 下沉):status 过滤从前端内存 filter 改为构造 query 调后端,
|
||||
// store.tasks 已是筛选后的结果(后端 WHERE)。filteredGroups 仅做按项目分组(project
|
||||
// 维度也有后端 query 下沉,这里 groupMap 仅做展示分组,不再二次 filter)。
|
||||
//
|
||||
// 注:project 维度同样下沉后端(activeProject watch 构造 query),store.tasks 已按
|
||||
// project_id 过滤。groupMap 保留分组逻辑(展示按项目聚合),无需再 filter。
|
||||
const filteredGroups = computed(() => {
|
||||
let filtered = store.tasks
|
||||
if (activeProject.value !== 'all') {
|
||||
filtered = filtered.filter(t => t.project_id === activeProject.value)
|
||||
}
|
||||
if (activeStatus.value !== 'all') {
|
||||
filtered = filtered.filter(t => t.status === activeStatus.value)
|
||||
}
|
||||
const filtered = store.tasks
|
||||
|
||||
const groupMap = new Map<string, TaskRecord[]>()
|
||||
for (const task of filtered) {
|
||||
@@ -200,10 +200,23 @@ const filteredGroups = computed(() => {
|
||||
|
||||
// statusLabel / priorityLabel / priorityClass 由 ../constants/project 提供(与 ProjectDetail 文案统一)
|
||||
|
||||
// F-260621-02(P1 status 下沉):按当前 activeProject + activeStatus 构造后端 query。
|
||||
// - 全 all → undefined(全量,等价旧行为)
|
||||
// - 任一具体值 → query 下沉后端 WHERE(project_id / status)
|
||||
function buildTaskQuery(): TaskQuery | undefined {
|
||||
const projAll = activeProject.value === 'all'
|
||||
const statusAll = activeStatus.value === 'all'
|
||||
if (projAll && statusAll) return undefined
|
||||
const query: TaskQuery = {}
|
||||
if (!projAll) query.project_id = activeProject.value
|
||||
if (!statusAll) query.status = activeStatus.value
|
||||
return query
|
||||
}
|
||||
|
||||
async function refresh() {
|
||||
loading.value = true
|
||||
try {
|
||||
await store.loadTasks(activeProject.value === 'all' ? undefined : activeProject.value)
|
||||
await store.loadTasks(buildTaskQuery())
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
@@ -236,17 +249,27 @@ async function confirmCreate() {
|
||||
}
|
||||
}
|
||||
|
||||
// B-260615-29: 切换项目筛选时按 projectId 重载(避免仅前端 filter,跨项目视图不同步)
|
||||
// B-260615-29: 切换项目筛选时按筛选重载(避免仅前端 filter,跨项目视图不同步)
|
||||
// CR-260615-26(R-1): 同步登记当前筛选到 store,供 AR-11 listener 按筛选拉取
|
||||
watch(activeProject, (key) => {
|
||||
store.setActiveTaskProject(key)
|
||||
// F-260621-02(P1 status 下沉):重载 query 同时含 project_id + status,前端不再 filter status
|
||||
watch(activeProject, () => {
|
||||
store.setActiveTaskProject(activeProject.value)
|
||||
loading.value = true
|
||||
store.loadTasks(key === 'all' ? undefined : key).finally(() => { loading.value = false })
|
||||
store.loadTasks(buildTaskQuery()).finally(() => { loading.value = false })
|
||||
})
|
||||
|
||||
// F-260621-02(P1 status 下沉):切换 status 筛选时构造 query 重载(后端 WHERE,非前端 filter),
|
||||
// 并登记到 store 供 AR-11 listener 复用同一筛选(保 B-29 契约)
|
||||
watch(activeStatus, (key) => {
|
||||
store.setActiveTaskStatus(key)
|
||||
loading.value = true
|
||||
store.loadTasks(buildTaskQuery()).finally(() => { loading.value = false })
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
// R-1: 登记初始筛选(all=全量),让数据变更 listener 知道视图已挂载、按全量拉取
|
||||
store.setActiveTaskProject(activeProject.value)
|
||||
store.setActiveTaskStatus(activeStatus.value)
|
||||
loading.value = true
|
||||
try {
|
||||
await Promise.all([store.loadProjects(), store.loadTasks()])
|
||||
|
||||
Reference in New Issue
Block a user