新增: AI Chat @灵感提及(ChatInput mention idea组)
- ChatInput.vue: MentionItem type加idea + mentionItems加idea过滤(title/description projectsStore.ideas) + mentionGroups加idea组 + selectMention插入[灵感: title] - i18n zh/en: mentionGroupIdea 灵感走store同步computed(同project/task模式 无IPC)
This commit is contained in:
@@ -119,7 +119,7 @@ import { ref, computed, nextTick, watch } from 'vue'
|
|||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
import { useAiStore } from '../../stores/ai'
|
import { useAiStore } from '../../stores/ai'
|
||||||
import { useProjectStore } from '../../stores/project'
|
import { useProjectStore } from '../../stores/project'
|
||||||
import type { AiMessage, ContentPart, SkillInfo, ProjectRecord, TaskRecord } from '../../api/types'
|
import type { AiMessage, ContentPart, SkillInfo, ProjectRecord, TaskRecord, IdeaRecord } from '../../api/types'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
/** UX-09: 编辑末条 user 消息态;父持有,handleSend 据此走 editMessage 而非 sendMessage */
|
/** UX-09: 编辑末条 user 消息态;父持有,handleSend 据此走 editMessage 而非 sendMessage */
|
||||||
@@ -324,7 +324,7 @@ function clearSkill() {
|
|||||||
// 触发:输入框光标前最近的 @ 后无空白/换行 → 激活;Esc/光标移开/选中插入 → 关闭。
|
// 触发:输入框光标前最近的 @ 后无空白/换行 → 激活;Esc/光标移开/选中插入 → 关闭。
|
||||||
// Enter 不冲突:popover 开时 Enter 插入选中(覆盖发送),关时 Enter 发送(原逻辑)。
|
// Enter 不冲突:popover 开时 Enter 插入选中(覆盖发送),关时 Enter 发送(原逻辑)。
|
||||||
interface MentionItem {
|
interface MentionItem {
|
||||||
type: 'project' | 'task'
|
type: 'project' | 'task' | 'idea'
|
||||||
/** 扁平后唯一 key(用于 key/导航扁平索引定位) */
|
/** 扁平后唯一 key(用于 key/导航扁平索引定位) */
|
||||||
uid: string
|
uid: string
|
||||||
/** 实体原始 id(供后端解析,目前仅插入 [类型:名] 文本,id 未透传) */
|
/** 实体原始 id(供后端解析,目前仅插入 [类型:名] 文本,id 未透传) */
|
||||||
@@ -342,6 +342,7 @@ const mentionStart = ref(-1)
|
|||||||
const mentionGroupLabel = computed(() => ({
|
const mentionGroupLabel = computed(() => ({
|
||||||
project: t('aiChat.mentionGroupProject'),
|
project: t('aiChat.mentionGroupProject'),
|
||||||
task: t('aiChat.mentionGroupTask'),
|
task: t('aiChat.mentionGroupTask'),
|
||||||
|
idea: t('aiChat.mentionGroupIdea'),
|
||||||
}))
|
}))
|
||||||
|
|
||||||
/** 当前 @ 查询文本(@ 之后的串,不含 @) */
|
/** 当前 @ 查询文本(@ 之后的串,不含 @) */
|
||||||
@@ -378,19 +379,33 @@ const mentionItems = computed<MentionItem[]>(() => {
|
|||||||
desc: tk.description || '',
|
desc: tk.description || '',
|
||||||
typeLabel: t('aiChat.mentionGroupTask'),
|
typeLabel: t('aiChat.mentionGroupTask'),
|
||||||
}))
|
}))
|
||||||
return [...projectItems, ...taskItems]
|
const ideas = (projectsStore.ideas || []) as IdeaRecord[]
|
||||||
|
const ideaItems: MentionItem[] = ideas
|
||||||
|
.filter(it => match(it.title) || match(it.description || ''))
|
||||||
|
.slice(0, 20)
|
||||||
|
.map(it => ({
|
||||||
|
type: 'idea' as const,
|
||||||
|
uid: 'i-' + it.id,
|
||||||
|
id: it.id,
|
||||||
|
name: it.title,
|
||||||
|
desc: it.description || '',
|
||||||
|
typeLabel: t('aiChat.mentionGroupIdea'),
|
||||||
|
}))
|
||||||
|
return [...projectItems, ...taskItems, ...ideaItems]
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 联想候选总数(未过滤,用于区分"无匹配" vs "空") */
|
/** 联想候选总数(未过滤,用于区分"无匹配" vs "空") */
|
||||||
const mentionTotal = computed(() =>
|
const mentionTotal = computed(() =>
|
||||||
((projectsStore.projects || []) as ProjectRecord[]).length
|
((projectsStore.projects || []) as ProjectRecord[]).length
|
||||||
+ ((projectsStore.tasks || []) as TaskRecord[]).length,
|
+ ((projectsStore.tasks || []) as TaskRecord[]).length
|
||||||
|
+ ((projectsStore.ideas || []) as IdeaRecord[]).length,
|
||||||
)
|
)
|
||||||
|
|
||||||
/** 按类型分组(模板按组渲染组标题) */
|
/** 按类型分组(模板按组渲染组标题) */
|
||||||
const mentionGroups = computed(() => [
|
const mentionGroups = computed(() => [
|
||||||
{ key: 'project', label: mentionGroupLabel.value.project, items: mentionItems.value.filter(i => i.type === 'project') },
|
{ key: 'project', label: mentionGroupLabel.value.project, items: mentionItems.value.filter(i => i.type === 'project') },
|
||||||
{ key: 'task', label: mentionGroupLabel.value.task, items: mentionItems.value.filter(i => i.type === 'task') },
|
{ key: 'task', label: mentionGroupLabel.value.task, items: mentionItems.value.filter(i => i.type === 'task') },
|
||||||
|
{ key: 'idea', label: mentionGroupLabel.value.idea, items: mentionItems.value.filter(i => i.type === 'idea') },
|
||||||
])
|
])
|
||||||
|
|
||||||
/** 扁平下标(组内 item 在扁平列表中的位置,供键盘导航 ↑↓/高亮) */
|
/** 扁平下标(组内 item 在扁平列表中的位置,供键盘导航 ↑↓/高亮) */
|
||||||
@@ -405,7 +420,9 @@ function selectMention(item: MentionItem) {
|
|||||||
const after = inputText.value.slice(cursorAfter)
|
const after = inputText.value.slice(cursorAfter)
|
||||||
const label = item.type === 'project'
|
const label = item.type === 'project'
|
||||||
? `[${t('aiChat.mentionGroupProject')}: ${item.name}]`
|
? `[${t('aiChat.mentionGroupProject')}: ${item.name}]`
|
||||||
: `[${t('aiChat.mentionGroupTask')}: ${item.name}]`
|
: item.type === 'task'
|
||||||
|
? `[${t('aiChat.mentionGroupTask')}: ${item.name}]`
|
||||||
|
: `[${t('aiChat.mentionGroupIdea')}: ${item.name}]`
|
||||||
inputText.value = before + label + ' ' + after
|
inputText.value = before + label + ' ' + after
|
||||||
mentionOpen.value = false
|
mentionOpen.value = false
|
||||||
mentionStart.value = -1
|
mentionStart.value = -1
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export default {
|
|||||||
mentionHint: "{'@'} to mention project/task",
|
mentionHint: "{'@'} to mention project/task",
|
||||||
mentionGroupProject: 'Projects',
|
mentionGroupProject: 'Projects',
|
||||||
mentionGroupTask: 'Tasks',
|
mentionGroupTask: 'Tasks',
|
||||||
|
mentionGroupIdea: 'Ideas',
|
||||||
mentionNoMatch: 'No matching entity',
|
mentionNoMatch: 'No matching entity',
|
||||||
mentionEmpty: 'No project/task to mention',
|
mentionEmpty: 'No project/task to mention',
|
||||||
|
|
||||||
|
|||||||
@@ -90,6 +90,7 @@ export default {
|
|||||||
mentionHint: "{'@'} 引用项目/任务",
|
mentionHint: "{'@'} 引用项目/任务",
|
||||||
mentionGroupProject: '项目',
|
mentionGroupProject: '项目',
|
||||||
mentionGroupTask: '任务',
|
mentionGroupTask: '任务',
|
||||||
|
mentionGroupIdea: '灵感',
|
||||||
mentionNoMatch: '无匹配实体',
|
mentionNoMatch: '无匹配实体',
|
||||||
mentionEmpty: '暂无可引用的项目/任务',
|
mentionEmpty: '暂无可引用的项目/任务',
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user