重构: 前端God文件拆分(StreamRenderer+EmptyState+MentionPopover+EnrichmentPanel)

- 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: 进度条文案加已完成工具数(中英文)
This commit is contained in:
lxy
2026-07-02 10:11:15 +08:00
parent 54ffc868b2
commit acf0ed091d
12 changed files with 728 additions and 463 deletions
+144
View File
@@ -0,0 +1,144 @@
<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>