- 全局样式类(df-sec-h/df-toast族)替换散装实现,主题token去硬编码兜底 - 拆分子组件:ai-chat/queue-panel(队列时钟与编辑随迁)、 conversation-sidebar/ConversationList、settings/provider-panel - 父组件死副本清理(队列编辑/时钟/键位表,TS6133清零)并补 fill-draft父侧转发(重构半成品断链修复) - 验收:vue-tsc EXIT 0
118 lines
3.6 KiB
Vue
118 lines
3.6 KiB
Vue
<template>
|
|
<!-- I12(2026-10-05):键盘快捷键速查。Teleport 到 body 防 .ai-panel overflow 裁剪;
|
|
主窗/分离窗各自实例独立渲染,入口在 TopBar ... 菜单(emit show-shortcuts)。
|
|
C4b 拆分自 AiChat.vue(visible 状态留父——onGlobalKeydown Esc 仲裁需读写,开/关经 props/emits) -->
|
|
<Teleport to="body">
|
|
<div v-if="visible" class="ai-shortcuts-mask" @click.self="emit('close')">
|
|
<div class="ai-shortcuts-panel" role="dialog" :aria-label="t('aiChat.shortcutsTitle')">
|
|
<div class="ai-shortcuts-head">
|
|
<span class="ai-shortcuts-title">{{ t('aiChat.shortcutsTitle') }}</span>
|
|
<button class="ai-shortcuts-close" :aria-label="t('aiChat.scCloseOverlay')" @click="emit('close')">✕</button>
|
|
</div>
|
|
<ul class="ai-shortcuts-list">
|
|
<li v-for="s in shortcuts" :key="s.key" class="ai-shortcuts-row">
|
|
<span class="ai-shortcuts-key">{{ s.key }}</span>
|
|
<span class="ai-shortcuts-act">{{ s.label }}</span>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</Teleport>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed } from 'vue'
|
|
import { useI18n } from 'vue-i18n'
|
|
|
|
defineProps<{
|
|
/** 面板显隐(状态留父:TopBar emit show-shortcuts 置 true,onGlobalKeydown Esc 分支仲裁) */
|
|
visible: boolean
|
|
}>()
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'close'): void
|
|
}>()
|
|
|
|
const { t } = useI18n()
|
|
|
|
// 键位表 shortcuts 与父 AiChat onGlobalKeydown 同族——键区调整时两边同步,防速查与真实键位漂移。
|
|
// computed:随 locale 即时翻译(仅面板打开时消费,开销可忽略)。
|
|
const shortcuts = computed(() => [
|
|
{ key: 'Ctrl+N', label: t('aiChat.scNewConv') },
|
|
{ key: 'Ctrl+I', label: t('aiChat.scFocusInput') },
|
|
{ key: 'Ctrl+R', label: t('aiChat.scRegenerate') },
|
|
{ key: 'Ctrl+L', label: t('aiChat.scClearChat') },
|
|
{ key: 'Ctrl+K', label: t('aiChat.scSearch') },
|
|
{ key: 'Ctrl+B', label: t('aiChat.scToggleSidebar') },
|
|
{ key: 'Esc', label: t('aiChat.scCloseOverlay') },
|
|
])
|
|
</script>
|
|
|
|
<style scoped>
|
|
/* ═══ I12 快捷键速查面板(Teleport 到 body,scoped 对 Teleport 内容仍生效——
|
|
Vue scoped 把 data 属性打在编译期节点上,与挂载位置无关;样式随区块自 AiChat.vue 迁入) ═══ */
|
|
.ai-shortcuts-mask {
|
|
position: fixed;
|
|
inset: 0;
|
|
background: rgba(0, 0, 0, 0.4);
|
|
z-index: 1000;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.ai-shortcuts-panel {
|
|
background: var(--df-bg-card);
|
|
border: 0.5px solid var(--df-border);
|
|
border-radius: var(--df-radius-lg, 8px);
|
|
box-shadow: var(--df-shadow-sm);
|
|
width: 320px;
|
|
max-width: calc(100vw - 48px);
|
|
padding: 14px 16px;
|
|
}
|
|
.ai-shortcuts-head {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
margin-bottom: 10px;
|
|
}
|
|
.ai-shortcuts-title {
|
|
font-size: 13px;
|
|
font-weight: 600;
|
|
color: var(--df-text);
|
|
}
|
|
.ai-shortcuts-close {
|
|
background: transparent;
|
|
border: none;
|
|
color: var(--df-text-secondary);
|
|
cursor: pointer;
|
|
font-size: 12px;
|
|
padding: 2px 4px;
|
|
border-radius: 4px;
|
|
}
|
|
.ai-shortcuts-close:hover { color: var(--df-text); background: var(--df-bg-card); }
|
|
.ai-shortcuts-list {
|
|
list-style: none;
|
|
margin: 0;
|
|
padding: 0;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
}
|
|
.ai-shortcuts-row {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
font-size: 12px;
|
|
}
|
|
.ai-shortcuts-key {
|
|
font-family: var(--df-font-mono);
|
|
font-size: 11px;
|
|
background: var(--df-bg-card);
|
|
border: 0.5px solid var(--df-border);
|
|
border-radius: 4px;
|
|
padding: 2px 8px;
|
|
color: var(--df-text);
|
|
}
|
|
.ai-shortcuts-act { color: var(--df-text-secondary); }
|
|
</style>
|