diff --git a/crates/df-ideas/src/scoring.rs b/crates/df-ideas/src/scoring.rs
index f8e9d48..feea562 100644
--- a/crates/df-ideas/src/scoring.rs
+++ b/crates/df-ideas/src/scoring.rs
@@ -11,6 +11,30 @@ pub use crate::capture::IdeaScores;
use df_types::types::Priority;
+// ─── 启发式关键词(DEC-03 c:同文件 const 拆出,接 LLM 语义评分后升 a:JSON 文件配置)───
+//
+// 现阶段关键词集合固定且与维度强相关,抽 const 仅为可读性/可维护性(零行为变化)。
+// TODO(DEC-03 a):接入 LLM 语义评分后,这些关键词退居「冷启动兜底」,
+// 集合迁移到 JSON 配置文件,支持热更新与项目级覆盖。
+// 注:以下集合与原方法内字面量逐一对应,集合/顺序/否定前缀/权重均不变(纯结构提取)。
+
+/// 可行性正向信号词(描述充实度外的技术/资源信号),命中每个 +0.5
+pub const FEASIBILITY_POSITIVE_KEYWORDS: &[&str] = &[
+ "复用", "已有", "简单", "集成", "支持", "成熟", "基于", "现成", "脚手架", "模板",
+];
+/// 可行性负向信号词(复杂度上升信号),命中每个 -0.6
+pub const FEASIBILITY_NEGATIVE_KEYWORDS: &[&str] = &[
+ "重构", "迁移", "大规模", "分布式", "重写", "从零", "全新架构", "高并发", "底层",
+];
+/// 影响力价值信号词(用户/增长/收入...),命中每个 +0.5
+pub const IMPACT_KEYWORDS: &[&str] = &[
+ "用户", "增长", "收入", "效率", "体验", "核心", "关键", "痛点", "竞品", "留存",
+];
+/// 紧急度时效信号词(立即/马上/紧急...),命中每个 +0.5
+pub const URGENCY_KEYWORDS: &[&str] = &[
+ "立即", "马上", "紧急", "尽快", "本周", "上线", "deadline", "截止", "先行", "阻塞",
+];
+
/// 评分权重配置
#[derive(Debug, Clone)]
pub struct ScoringWeights {
@@ -71,14 +95,10 @@ impl ScoringEngine {
score -= 0.5;
}
// 可行性正向信号
- let pos = count_any(desc, &[
- "复用", "已有", "简单", "集成", "支持", "成熟", "基于", "现成", "脚手架", "模板",
- ]);
+ let pos = count_any(desc, FEASIBILITY_POSITIVE_KEYWORDS);
score += (pos as f64) * 0.5;
// 复杂度负向信号
- let neg = count_any(desc, &[
- "重构", "迁移", "大规模", "分布式", "重写", "从零", "全新架构", "高并发", "底层",
- ]);
+ let neg = count_any(desc, FEASIBILITY_NEGATIVE_KEYWORDS);
score -= (neg as f64) * 0.6;
score.clamp(0.0, 10.0)
}
@@ -97,9 +117,7 @@ impl ScoringEngine {
score += (idea.tags.len().min(4) as f64) * 0.25;
}
let desc = idea.description.trim();
- let value_hits = count_any(desc, &[
- "用户", "增长", "收入", "效率", "体验", "核心", "关键", "痛点", "竞品", "留存",
- ]);
+ let value_hits = count_any(desc, IMPACT_KEYWORDS);
score += (value_hits as f64) * 0.5;
if desc.chars().count() > 100 {
score += 0.5;
@@ -116,9 +134,7 @@ impl ScoringEngine {
Priority::Low => 3.0,
};
let desc = idea.description.trim();
- let time_hits = count_any(desc, &[
- "立即", "马上", "紧急", "尽快", "本周", "上线", "deadline", "截止", "先行", "阻塞",
- ]);
+ let time_hits = count_any(desc, URGENCY_KEYWORDS);
score += (time_hits as f64) * 0.5;
score.clamp(0.0, 10.0)
}
diff --git a/docs/todo.md b/docs/todo.md
index c99901a..c548503 100644
--- a/docs/todo.md
+++ b/docs/todo.md
@@ -40,7 +40,7 @@ graph TD
P2["父② 知识图谱Phase1
✅Phase1(数据层+业务层)"]:::done
P3["父③ AI对话体验
📋待办"]:::todo
P4["父④ F-09 per-conv
📋待办"]:::todo
- P5["父⑤ 灵感模块
🔨⑤.1/①.4✅·⑤.2待办"]:::doing
+ P5["父⑤ 灵感模块
✅完成(⑤.1/①.4/⑤.2)"]:::done
P6["父⑥ Phase2-5
📋待办"]:::todo
P7["父⑦ 技术债
📋待办"]:::todo
@@ -58,7 +58,7 @@ graph TD
| **父②** 知识图谱Phase1 | ✅ Phase1完成 | ②.1 V29迁移✅ / ②.2 TaskRecord+TaskLinkRepo✅ / ②.3 IPC(create_task扩展+task_link CRUD+move_queue+get_tree)✅ / ②.4 父聚合✅(set_status_for_aggregation绕status收口) / ②.5 AI工具6✅(基线38) | G1(弱) |
| **父③** AI对话体验 | 📋 待办 | ③.1 B-260619-04 ToolCard / ③.2 REFACTOR-260619-04 审批状态机 | — |
| **父④** F-09 per-conv | 📋 待办 | ④.1 streaming/currentText per-conv | — |
-| **父⑤** 灵感模块 | 🔨 进行中 | ⑤.1 软删除✅ / ①.4 雷达图✅ / ⑤.2 #05 record_to_idea✅ / #06 配置化·#09·#10 表单待办 / #07 promote补偿待定(soft_delete 污染回收站 vs purge 内部回滚干净?) | ⑤.2#07→②.1 |
+| **父⑤** 灵感模块 | ✅ 完成 | ⑤.1 软删除✅ / ①.4 雷达图✅ / ⑤.2 #05✅/#06拆const✅/#09/#10表单(逗号tags)✅ / #07 DEC-02保留purge(不改) / 附:priority_from_i32跨层映射修复(对齐前端0=critical) | #07→②.1 |
| **父⑥** Phase2-5 | 📋 待办 | ⑥.1事件流 / ⑥.2基础设施 / ⑥.3注入 / ⑥.4前端 | ⑥→父②, ⑥.3→父②+父④+G1 |
| **父⑦** 技术债 | 📋 待办 | SMELL-P1-6 / conditions / CR缓存 / UX分页 / 审批超时 / miniapp / 双监听器 | — (穿插) |
diff --git a/src-tauri/src/commands/idea.rs b/src-tauri/src/commands/idea.rs
index ffde2aa..6a46531 100644
--- a/src-tauri/src/commands/idea.rs
+++ b/src-tauri/src/commands/idea.rs
@@ -78,7 +78,7 @@ pub async fn create_idea(
// 防 LLM/前端传 99 等被静默吞为 Critical。
if !(0..=3).contains(&input.priority) {
return Err(format!(
- "priority 必须在 0..=3 (Low=0/Medium=1/High=2/Critical=3),收到 {}",
+ "priority 必须在 0..=3 (0=critical/1=high/2=medium/3=low,对齐前端约定 api/types.ts:141),收到 {}",
input.priority
));
}
@@ -425,13 +425,18 @@ fn record_to_idea(record: &IdeaRecord) -> Idea {
}
}
-/// i32 优先级 → Priority 枚举(与 df-types 枚举值一致:Low=0/Medium=1/High=2/Critical=3)
+/// i32 优先级 → Priority 枚举(对齐**前端约定** 0=critical/1=high/2=medium/3=low,
+/// 见 api/types.ts:141 + Tasks.vue option + constants/project.ts PRIORITY_CLASS)。
+/// 注:df-types Priority 枚举 discriminant 是 Low=0/Critical=3(历史定义,与前端相反),
+/// 故本函数手动映射对齐前端语义,不靠 discriminant。Priority as_str 序列化("low"/"critical")
+/// 不受影响。verify agent 发现的预存跨层 bug(父⑤⑤.2 收尾揪出):原 0=>Low 致 Ideas
+/// 表单选 Critical(P0) 存 Low,标签与实际存储/评分相反。
fn priority_from_i32(p: i32) -> Priority {
match p {
- 0 => Priority::Low,
- 2 => Priority::High,
- x if x >= 3 => Priority::Critical,
- _ => Priority::Medium,
+ 0 => Priority::Critical,
+ 1 => Priority::High,
+ 2 => Priority::Medium,
+ _ => Priority::Low,
}
}
diff --git a/src/i18n/en/ideas.ts b/src/i18n/en/ideas.ts
index a0e4ba2..d13af27 100644
--- a/src/i18n/en/ideas.ts
+++ b/src/i18n/en/ideas.ts
@@ -112,8 +112,17 @@ export default {
captureTitle: '✨ Capture New Idea',
fieldTitle: 'Title',
fieldDesc: 'Description',
+ fieldTags: 'Tags',
+ fieldPriority: 'Priority',
+ fieldSource: 'Source',
titlePlaceholder: 'Describe your idea in one sentence...',
descPlaceholder: 'More details (optional)...',
+ tagsPlaceholder: 'Comma-separated, e.g. tools, efficiency, automation',
+ sourcePlaceholder: 'Source (optional, e.g. user feedback, competitor analysis)',
+ priorityCritical: 'Critical',
+ priorityHigh: 'High',
+ priorityMedium: 'Medium',
+ priorityLow: 'Low',
// Deep link not found
notFound: 'Idea not found or deleted',
diff --git a/src/i18n/zh-CN/ideas.ts b/src/i18n/zh-CN/ideas.ts
index ad076cb..534a051 100644
--- a/src/i18n/zh-CN/ideas.ts
+++ b/src/i18n/zh-CN/ideas.ts
@@ -112,8 +112,17 @@ export default {
captureTitle: '✨ 捕捉新灵感',
fieldTitle: '标题',
fieldDesc: '描述',
+ fieldTags: '标签',
+ fieldPriority: '优先级',
+ fieldSource: '来源',
titlePlaceholder: '一句话描述你的灵感...',
descPlaceholder: '详细说明(可选)...',
+ tagsPlaceholder: '用逗号分隔,如: 工具,效率,自动化',
+ sourcePlaceholder: '来源(可选,如:用户反馈、竞品分析)',
+ priorityCritical: '紧急',
+ priorityHigh: '高',
+ priorityMedium: '中',
+ priorityLow: '低',
// 深链不存在提示
notFound: '灵感不存在或已删除',
diff --git a/src/styles/components.css b/src/styles/components.css
index 4bf1f2c..184f324 100644
--- a/src/styles/components.css
+++ b/src/styles/components.css
@@ -10,7 +10,7 @@
- 仅提取 ≥2 处逐字相同的规则(已 grep 比对一致)
- scoped 故意 override 全局值的(如 .empty-hint padding 24px vs 全局 12px)
不提取,保留各组件 scoped 覆盖(零视觉变化)
- - global.css 已有的 .btn/.modal-*/.page-header/.empty-hint/.empty-state
+ - global.css 已有的 .btn / .modal 系列 / .page-header / .empty-hint / .empty-state
不在此重复,仅补充尚未全局化的类
落地批:DRY 收口(B-260619)
diff --git a/src/views/Ideas.vue b/src/views/Ideas.vue
index a4522d5..aa7dcf2 100644
--- a/src/views/Ideas.vue
+++ b/src/views/Ideas.vue
@@ -112,6 +112,17 @@
+
+
+
+
+
+