From 8c121745218816cf37cf525096d49b4f2aa0917e Mon Sep 17 00:00:00 2001 From: hoping Date: Fri, 19 Dec 2025 17:31:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=AA=E6=80=A7=E5=8C=96=E5=A4=84=E7=90=86?= =?UTF-8?q?=201.=20=E4=B8=BA=E6=89=80=E6=9C=89=E6=8A=BD=E5=B1=89=E5=92=8C?= =?UTF-8?q?=E5=AF=B9=E8=AF=9D=E6=A1=86=E6=B7=BB=E5=8A=A0=20ESC=20=E9=94=AE?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=8A=9F=E8=83=BD=EF=BC=9B=202.=20=E4=B8=BA`?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=AE=B0=E5=BD=95`=E8=A1=A8=E6=A0=BC?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=87=AA=E5=8A=A8=E5=88=B7=E6=96=B0=E5=BC=80?= =?UTF-8?q?=E5=85=B3=EF=BC=9B=203.=20=E4=B8=BA=E5=90=8E=E7=AB=AF=20API=20?= =?UTF-8?q?=E8=AF=B7=E6=B1=82=E5=A2=9E=E5=8A=A0=20User-Agent=20=E5=A4=B4?= =?UTF-8?q?=E9=83=A8=EF=BC=9B=204.=20=E4=BF=AE=E6=94=B9=E5=90=AF=E5=8A=A8?= =?UTF-8?q?=E5=91=BD=E4=BB=A4=E6=94=AF=E6=8C=81=E4=BB=8E.env=E4=B8=AD?= =?UTF-8?q?=E8=AF=BB=E5=8F=96=E6=95=B0=E6=8D=AE=E5=BA=93=E5=92=8CRedis?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.example | 10 ++- dev.sh | 3 +- frontend/src/components/ui/dialog/Dialog.vue | 11 +++ frontend/src/composables/useEscapeKey.ts | 75 +++++++++++++++++++ .../models/components/ModelDetailDrawer.vue | 11 +++ .../components/ProviderDetailDrawer.vue | 11 +++ .../usage/components/RequestDetailDrawer.vue | 11 +++ .../usage/components/UsageRecordsTable.vue | 16 ++++ frontend/src/views/shared/Usage.vue | 32 +++++++- .../user/components/UserModelDetailDrawer.vue | 11 +++ src/api/admin/provider_query.py | 16 +++- 11 files changed, 201 insertions(+), 6 deletions(-) create mode 100644 frontend/src/composables/useEscapeKey.ts diff --git a/.env.example b/.env.example index 4c14763..12289d0 100644 --- a/.env.example +++ b/.env.example @@ -1,8 +1,16 @@ # ==================== 必须配置(启动前) ==================== # 以下配置项必须在项目启动前设置 -# 数据库密码 +# 数据库配置 +DB_HOST=localhost +DB_PORT=5432 +DB_USER=postgres +DB_NAME=aether DB_PASSWORD=your_secure_password_here + +# Redis 配置 +REDIS_HOST=localhost +REDIS_PORT=6379 REDIS_PASSWORD=your_redis_password_here # JWT密钥(使用 python generate_keys.py 生成) diff --git a/dev.sh b/dev.sh index 43e8dee..e66d786 100755 --- a/dev.sh +++ b/dev.sh @@ -8,7 +8,8 @@ source .env set +a # 构建 DATABASE_URL -export DATABASE_URL="postgresql://postgres:${DB_PASSWORD}@localhost:5432/aether" +export DATABASE_URL="postgresql://${DB_USER:-postgres}:${DB_PASSWORD}@${DB_HOST:-localhost}:${DB_PORT:-5432}/${DB_NAME:-aether}" +export REDIS_URL=redis://:${REDIS_PASSWORD}@${REDIS_HOST:-localhost}:${REDIS_PORT:-6379}/0 # 启动 uvicorn(热重载模式) echo "🚀 启动本地开发服务器..." diff --git a/frontend/src/components/ui/dialog/Dialog.vue b/frontend/src/components/ui/dialog/Dialog.vue index 26cb66c..9e45f5f 100644 --- a/frontend/src/components/ui/dialog/Dialog.vue +++ b/frontend/src/components/ui/dialog/Dialog.vue @@ -92,6 +92,7 @@ diff --git a/frontend/src/composables/useEscapeKey.ts b/frontend/src/composables/useEscapeKey.ts new file mode 100644 index 0000000..acb4094 --- /dev/null +++ b/frontend/src/composables/useEscapeKey.ts @@ -0,0 +1,75 @@ +import { onMounted, onUnmounted, ref } from 'vue' + +/** + * ESC 键监听 Composable(简化版本,直接使用独立监听器) + * 用于按 ESC 键关闭弹窗或其他可关闭的组件 + * + * @param callback - 按 ESC 键时执行的回调函数 + * @param options - 配置选项 + */ +export function useEscapeKey( + callback: () => void, + options: { + /** 是否在输入框获得焦点时禁用 ESC 键,默认 true */ + disableOnInput?: boolean + /** 是否只监听一次,默认 false */ + once?: boolean + } = {} +) { + const { disableOnInput = true, once = false } = options + const isActive = ref(true) + + function handleKeyDown(event: KeyboardEvent) { + // 只处理 ESC 键 + if (event.key !== 'Escape') return + + // 检查组件是否还活跃 + if (!isActive.value) return + + // 如果配置了在输入框获得焦点时禁用,则检查当前焦点元素 + if (disableOnInput) { + const activeElement = document.activeElement + const isInputElement = activeElement && ( + activeElement.tagName === 'INPUT' || + activeElement.tagName === 'TEXTAREA' || + activeElement.tagName === 'SELECT' || + activeElement.contentEditable === 'true' || + activeElement.getAttribute('role') === 'textbox' || + activeElement.getAttribute('role') === 'combobox' + ) + + // 如果焦点在输入框中,不处理 ESC 键 + if (isInputElement) return + } + + // 执行回调 + callback() + + // 如果只监听一次,则移除监听器 + if (once) { + removeEventListener() + } + } + + function addEventListener() { + document.addEventListener('keydown', handleKeyDown) + } + + function removeEventListener() { + document.removeEventListener('keydown', handleKeyDown) + } + + onMounted(() => { + addEventListener() + }) + + onUnmounted(() => { + isActive.value = false + removeEventListener() + }) + + return { + addEventListener, + removeEventListener + } +} \ No newline at end of file diff --git a/frontend/src/features/models/components/ModelDetailDrawer.vue b/frontend/src/features/models/components/ModelDetailDrawer.vue index bbc21d0..ebbd39d 100644 --- a/frontend/src/features/models/components/ModelDetailDrawer.vue +++ b/frontend/src/features/models/components/ModelDetailDrawer.vue @@ -698,6 +698,7 @@ import { Layers, BarChart3 } from 'lucide-vue-next' +import { useEscapeKey } from '@/composables/useEscapeKey' import { useToast } from '@/composables/useToast' import Card from '@/components/ui/card.vue' import Badge from '@/components/ui/badge.vue' @@ -833,6 +834,16 @@ watch(() => props.open, (newOpen) => { detailTab.value = 'basic' } }) + +// 添加 ESC 键监听 +useEscapeKey(() => { + if (props.open) { + handleClose() + } +}, { + disableOnInput: true, + once: false +})