Files
DevFlow/src/components/ai/MentionPopover.vue
T
lxy 249b3b9ea8 重构: 跨模块样式DRY收口+审批列表增强+灵感模块优化
- 全局样式收口: page-header/btn/modal/filter-bar/back-link/empty-state
  等通用类从12个视图的scoped重复定义提取到global.css/components.css
- 审批列表增强: 筛选栏(状态/风险/工具搜索)+行展开查看完整参数结果
  +审批耗时显示+刷新spinner+硬编码颜色全部改用CSS变量
- 灵感模块: scoreTier统一评分阈值+score-bar/assessment-badge提取全局
  +statusLabelKey消除重复+i18n状态文案对齐+eval-report--muted统一空态
- Dashboard: df-panel/df-link提取全局(修scoped不渗透)+统计卡可点击跳转
  +活跃项目面板只显示active状态+灵感列表按分数排序+刷新spinner
- 修复: intent.rs tool_type类型对齐(newtype)+ToolCardList补ai-btn定义
  +SkillMention/ImageInput样式不渗透修复(提取全局)
2026-07-02 23:24:11 +08:00

107 lines
3.9 KiB
Vue

<script setup lang="ts">
//! @ 实体引用浮层 — 抽自 ChatInput.vue(零行为变更)。
//!
//! 职责:@ 触发的实体引用 popover(项目/任务/灵感联想候选列表)。
//! 架构复用技能联想 popover 的 .ai-skill-popover 容器样式,仅新增组标题/类型标签。
//!
//! 父组件注入:
//! - mentionItems:联想候选(项目 + 任务 + 灵感,已按查询过滤)
//! - mentionGroups:按类型分组的候选(模板按组渲染组标题)
//! - mentionTotal:未过滤候选总数(区分"无匹配" vs "空")
//! - mentionIndex:当前高亮项在扁平列表中的下标
//! 触发:
//! - select(item):选中实体 → 替换 @query 段为 [类型: 名] 标记
//! - update:mentionIndex:鼠标 hover 时同步高亮下标
import type { MentionItem } from './MentionItem'
defineProps<{
/** 联想候选(扁平,项目 + 任务 + 灵感) */
mentionItems: MentionItem[]
/** 按类型分组的候选(模板按组渲染组标题) */
mentionGroups: { key: string; label: string; items: MentionItem[] }[]
/** 未过滤候选总数(用于区分"无匹配" vs "空") */
mentionTotal: number
/** 当前高亮项在扁平列表中的下标 */
mentionIndex: number
/** 扁平下标查找函数(组内 item 在扁平列表中的位置,供高亮判定) */
mentionFlatIndex: (uid: string) => number
}>()
const emit = defineEmits<{
/** 选中实体 → 替换 @query 段为 [类型: 名] 标记 + 关闭浮层 */
(e: 'select', item: MentionItem): void
/** 鼠标 hover 时同步高亮下标 */
(e: 'update:mentionIndex', index: number): void
}>()
</script>
<template>
<!-- UX-10 §1.4: @ 实体引用浮层(@ 触发,选中插入 [类型:名] 标记,架构复用技能 popover) -->
<div class="ai-skill-popover">
<div v-if="!mentionItems.length" class="ai-skill-empty">
{{ mentionTotal ? $t('aiChat.mentionNoMatch') : $t('aiChat.mentionEmpty') }}
</div>
<div v-else>
<template v-for="g in mentionGroups" :key="g.key">
<div v-if="g.items.length" class="ai-mention-group">{{ g.label }}</div>
<div
v-for="item in g.items"
:key="item.uid"
class="ai-skill-item"
:class="{ 'ai-skill-item--active': mentionFlatIndex(item.uid) === mentionIndex }"
@click="emit('select', item)"
@mouseenter="emit('update:mentionIndex', mentionFlatIndex(item.uid))"
>
<div class="ai-skill-item-row">
<span class="ai-mention-item-type" :class="'ai-mention-item-type--' + item.type">{{ item.typeLabel }}</span>
<span class="ai-mention-item-name">{{ item.name }}</span>
<span class="ai-mention-item-desc">{{ item.desc }}</span>
</div>
</div>
</template>
</div>
</div>
</template>
<style scoped>
/* ── UX-10 §1.4: @ 实体引用浮层特定样式(复用全局 .ai-skill-popover/.ai-skill-item,仅新增 mention 专有) ──
.ai-skill-popover/.ai-skill-item/.ai-skill-empty 已提取到 styles/components.css 全局 */
.ai-mention-group {
padding: 4px 10px 2px;
font-size: 10px;
font-weight: 600;
color: var(--df-text-dim);
text-transform: uppercase;
letter-spacing: 0.5px;
user-select: none;
}
.ai-mention-item-name { color: var(--df-text); font-weight: 500; flex-shrink: 0; }
.ai-mention-item-desc {
color: var(--df-text-dim);
flex: 1;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
/* 类型标签小色块(项目/任务区分) */
.ai-mention-item-type {
flex-shrink: 0;
padding: 1px 5px;
border-radius: var(--df-radius-sm);
font-size: 9px;
font-weight: 600;
letter-spacing: 0.3px;
background: var(--df-bg-hover);
color: var(--df-text-dim);
}
.ai-mention-item-type--project {
background: color-mix(in srgb, var(--df-accent) 16%, transparent);
color: var(--df-accent);
}
.ai-mention-item-type--task {
background: color-mix(in srgb, var(--df-success) 16%, transparent);
color: var(--df-success);
}
</style>