重构: 审批交互重设计

This commit is contained in:
lxy
2026-10-05 14:59:12 +08:00
parent 24d6803ecc
commit 2eea0118db
7 changed files with 254 additions and 30 deletions
+55 -23
View File
@@ -135,52 +135,59 @@
</div>
<!-- 目标计数 -->
<div v-if="goals.length" class="ai-goals-inline">
<span class="ai-goals-inline-badge ai-tool-badge" @click="togglePanel('goals')" :title="$t('aiChat.viewGoals')">
<span class="ai-goals-inline-badge ai-tool-badge" :class="{ 'is-active': goalsExpanded }" @click="togglePanel('goals')" :title="$t('aiChat.viewGoals')">
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><circle cx="12" cy="12" r="6"/><circle cx="12" cy="12" r="2"/></svg>
{{ goals.filter(g => g.status === 'active').length }}/{{ goals.length }}
</span>
<div v-if="goalsExpanded" class="ai-goals-inline-list">
<div v-for="(g, i) in goals" :key="i" class="ai-goal-inline-item" :class="{ 'ai-goal-completed': g.status === 'completed' }">
<span v-if="g.status === 'active'" class="ai-goal-dot"></span>
<span v-else class="ai-goal-check">✓</span>
<span class="ai-goal-text">{{ g.text }}</span>
<button class="ai-goal-remove" @click.stop="removeGoal(i)">×</button>
<Transition name="ai-pop">
<div v-if="goalsExpanded" class="ai-goals-inline-list">
<div v-for="(g, i) in goals" :key="i" class="ai-goal-inline-item" :class="{ 'ai-goal-completed': g.status === 'completed' }">
<span v-if="g.status === 'active'" class="ai-goal-dot"></span>
<span v-else class="ai-goal-check">✓</span>
<span class="ai-goal-text">{{ g.text }}</span>
<button class="ai-goal-remove" @click.stop="removeGoal(i)">×</button>
</div>
</div>
</div>
</Transition>
</div>
<!-- 历史消息 -->
<div v-if="historyMsgs.length" class="ai-history-inline">
<span class="ai-history-inline-badge ai-tool-badge" @click="togglePanel('history')" :title="$t('aiChat.viewHistory')">
<span class="ai-history-inline-badge ai-tool-badge" :class="{ 'is-active': historyExpanded }" @click="togglePanel('history')" :title="$t('aiChat.viewHistory')">
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/></svg>
{{ historyMsgs.length }}
</span>
<div v-if="historyExpanded" class="ai-history-inline-list">
<div v-for="h in historyMsgs" :key="h.id" class="ai-history-inline-item" @click="scrollToMessage(h.id)">
<span class="ai-history-index">{{ h.num }}</span>
<span class="ai-history-text">{{ h.text }}</span>
<span class="ai-history-time">{{ h.time }}</span>
<Transition name="ai-pop">
<div v-if="historyExpanded" class="ai-history-inline-list">
<div v-for="h in historyMsgs" :key="h.id" class="ai-history-inline-item" @click="scrollToMessage(h.id)">
<span class="ai-history-index">{{ h.num }}</span>
<span class="ai-history-text">{{ h.text }}</span>
<span class="ai-history-time">{{ h.time }}</span>
</div>
</div>
</div>
</Transition>
</div>
<!-- 上下文摘要 -->
<div v-if="summaryMsgs.length" class="ai-summary-inline">
<span class="ai-summary-inline-badge ai-tool-badge" @click="togglePanel('summary')" :title="$t('aiChat.viewSummary')">
<span class="ai-summary-inline-badge ai-tool-badge" :class="{ 'is-active': summaryExpanded }" @click="togglePanel('summary')" :title="$t('aiChat.viewSummary')">
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 6h16M4 12h16M4 18h12"/></svg>
{{ summaryMsgs.length }}
</span>
<div v-if="summaryExpanded" class="ai-summary-inline-list">
<div v-for="(s, i) in summaryMsgs" :key="i" class="ai-summary-inline-item" @click="scrollToMessage(s.id)">
<span class="ai-summary-label">{{ $t('aiChat.compressedSummaryLabel') }} #{{ summaryMsgs.length - i }}</span>
<p class="ai-summary-preview">{{ s.text }}</p>
<Transition name="ai-pop">
<div v-if="summaryExpanded" class="ai-summary-inline-list">
<div v-for="(s, i) in summaryMsgs" :key="i" class="ai-summary-inline-item" @click="scrollToMessage(s.id)">
<span class="ai-summary-label">{{ $t('aiChat.compressedSummaryLabel') }} #{{ summaryMsgs.length - i }}</span>
<p class="ai-summary-preview">{{ s.text }}</p>
</div>
</div>
</div>
</Transition>
</div>
<!-- L3 完整上下文 -->
<div v-if="contextInfo.goals.length || contextInfo.summaryCount > 0" class="ai-context-inline">
<span class="ai-context-inline-badge ai-tool-badge" @click="togglePanel('context')" :title="$t('aiChat.viewContext')">
<span class="ai-context-inline-badge ai-tool-badge" :class="{ 'is-active': contextExpanded }" @click="togglePanel('context')" :title="$t('aiChat.viewContext')">
<svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4"/><circle cx="12" cy="8" r="1" fill="currentColor"/></svg>
</span>
<div v-if="contextExpanded" class="ai-context-inline-list">
<Transition name="ai-pop">
<div v-if="contextExpanded" class="ai-context-inline-list">
<!-- 系统提示 -->
<div class="ai-context-section">
<div class="ai-context-section-title">{{ $t('aiChat.contextSystemPrompt') }}</div>
@@ -210,6 +217,7 @@
</div>
</div>
</div>
</Transition>
</div>
</div>
</div>
@@ -1130,4 +1138,28 @@ onBeforeUnmount(() => {
.ai-provider-switch-leave-to { opacity: 0; max-height: 0; padding-top: 0; padding-bottom: 0; }
.ai-provider-switch-enter-to,
.ai-provider-switch-leave-from { opacity: 1; max-height: 32px; }
/* ═══ 底部四面板 popover 过渡(ux-9d:平滑出入动画,对齐 more-menu popout) ═══ */
.ai-pop-enter-active { transition: opacity 0.16s var(--df-ease), transform 0.16s var(--df-ease); }
.ai-pop-leave-active { transition: opacity 0.12s var(--df-ease), transform 0.12s var(--df-ease); }
.ai-pop-enter-from,
.ai-pop-leave-to { opacity: 0; transform: translateY(-4px) scale(0.98); }
/* 面板激活态徽章(accent 描边,当前展开的面板入口可见) */
.ai-tool-badge.is-active {
color: var(--df-accent);
border-color: color-mix(in srgb, var(--df-accent) 45%, transparent);
background: var(--df-accent-bg);
}
/* goals 面板补 max-height 滚动(原无上限,目标多时无限拉高遮对话) */
.ai-goals-inline-list {
max-height: 300px;
overflow-y: auto;
}
/* context 面板补 max-height 滚动(原无上限,增强上下文多时同理) */
.ai-context-inline-list {
max-height: 360px;
overflow-y: auto;
}
</style>