重构: 前端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:
@@ -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>
|
||||
Reference in New Issue
Block a user