重构: AiChat第三批TopBar + audit第二批restore/finalize(strategy并行)
- AiChat 第三批: 新建 ai/TopBar.vue(499行, header+provider bar+切换bar+scoped样式), AiChat.vue 3128→2937(-191); props4+emits13+store共享; toast单一源emit provider-switched - audit 第二批: 新建 audit/restore.rs(183行 restore_pending_approvals+2单测) + audit/finalize.rs(79行 audit_tool_call/audit_finalize), audit/mod.rs 812→586(-226); re-export路径透明, F-09 per_conv保留 主代兜底: cargo check --workspace 0 + test 98 + vue-tsc 0 strategy: 单批1-2文件原子, 并行(AiChat前端vue-tsc/audit后端cargo独立域) git add指定(非-A, 避免覆盖其他会话改动)
This commit is contained in:
@@ -285,3 +285,215 @@ onBeforeUnmount(() => {
|
||||
if (_providerBarTimer) { clearTimeout(_providerBarTimer); _providerBarTimer = null }
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- ═══ TopBar scoped 样式(第三批抽离: 从 AiChat.vue <style scoped> 迁移相关规则,
|
||||
零视觉变更。TopBar 多根节点(header + provider bar + Transition),父级 AiChat scoped
|
||||
不穿透多根子组件,故本组件自持样式。CSS 变量(--df-*)由根级提供,共享同一份。) ═══ -->
|
||||
<style scoped>
|
||||
/* ═══ Header ═══ */
|
||||
.ai-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 14px;
|
||||
border-bottom: 0.5px solid var(--df-border);
|
||||
}
|
||||
.ai-header-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.ai-header-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.ai-header-title {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: var(--df-text);
|
||||
}
|
||||
/* AE-2025-02:审批徽标(待审批>0 时显于头部,点击跳首个审批卡) */
|
||||
.ai-approval-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
background: var(--df-warning-bg);
|
||||
color: var(--df-warning);
|
||||
padding: 2px 8px;
|
||||
border-radius: var(--df-radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
transition: opacity 0.15s var(--df-ease);
|
||||
}
|
||||
.ai-approval-badge:hover {
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
/* ═══ F-01 阶段6: 模型选择器(自动/指定 toggle + 下拉) ═══ */
|
||||
.ai-model-picker {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
margin-left: auto;
|
||||
margin-right: 4px;
|
||||
padding-left: 8px;
|
||||
min-width: 0;
|
||||
}
|
||||
.ai-model-mode {
|
||||
display: inline-flex;
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius-sm);
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ai-model-mode-btn {
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--df-text-dim);
|
||||
font-size: 10px;
|
||||
padding: 3px 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s, color 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ai-model-mode-btn:hover:not(:disabled) {
|
||||
background: var(--df-bg-card);
|
||||
color: var(--df-text);
|
||||
}
|
||||
.ai-model-mode-btn--active {
|
||||
background: var(--df-accent-bg);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.ai-model-mode-btn:disabled {
|
||||
opacity: 0.45;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ai-model-select {
|
||||
min-width: 0;
|
||||
max-width: 180px;
|
||||
border: 0.5px solid var(--df-border);
|
||||
border-radius: var(--df-radius-sm);
|
||||
background: var(--df-bg);
|
||||
color: var(--df-text);
|
||||
font-size: 10px;
|
||||
font-family: var(--df-font-mono);
|
||||
padding: 3px 6px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ai-model-select:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ai-model-empty {
|
||||
font-size: 10px;
|
||||
color: var(--df-text-dim);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ai-header-actions {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
/* ── 图标按钮(共享工具类,header-actions 内清空/压缩/最大化/分离/关闭等)── */
|
||||
.ai-btn-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border: none;
|
||||
border-radius: var(--df-radius-sm);
|
||||
background: transparent;
|
||||
color: var(--df-text-dim);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.ai-btn-icon:hover {
|
||||
background: var(--df-bg-card);
|
||||
color: var(--df-text-secondary);
|
||||
}
|
||||
.ai-btn-icon--active {
|
||||
background: var(--df-accent-bg);
|
||||
color: var(--df-accent);
|
||||
}
|
||||
.ai-btn-icon:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.ai-btn-icon:disabled:hover {
|
||||
background: transparent;
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
/* 压缩中 spinner 图标旋转动画(@keyframes ai-btn-spin 在父级 AiChat.vue 全局定义,
|
||||
scoped 不重命名 @keyframes,此处按名引用) */
|
||||
.ai-btn-spinner {
|
||||
animation: ai-btn-spin 0.9s linear infinite;
|
||||
}
|
||||
|
||||
/* ═══ Provider Bar ═══ */
|
||||
.ai-provider-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 14px;
|
||||
border-bottom: 0.5px solid var(--df-border);
|
||||
}
|
||||
.ai-provider-bar--switchable {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ai-provider-bar--switchable:hover {
|
||||
background: var(--df-bg-card);
|
||||
}
|
||||
.provider-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--df-success);
|
||||
}
|
||||
.provider-name {
|
||||
font-size: 11px;
|
||||
font-family: var(--df-font-mono);
|
||||
color: var(--df-text-dim);
|
||||
}
|
||||
.ai-provider-empty {
|
||||
background: var(--df-danger-bg);
|
||||
}
|
||||
.provider-hint {
|
||||
font-size: 11px;
|
||||
color: var(--df-danger);
|
||||
}
|
||||
/* UX-2025-14: Provider 切换临时 bar(淡入淡出 0.15s) */
|
||||
.ai-provider-switch-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 4px 14px;
|
||||
background: color-mix(in srgb, var(--df-accent) 12%, transparent);
|
||||
border-bottom: 0.5px solid color-mix(in srgb, var(--df-accent) 25%, transparent);
|
||||
font-size: 11px;
|
||||
color: var(--df-accent);
|
||||
user-select: none;
|
||||
}
|
||||
.ai-provider-switch-dot {
|
||||
width: 5px;
|
||||
height: 5px;
|
||||
border-radius: 50%;
|
||||
background: var(--df-accent);
|
||||
flex-shrink: 0;
|
||||
animation: ai-agentic-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
.ai-provider-switch-text {
|
||||
font-family: var(--df-font-mono);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.ai-provider-switch-enter-active,
|
||||
.ai-provider-switch-leave-active { transition: opacity 0.15s var(--df-ease), max-height 0.15s var(--df-ease); }
|
||||
.ai-provider-switch-enter-from,
|
||||
.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; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user