- useStreamRenderer.ts: 流式Markdown块级渲染提取为composable(220行) - EmptyState.vue: 空状态独立组件(无provider引导+示例问题) - MentionPopover.vue: @实体联想浮层独立组件 - EnrichmentPanel.vue: @项目enrichment预览面板独立组件 - MessageList.vue: 1386→1148行, ChatInput.vue: 1155→1007行 - AiChat进度条: completedTools计数器(AiToolCallCompleted+1,AiAgentRound重置) - i18n: 进度条文案加已完成工具数(中英文)
145 lines
4.4 KiB
Vue
145 lines
4.4 KiB
Vue
<script setup lang="ts">
|
||
//! @项目展开摘要面板 — 抽自 ChatInput.vue(零行为变更)。
|
||
//!
|
||
//! 职责:⑥.4 Phase 4 @项目 enrichment 预览(输入区下方 badge + 可展开上下文参考面板)。
|
||
//! 当用户 @[项目:xxx] 后,从 stores 反查项目 + 关联任务/灵感,显式 badge + 可展开面板。
|
||
//!
|
||
//! 父组件注入:
|
||
//! - hasEnrichment:是否有 enrichment 可展示(控制整个面板显隐)
|
||
//! - enrichmentBadgeText:概要 badge 文本(汇总所有 @项目)
|
||
//! - enrichmentDetailText:展开后的正文(mimic 后端 build_augmentation_segment 格式)
|
||
//! - enrichmentExpanded:展开态(v-model 双向)
|
||
//! 触发:
|
||
//! - dismiss:取消 @项目 关联(清 mentionSpan + 移除输入框标记)
|
||
//! - update:enrichmentExpanded:展开/收起切换
|
||
|
||
defineProps<{
|
||
/** 是否有 enrichment 可展示(控制整个面板显隐) */
|
||
hasEnrichment: boolean
|
||
/** 概要 badge 文本(汇总所有 @项目 enrichment) */
|
||
enrichmentBadgeText: string
|
||
/** 展开后的 enrichment 正文(mimic 后端 build_augmentation_segment 格式) */
|
||
enrichmentDetailText: string
|
||
/** 展开态 */
|
||
enrichmentExpanded: boolean
|
||
}>()
|
||
|
||
const emit = defineEmits<{
|
||
/** 取消 @项目 关联:清除 mentionSpan + 移除输入框中的 [@项目: xxx] 标记 */
|
||
(e: 'dismiss'): void
|
||
/** 展开/收起切换 */
|
||
(e: 'update:enrichmentExpanded', expanded: boolean): void
|
||
}>()
|
||
|
||
function toggleExpanded(current: boolean) {
|
||
emit('update:enrichmentExpanded', !current)
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<!-- ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) -->
|
||
<div v-if="hasEnrichment" class="ai-enrichment-bar">
|
||
<button
|
||
class="ai-enrichment-toggle"
|
||
:class="{ 'ai-enrichment-toggle--expanded': enrichmentExpanded }"
|
||
@click="toggleExpanded(enrichmentExpanded)"
|
||
:title="enrichmentExpanded ? $t('aiChat.enrichmentCollapse') : $t('aiChat.enrichmentExpand')"
|
||
:aria-label="enrichmentExpanded ? $t('aiChat.enrichmentCollapse') : $t('aiChat.enrichmentExpand')"
|
||
>
|
||
<svg class="ai-enrichment-chevron" width="10" height="10" viewBox="0 0 10 10" fill="currentColor">
|
||
<path d="M2 3l3 4 3-4" />
|
||
</svg>
|
||
<span class="ai-enrichment-badge">{{ enrichmentBadgeText }}</span>
|
||
</button>
|
||
<button class="ai-enrichment-dismiss" @click="emit('dismiss')" title="取消关联">×</button>
|
||
<div v-if="enrichmentExpanded" class="ai-enrichment-panel">
|
||
<div class="ai-enrichment-label">{{ $t('aiChat.enrichmentLabel') }}</div>
|
||
<pre class="ai-enrichment-body"><code>{{ enrichmentDetailText }}</code></pre>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped>
|
||
/* ── ⑥.4 Phase 4: @项目展开摘要(输入区 enrichment 预览) ── */
|
||
.ai-enrichment-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 6px;
|
||
border: 0.5px solid color-mix(in srgb, var(--df-accent) 30%, transparent);
|
||
border-radius: var(--df-radius);
|
||
overflow: hidden;
|
||
}
|
||
.ai-enrichment-toggle {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
width: 100%;
|
||
padding: 4px 8px;
|
||
border: none;
|
||
background: color-mix(in srgb, var(--df-accent) 10%, transparent);
|
||
color: var(--df-text);
|
||
cursor: pointer;
|
||
font-size: 11px;
|
||
text-align: left;
|
||
transition: background 0.15s;
|
||
}
|
||
.ai-enrichment-toggle:hover {
|
||
background: color-mix(in srgb, var(--df-accent) 18%, transparent);
|
||
}
|
||
.ai-enrichment-toggle--expanded {
|
||
border-bottom: 0.5px solid color-mix(in srgb, var(--df-accent) 20%, transparent);
|
||
}
|
||
.ai-enrichment-chevron {
|
||
flex-shrink: 0;
|
||
color: var(--df-text-dim);
|
||
transition: transform 0.15s;
|
||
}
|
||
.ai-enrichment-toggle--expanded .ai-enrichment-chevron {
|
||
transform: rotate(180deg);
|
||
}
|
||
.ai-enrichment-dismiss {
|
||
flex-shrink: 0;
|
||
border: none;
|
||
background: transparent;
|
||
color: var(--df-text-dim);
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
padding: 0 4px;
|
||
line-height: 1;
|
||
transition: color 0.15s;
|
||
}
|
||
.ai-enrichment-dismiss:hover {
|
||
color: var(--df-danger);
|
||
}
|
||
.ai-enrichment-badge {
|
||
flex: 1;
|
||
min-width: 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
color: var(--df-accent);
|
||
font-weight: 500;
|
||
}
|
||
.ai-enrichment-panel {
|
||
padding: 6px 8px;
|
||
background: var(--df-bg);
|
||
max-height: 200px;
|
||
overflow-y: auto;
|
||
}
|
||
.ai-enrichment-label {
|
||
font-size: 10px;
|
||
color: var(--df-text-dim);
|
||
margin-bottom: 4px;
|
||
font-weight: 600;
|
||
}
|
||
.ai-enrichment-body {
|
||
margin: 0;
|
||
font-size: 11px;
|
||
line-height: 1.6;
|
||
color: var(--df-text);
|
||
white-space: pre-wrap;
|
||
word-break: break-all;
|
||
font-family: var(--df-font-mono, ui-monospace, monospace);
|
||
}
|
||
</style>
|