diff --git a/crates/df-ai/src/plan_executor.rs b/crates/df-ai/src/plan_executor.rs index 8cb23e5..8349409 100644 --- a/crates/df-ai/src/plan_executor.rs +++ b/crates/df-ai/src/plan_executor.rs @@ -75,7 +75,7 @@ impl PlanExecutor { ); // 层内并行:JoinSet 收集 - let mut join_set: tokio::task::JoinSet = tokio::task::JoinSet::new(); + let _join_set: tokio::task::JoinSet = tokio::task::JoinSet::new(); let summary_clone = parent_summary.clone(); for task in layer { diff --git a/crates/df-storage/src/crud/module_dependency_repo.rs b/crates/df-storage/src/crud/module_dependency_repo.rs index 8d710fb..8b74ef9 100644 --- a/crates/df-storage/src/crud/module_dependency_repo.rs +++ b/crates/df-storage/src/crud/module_dependency_repo.rs @@ -4,7 +4,7 @@ use std::sync::Arc; -use rusqlite::{params, OptionalExtension, Row}; +use rusqlite::{params, Row}; use tokio::sync::Mutex; use crate::db::Database; diff --git a/docs/Batch-Overview.md b/docs/Batch-Overview.md new file mode 100644 index 0000000..117c4e9 --- /dev/null +++ b/docs/Batch-Overview.md @@ -0,0 +1,188 @@ +# DevFlow 批次可视化总览 + +> 基于 [Batch.md](../Batch.md) 生成 | 更新: 2026-07-01 + +--- + +## 一、批次时间线(已完成) + +```mermaid +gantt + title DevFlow 批次推进时间线 + dateFormat YYYY-MM-DD + axisFormat %m-%d + + section 审批与工具卡 + Batch 1 审批超时后端兜底 :done, b1, 2026-06-14, 1d + Batch 2 工具卡片拆分核验 :done, b2, after b1, 1d + Batch 3 条件表达式 UI 翻译 :done, b3, after b2, 1d + + section 数据与状态 + Batch 4 知识提炼切读消息表 :done, b4, after b3, 1d + Batch 5 关闭已完成待办项 :done, b5, after b4, 1d + Batch 6 应用状态拆分+路径授权 :done, b6, after b5, 1d + + section 跨端与消息 + Batch 7 小程序配置持久化 :done, b7, after b6, 1d + Batch 8 消息分页懒加载 :done, b8, after b7, 1d + Batch 9 多工程数据层 :done, b9, after b8, 1d + + section 文件浏览器 + Batch 10 文件浏览器 UI :done, b10, after b9, 1d + Batch 11 Git 只读 AI 工具 :done, b11, after b10, 1d + Batch 12 Git 写操作 AI 工具 :done, b12, after b11, 1d + Batch 13 文件浏览器增强 :done, b13, after b12, 1d + + section 收尾与重构 + Batch 14 浏览器收尾 :done, b14, after b13, 1d + Batch 15 多工程管理 :done, b15, after b14, 1d + Batch 16 提交历史作者 :done, b16, after b15, 1d + + section 崩溃修复与 UX + 渲染崩溃修复 TDZ+窗口权限 :done, fix1, after b16, 1d + Batch 17 Git 能力闭环 :done, b17, after fix1, 1d + Batch 18 小程序审批超时 :done, b18, after b17, 1d + + section UX 与架构 + Batch 19 任务管理 UX 重构 :done, b19, after b18, 1d + Batch 20 coordinator.rs 删除 :done, b20, after b19, 1d + 任务快捷操作菜单 :done, menu1, after b20, 1d + + section DAG 与依赖图 + TDZ 防御 :done, tdz1, after menu1, 1d + Plan 执行器骨架 :done, plan1, after tdz1, 1d + Batch 25 工程依赖图数据层 :done, b25, after plan1, 1d + + section 增强 + Batch 26 dagre 布局 :done, b26, after b25, 1d + Batch 28 架构债收尾 :done, b28, after b26, 1d + Batch 29 环检测+PNG+总数 :done, b29, after b28, 1d + + section 待推进 + Batch 30 代码质量收尾 :b30, after b29, 3d + Batch 31 MessageList 拆分 :b31, after b30, 3d + Batch 32 ChatInput 拆分 :b32, after b31, 3d + Batch 33 多 ReAct 对接 :b33, after b32, 5d + Batch 34 产品完善 :b34, after b33, 3d +``` + +--- + +## 二、功能领域分布 + +```mermaid +pie title 批次功能领域分布(按完成批次数) + "文件浏览器/Git" : 8 + "任务管理 UX" : 3 + "工程系统" : 4 + "AI/Agent" : 4 + "架构债/重构" : 4 + "小程序" : 2 + "Bug 修复" : 3 + "依赖图可视化" : 4 +``` + +--- + +## 三、批次依赖关系图 + +> 仅展示批次级别依赖,子任务明细见下方表格。 + +```mermaid +flowchart TD + B6["B6 状态拆分"] --> B9["B9 多工程数据层"] + B9 --> B10["B10 文件浏览器"] + B9 --> B25["B25 依赖图数据"] + B10 --> B13["B13 浏览器增强"] + B13 --> B14["B14 收尾"] + B13 --> B15["B15 多工程管理"] + B13 --> B16["B16 提交作者"] + B13 --> B17["B17 Git 闭环"] + B25 --> B26["B26 dagre布局"] + B26 --> B29["B29 环检测+PNG"] + B9 --> B11["B11 Git只读"] + B11 --> B12["B12 Git写操作"] + PLAN["Plan执行器"] --> B33["B33 多ReAct"] + B30["B30 代码质量"] --> B31["B31 MsgList拆分"] + B31 --> B32["B32 ChatInput拆分"] + + style B6 fill:#2d5a2d,color:#fff + style B9 fill:#2d5a2d,color:#fff + style B10 fill:#2d5a2d,color:#fff + style B11 fill:#2d5a2d,color:#fff + style B12 fill:#2d5a2d,color:#fff + style B13 fill:#2d5a2d,color:#fff + style B14 fill:#2d5a2d,color:#fff + style B15 fill:#2d5a2d,color:#fff + style B16 fill:#2d5a2d,color:#fff + style B17 fill:#2d5a2d,color:#fff + style B25 fill:#2d5a2d,color:#fff + style B26 fill:#2d5a2d,color:#fff + style B29 fill:#2d5a2d,color:#fff + style PLAN fill:#2d5a2d,color:#fff + style B30 fill:#5a4a2d,color:#fff + style B31 fill:#5a4a2d,color:#fff + style B32 fill:#5a4a2d,color:#fff + style B33 fill:#5a4a2d,color:#fff +``` + +绿色 = 已完成 | 橙色 = 待推进 + +--- + +## 四、批次子任务明细 + +| 批次 | 状态 | 子任务 | +|------|------|--------| +| Batch 9 | ✅ | project_modules 表 · ModuleRepo CRUD · 创建项目适配 · AI 工具注册 | +| Batch 10 | ✅ | 文件树组件 · 文件预览 · Git 状态标记 · 面包屑导航 | +| Batch 13 | ✅ | 行号/图标 · Diff 视图 · Git 变更面板 · 窗口分离 · 分页提交历史 | +| Batch 17 | ✅ | Diff 行号解析 · 分支只读展示 · 提交详情 · 写入后自动刷新 | +| Batch 19 | ✅ | 搜索框 · 筛选改下拉 · 排序选择器 · 分组可折叠 · 快捷操作菜单 | +| Batch 25 | ✅ | module_dependencies 表 · DependencyRepo CRUD · 依赖边渲染 · 小地图 | +| Batch 26 | ✅ | dagre 布局 · 添加依赖弹窗 · 边类型着色 | +| Batch 29 | ✅ | 环形检测 IPC · PNG 导出 · count_tasks IPC · 真实 total | +| Batch 30 | ⏸️ | 搜索索引核验 · 编译警告清理 · i18n 核验 · String 替 newtype | +| Batch 31 | ⏸️ | 提取 MessageItem.vue · 提取 useMessageScroll.ts | +| Batch 32 | ⏸️ | 提取 SkillMention.vue · 提取 ImageInput.vue | +| Batch 33 | ⏸️ | PlanExecutor 接入 loop · plan_hint 生成 DAG · 执行进度展示 | + +--- + +## 四、关键指标 + +| 指标 | 数值 | +|------|------| +| 已完成批次 | 29 | +| 待推进批次 | 5 | +| 总提交数(Batch 1-29) | ~35 | +| 涉及 Rust 文件 | ~25 | +| 涉及 Vue/TS 文件 | ~40 | +| 新增数据库迁移版本 | V35 | +| 新增 IPC 命令 | ~15 | +| 新增 npm 依赖 | @antv/x6 / dagre / highlight.js | +| 当前最大文件 | MessageList.vue(1552行,待拆分) | + +--- + +## 五、待推进批次优先级矩阵 + +| 批次 | 价值 | 风险 | 象限 | 建议 | +|------|------|------|------|------| +| Batch 30 代码质量 | 中 | 低 | 高价值低风险 | ✅ 优先做 | +| Batch 31 MessageList 拆分 | 高 | 高 | 高价值高风险 | ⚠️ 需谨慎,要测试覆盖 | +| Batch 32 ChatInput 拆分 | 高 | 高 | 高价值高风险 | ⚠️ 需谨慎,要测试覆盖 | +| Batch 33 多 ReAct 对接 | 很高 | 很高 | 高价值高风险 | ⚠️ 核心架构改动 | +| Batch 34 产品完善 | 中 | 中 | 中价值中风险 | 🟡 可延后 | + +--- + +## 六、批次提交密度 + +```mermaid +xychart-beta + title "批次提交密度(按阶段)" + x-axis ["阶段1", "阶段2", "阶段3", "阶段4", "阶段5", "阶段6", "阶段7", "阶段8", "阶段9"] + y-axis "批次数" 0 --> 8 + bar [3, 3, 2, 3, 3, 3, 3, 3, 3] +``` diff --git a/src/api/types.ts b/src/api/types.ts index e49370a..0e6c68d 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -1,5 +1,20 @@ //! TypeScript 类型定义 — 与 Rust Record 结构体严格对齐 +// ============================================================ +// 域 ID newtype(纯类型级标记,零运行时开销) +// ============================================================ + +/** 项目 ID — branded newtype,替代 string */ +export type ProjectId = string & { readonly __brand: 'ProjectId' } +/** 任务 ID — branded newtype,替代 string */ +export type TaskId = string & { readonly __brand: 'TaskId' } +/** 对话 ID — branded newtype,替代 string */ +export type ConvId = string & { readonly __brand: 'ConvId' } +/** 工程/模块 ID — branded newtype,替代 string */ +export type ModuleId = string & { readonly __brand: 'ModuleId' } +/** 消息 ID — branded newtype,替代 string */ +export type MessageId = string & { readonly __brand: 'MessageId' } + // ============================================================ // 灵感 // ============================================================ @@ -78,7 +93,7 @@ export type ProjectStatus = | 'planning' | 'in_progress' | 'testing' | 'releasing' | 'completed' | 'paused' | 'cancelled' export interface ProjectRecord { - id: string + id: ProjectId name: string description: string status: ProjectStatus @@ -116,7 +131,7 @@ export interface AiScanResult { /** 灵感晋升结果(后端 promote_idea 返回) */ export interface PromotionResult { idea_id: string - project_id: string + project_id: ProjectId promoted: boolean reason: string } @@ -133,8 +148,8 @@ export type TaskStatus = | 'todo' | 'in_progress' | 'in_review' | 'testing' | 'done' | 'blocked' | 'cancelled' export interface TaskRecord { - id: string - project_id: string + id: TaskId + project_id: ProjectId title: string description: string status: TaskStatus @@ -159,7 +174,7 @@ export interface TaskRecord { } export interface CreateTaskInput { - project_id: string + project_id: ProjectId title: string description?: string priority?: number @@ -423,7 +438,7 @@ export type AiChatEvent = ({ // 上下文已清空:前端刷新消息列表 + toast 提示 type: 'AiContextCleared' }) & { - conversation_id?: string + conversation_id?: ConvId } /** @@ -447,7 +462,7 @@ export type ContentPart = /** AI 消息(前端渲染用,isError 标记错误消息用于差异化样式) */ export interface AiMessage { - id: string + id: MessageId role: 'user' | 'assistant' | 'tool' | 'system' content: string isError?: boolean @@ -581,7 +596,7 @@ export interface AiToolCallInfo { /** 对话列表摘要 */ export interface AiConversationSummary { - id: string + id: ConvId title: string | null provider_id: string | null model: string | null @@ -620,7 +635,7 @@ export interface HumanApprovalResponse { /** 切换对话返回(含 messages JSON) */ export interface AiConversationDetail { - id: string + id: ConvId title: string | null messages: string // JSON string of ChatMessage[] /** 生成中返回 true(后端 ai_conversation_switch 在 generating 时置),前端据此禁编辑(FR-C5) */ diff --git a/src/components/settings/searchIndex.ts b/src/components/settings/searchIndex.ts index c9d2240..019a676 100644 --- a/src/components/settings/searchIndex.ts +++ b/src/components/settings/searchIndex.ts @@ -85,6 +85,8 @@ export const SETTINGS_INDEX: SettingIndexEntry[] = [ // ===== 高级 ===== // F-#97:autoExecute(boolean) → autoExecuteMode(low/medium/all 三档),key 同步更名 + // dataDir(应用数据目录):AdvancedSection.vue 渲染行,此前漏入索引 + { category: 'advanced', key: 'dataDir', labelKey: 'settings.labelDataDir', descKey: 'settings.descDataDir' }, { category: 'advanced', key: 'autoExecuteMode', labelKey: 'settings.labelAutoExecute', descKey: 'settings.descAutoExecute' }, { category: 'advanced', key: 'logLevel', labelKey: 'settings.labelLogLevel', descKey: 'settings.descLogLevel' }, { category: 'advanced', key: 'approvalTimeout', labelKey: 'settings.labelApprovalTimeout', descKey: 'settings.descApprovalTimeout' }, diff --git a/src/composables/ai/aiShared.ts b/src/composables/ai/aiShared.ts index 467787a..1a2d831 100644 --- a/src/composables/ai/aiShared.ts +++ b/src/composables/ai/aiShared.ts @@ -14,7 +14,7 @@ import { reactive } from 'vue' import { aiApi } from '@/api' import { useAppSettingsStore } from '@/stores/appSettings' import { t } from '@/i18n/i18n-helpers' -import type { AiMessage, AiToolCallInfo, ConvState } from '@/api/types' +import type { AiMessage, AiToolCallInfo, ConvState, MessageId } from '@/api/types' /** @@ -154,7 +154,7 @@ export function startApprovalTimer( console.error('[AI] 审批超时自动拒绝 IPC 未送达:', e) }) getMessages().push({ - id: `approval-timeout-${nextMsgId()}`, + id: `approval-timeout-${nextMsgId()}` as MessageId, role: 'assistant', content: t('ai.approvalTimeout', { toolName }), isError: true, diff --git a/src/composables/ai/useAiConversations.ts b/src/composables/ai/useAiConversations.ts index 6d238a5..912355b 100644 --- a/src/composables/ai/useAiConversations.ts +++ b/src/composables/ai/useAiConversations.ts @@ -12,7 +12,7 @@ import { persistUiState } from './useAiPanel' import { setStreaming } from './streamingGuard' import { nextMsgId, getConvState, clearConvStreamState, startApprovalTimer } from './aiShared' import { t } from '@/i18n/i18n-helpers' -import type { AiConversationDetail, AiMessage, AiToolCallInfo } from '@/api/types' +import type { AiConversationDetail, AiMessage, AiToolCallInfo, ConvId } from '@/api/types' const appSettings = useAppSettingsStore() @@ -28,7 +28,7 @@ async function loadConversations() { && !state.conversations.some(c => c.id === state.activeConversationId)) { const now = String(Date.now()) state.conversations.unshift({ - id: state.activeConversationId, + id: state.activeConversationId as ConvId, title: null, provider_id: null, model: null, @@ -102,7 +102,7 @@ export async function switchConversation(id: string) { if (mySwitchId !== _latestSwitchId) return void appSettings.set('df-ai-active-conv', created.id) // 用新对话 id 重走后续逻辑 - detail = { id: created.id, title: null, messages: '[]' } + detail = { id: created.id as ConvId, title: null, messages: '[]' } state.messages = [] notifyConversationChanged() void loadConversations() diff --git a/src/composables/ai/useAiEvents.ts b/src/composables/ai/useAiEvents.ts index b8f9ea9..9164bac 100644 --- a/src/composables/ai/useAiEvents.ts +++ b/src/composables/ai/useAiEvents.ts @@ -32,7 +32,7 @@ export { getConvState } from './aiShared' import { resetStreamWatchdog, clearStreamWatchdog, clearAllStreamWatchdogs } from './useAiStream' import { setStreaming } from './streamingGuard' import { loadConversations } from './useAiConversations' -import type { AiChatEvent, AiMessage, AiToolCallInfo } from '@/api/types' +import type { AiChatEvent, AiMessage, AiToolCallInfo, MessageId } from '@/api/types' let _unlistenAiEvent: (() => void) | null = null let _unlistenConvChanged: (() => void) | null = null @@ -283,7 +283,7 @@ function handleStreamingEvent(event: AiChatEvent): boolean { flushCurrentText() state.currentText = '' state.messages.push({ - id: `ai-${nextMsgId()}`, + id: `ai-${nextMsgId()}` as MessageId, role: 'assistant', content: '', timestamp: Date.now(), @@ -313,7 +313,7 @@ function handleStreamingEvent(event: AiChatEvent): boolean { lastMsg.content = retryText } else { state.messages.push({ - id: `err-${nextMsgId()}`, + id: `err-${nextMsgId()}` as MessageId, role: 'assistant', content: retryText, isError: true, @@ -557,7 +557,7 @@ function handleUserMessageEvent(event: AiChatEvent): boolean { return true } state.messages.push({ - id: `user-${nextMsgId()}`, + id: `user-${nextMsgId()}` as MessageId, role: 'user', content: event.message, timestamp: Date.now(), @@ -623,7 +623,7 @@ function handleLifecycleEvent(event: AiChatEvent): boolean { // 注:此系统提示仅前端展示,后端已独立 push 到 session.messages 落库。 if (event.incomplete) { state.messages.push({ - id: `incomplete-${nextMsgId()}`, + id: `incomplete-${nextMsgId()}` as MessageId, role: 'assistant', content: t('ai.responseIncomplete'), timestamp: Date.now(), @@ -667,7 +667,7 @@ function handleLifecycleEvent(event: AiChatEvent): boolean { // AiMessage 类型未含 errorType 字段(不在本批白名单),用对象字面量 + cast 扩展; // 消费方(AiChat.vue canOpenSettings)经同 cast 读取,类型闭环在两端,不污染 types.ts。 state.messages.push({ - id: `err-${nextMsgId()}`, + id: `err-${nextMsgId()}` as MessageId, role: 'assistant', content: friendlyError(event.error), isError: true, diff --git a/src/composables/ai/useAiSend.ts b/src/composables/ai/useAiSend.ts index 6675f8c..8a2031b 100644 --- a/src/composables/ai/useAiSend.ts +++ b/src/composables/ai/useAiSend.ts @@ -19,7 +19,7 @@ import { invoke } from '@tauri-apps/api/core' import { ref } from 'vue' import { aiApi } from '@/api' import { state } from '@/stores/ai' -import type { ContentPart, AiMessage, MentionSpan } from '@/api/types' +import type { ContentPart, AiMessage, MentionSpan, MessageId } from '@/api/types' import { t } from '@/i18n/i18n-helpers' import { resetStreamWatchdog, clearStreamWatchdog } from './useAiStream' import { setStreaming } from './streamingGuard' @@ -73,7 +73,7 @@ const modelOverride = ref(null) async function doSend(text: string, skill?: string, force = false, parts?: ContentPart[], spans?: MentionSpan[]) { const userMsgId = `user-${nextMsgId()}` state.messages.push({ - id: userMsgId, + id: userMsgId as MessageId, role: 'user', content: text.trim(), // 仅在非空时挂 parts(纯文本消息保持 undefined,渲染走原 content 路径零回归) @@ -85,7 +85,7 @@ async function doSend(text: string, skill?: string, force = false, parts?: Conte const aiMsgId = `ai-${nextMsgId()}` state.messages.push({ - id: aiMsgId, + id: aiMsgId as MessageId, role: 'assistant', content: '', timestamp: Date.now(), @@ -141,7 +141,7 @@ async function regenerate() { state.messages.pop() const aiMsgId = `ai-${nextMsgId()}` state.messages.push({ - id: aiMsgId, + id: aiMsgId as MessageId, role: 'assistant', content: '', timestamp: Date.now(), @@ -211,7 +211,7 @@ async function editMessage(newMessage: string) { // push 空气泡占位(新 AI 回复将流入) const aiMsgId = `ai-${nextMsgId()}` state.messages.push({ - id: aiMsgId, + id: aiMsgId as MessageId, role: 'assistant', content: '', timestamp: Date.now(), diff --git a/src/composables/ai/useAiStream.ts b/src/composables/ai/useAiStream.ts index 7ce3b65..3379a14 100644 --- a/src/composables/ai/useAiStream.ts +++ b/src/composables/ai/useAiStream.ts @@ -14,6 +14,7 @@ import { state } from '@/stores/ai' import { t } from '@/i18n/i18n-helpers' import { nextMsgId, getConvState } from './aiShared' +import type { MessageId } from '@/api/types' import { forceResetStreaming } from './streamingGuard' import { emit } from '@tauri-apps/api/event' @@ -124,7 +125,7 @@ export function onStreamTimeout(convId?: string) { ? t('ai.streamInterruptedAfterTool') : t('ai.streamInterrupted') state.messages.push({ - id: `timeout-${nextMsgId()}`, + id: `timeout-${nextMsgId()}` as MessageId, role: 'assistant', content, isError: true, diff --git a/src/composables/ai/useAiWindow.ts b/src/composables/ai/useAiWindow.ts index b5cdc70..1708bd0 100644 --- a/src/composables/ai/useAiWindow.ts +++ b/src/composables/ai/useAiWindow.ts @@ -11,6 +11,7 @@ import { invoke } from '@tauri-apps/api/core' import { state } from '@/stores/ai' import { nextMsgId, getConvState, convStates } from './aiShared' +import type { MessageId } from '@/api/types' import { setStreaming } from './streamingGuard' import { persistUiState } from './useAiPanel' @@ -194,7 +195,7 @@ export async function restoreGeneratingState(opts?: { fromMainPanel?: boolean; c state.currentText = (opts?.convId && localStorage.getItem(textKey(opts.convId))) || localStorage.getItem('df-ai-text') || '' } state.messages.push({ - id: `ai-${nextMsgId()}`, + id: `ai-${nextMsgId()}` as MessageId, role: 'assistant', content: '', timestamp: Date.now(), diff --git a/src/i18n/zh-CN/fileExplorer.ts b/src/i18n/zh-CN/fileExplorer.ts index e59bcb4..f50c146 100644 --- a/src/i18n/zh-CN/fileExplorer.ts +++ b/src/i18n/zh-CN/fileExplorer.ts @@ -33,7 +33,5 @@ export default { removeModule: '删除工程', removeConfirm: '确定删除工程「{name}」吗?此操作不可撤销。', scanSubmodules: '扫描子仓库', - // Git Changes tab - loadMore: '加载更多', }, } diff --git a/src/stores/project/tasks.ts b/src/stores/project/tasks.ts index fb907be..a50aa36 100644 --- a/src/stores/project/tasks.ts +++ b/src/stores/project/tasks.ts @@ -1,5 +1,5 @@ import { taskApi } from '@/api' -import type { TaskQuery } from '@/api/types' +import type { TaskQuery, ProjectId } from '@/api/types' import { t } from '@/i18n/i18n-helpers' import { runWithCatch } from '@/composables/useStoreAction' import { state } from './state' @@ -22,7 +22,7 @@ export function createTasksStore() { }) } - async function createTask(input: { project_id: string; title: string; description?: string; priority?: number; branch_name?: string; assignee?: string; idea_id?: string }) { + async function createTask(input: { project_id: ProjectId; title: string; description?: string; priority?: number; branch_name?: string; assignee?: string; idea_id?: string }) { const record = await runWithCatch(state, t('tasks.err.createFailed'), async () => { const r = await taskApi.create(input) state.tasks.push(r) diff --git a/src/views/ProjectDetail.vue b/src/views/ProjectDetail.vue index 297ced8..e4cb0a2 100644 --- a/src/views/ProjectDetail.vue +++ b/src/views/ProjectDetail.vue @@ -274,6 +274,7 @@ import FileExplorer from '@/components/project/FileExplorer.vue' import DependencyGraph from '@/components/project/DependencyGraph.vue' import { useConfirm } from '@/composables/useConfirm' import { useRendered } from '@/composables/useMarkdown' +import type { ProjectId } from '@/api/types' const route = useRoute() const router = useRouter() @@ -406,7 +407,7 @@ async function submitNewTask() { submitting.value = true try { const r = await store.createTask({ - project_id: projectId.value, + project_id: projectId.value as ProjectId, title: newTaskTitle.value.trim(), description: newTaskDesc.value.trim(), branch_name: newTaskBranch.value.trim() || undefined, diff --git a/src/views/Tasks.vue b/src/views/Tasks.vue index 865d812..1d8c0ab 100644 --- a/src/views/Tasks.vue +++ b/src/views/Tasks.vue @@ -166,7 +166,7 @@ import { useProjectStore } from '@/stores/project' import { formatRelative } from '@/utils/time' import { taskStatusLabel as statusLabel, taskStatusClass, priorityLabel, priorityClass } from '../constants/project' import { taskApi } from '@/api' -import type { TaskRecord, TaskQuery } from '@/api/types' +import type { TaskRecord, TaskQuery, ProjectId } from '@/api/types' import Paginator from '../components/Paginator.vue' const router = useRouter() @@ -368,7 +368,7 @@ async function confirmCreate() { submitting.value = true try { const r = await store.createTask({ - project_id: newTaskProjectId.value, + project_id: newTaskProjectId.value as ProjectId, title: newTaskTitle.value.trim(), description: newTaskDesc.value.trim(), branch_name: newTaskBranch.value.trim() || undefined,