From 099de1e922f0ccdad7ed9a10101491bef8eab301 Mon Sep 17 00:00:00 2001 From: JOJO <1498581755@qq.com> Date: Tue, 30 Dec 2025 10:32:09 +0800 Subject: [PATCH] feat: add blank chat hero and limit quick menu actions --- static/src/App.vue | 87 ++++++++++--------- static/src/app.ts | 83 ++++++++++++++++-- .../styles/components/input/_composer.scss | 55 ++++++++++++ 3 files changed, 176 insertions(+), 49 deletions(-) diff --git a/static/src/App.vue b/static/src/App.vue index 63f49f2..97c1671 100644 --- a/static/src/App.vue +++ b/static/src/App.vue @@ -127,6 +127,11 @@ /> +
+ +

{{ blankWelcomeText }}

+
+
- +
+ +
diff --git a/static/src/app.ts b/static/src/app.ts index 670f31f..63a6fe9 100644 --- a/static/src/app.ts +++ b/static/src/app.ts @@ -148,6 +148,19 @@ const appOptions = { historyLoading: false, historyLoadingFor: null, historyLoadSeq: 0, + blankHeroActive: false, + blankHeroExiting: false, + blankWelcomeText: '', + blankWelcomePool: [ + '有什么可以帮忙的?', + '想了解些热点吗?', + '要我帮你完成作业吗?', + '整点代码?', + '随便聊点什么?', + '想让我帮你整理一下思路吗?', + '要不要我帮你写个小工具?', + '发我一句话,我来接着做。' + ], mobileViewportQuery: null, modeMenuOpen: false, conversationListRequestSeq: 0, @@ -318,6 +331,9 @@ const appOptions = { composerBusy() { const monitorLock = this.monitorIsLocked && this.chatDisplayMode === 'monitor'; return this.streamingUi || this.taskInProgress || monitorLock || this.stopRequested; + }, + composerHeroActive() { + return this.blankHeroActive || this.blankHeroExiting; } }, @@ -342,6 +358,12 @@ const appOptions = { inputMessage() { this.autoResizeInput(); }, + messages: { + deep: true, + handler() { + this.refreshBlankHeroState(); + } + }, currentConversationId: { immediate: false, handler(newValue, oldValue) { @@ -354,6 +376,7 @@ const appOptions = { historyLoadingFor: this.historyLoadingFor, historyLoadSeq: this.historyLoadSeq }); + this.refreshBlankHeroState(); this.logMessageState('watch:currentConversationId', { oldValue, newValue, skipConversationHistoryReload: this.skipConversationHistoryReload }); if (!newValue || typeof newValue !== 'string' || newValue.startsWith('temp_')) { return; @@ -1397,6 +1420,7 @@ const appOptions = { const targetConversationId = this.currentConversationId; if (!targetConversationId || targetConversationId.startsWith('temp_')) { debugLog('没有当前对话ID,跳过历史加载'); + this.refreshBlankHeroState(); return; } @@ -1481,16 +1505,17 @@ const appOptions = { debugLog('尝试不显示错误弹窗,仅在控制台记录'); // 不显示alert,避免打断用户体验 this.logMessageState('fetchAndDisplayHistory:error-clear', { error: error?.message || String(error) }); - this.messages = []; - this.logMessageState('fetchAndDisplayHistory:error-cleared'); + this.messages = []; + this.logMessageState('fetchAndDisplayHistory:error-cleared'); + } + } finally { + // 仅在本次加载仍是最新请求时清除 loading 状态 + if (loadSeq === this.historyLoadSeq) { + this.historyLoading = false; + this.historyLoadingFor = null; + } + this.refreshBlankHeroState(); } - } finally { - // 仅在本次加载仍是最新请求时清除 loading 状态 - if (loadSeq === this.historyLoadSeq) { - this.historyLoading = false; - this.historyLoadingFor = null; - } - } }, // ========================================== @@ -2020,6 +2045,16 @@ const appOptions = { return; } + const wasBlank = this.isConversationBlank(); + if (wasBlank) { + this.blankHeroExiting = true; + this.blankHeroActive = true; + setTimeout(() => { + this.blankHeroExiting = false; + this.blankHeroActive = false; + }, 320); + } + // 标记任务进行中,直到任务完成或用户手动停止 this.taskInProgress = true; this.chatAddUserMessage(message); @@ -2373,6 +2408,36 @@ const appOptions = { this.uiSetPanelMenuOpen(false); }, + isConversationBlank() { + if (!Array.isArray(this.messages) || !this.messages.length) return true; + return !this.messages.some( + (msg) => msg && (msg.role === 'user' || msg.role === 'assistant') + ); + }, + + pickWelcomeText() { + const pool = this.blankWelcomePool; + if (!Array.isArray(pool) || !pool.length) { + this.blankWelcomeText = '有什么可以帮忙的?'; + return; + } + const idx = Math.floor(Math.random() * pool.length); + this.blankWelcomeText = pool[idx]; + }, + + refreshBlankHeroState() { + const isBlank = this.isConversationBlank(); + if (isBlank) { + if (!this.blankHeroExiting) { + this.pickWelcomeText(); + } + this.blankHeroActive = true; + } else { + this.blankHeroActive = false; + this.blankHeroExiting = false; + } + }, + applyToolSettingsSnapshot(categories) { if (!Array.isArray(categories)) { console.warn('[ToolSettings] Snapshot skipped: categories not array', categories); diff --git a/static/src/styles/components/input/_composer.scss b/static/src/styles/components/input/_composer.scss index aa5672f..80b1706 100644 --- a/static/src/styles/components/input/_composer.scss +++ b/static/src/styles/components/input/_composer.scss @@ -260,6 +260,61 @@ gap: 6px; } +/* Blank conversation hero */ +.chat-container { + position: relative; +} + +.blank-hero-overlay { + position: absolute; + inset: 0; + display: flex; + align-items: center; + justify-content: center; + flex-direction: column; + pointer-events: none; + z-index: 1; + gap: 10px; + padding-bottom: 140px; +} + +.blank-hero-text { + font-size: 32px; + color: #2f3a4a; + font-weight: 600; +} + +.blank-hero-overlay .icon-lg { + width: 48px; + height: 48px; +} + +.composer-container { + position: relative; + transition: transform 0.3s ease; + z-index: 2; +} + +.composer-container.blank-hero-mode { + transform: translateY(-38vh); +} + +@media (max-width: 768px) { + .blank-hero-text { + font-size: 28px; + } + .blank-hero-overlay .icon-lg { + width: 44px; + height: 44px; + } + .composer-container.blank-hero-mode { + transform: translateY(-32vh); + } + .blank-hero-overlay { + padding-bottom: 120px; + } +} + .quick-submenu.tool-submenu { top: auto; bottom: 0;