zlwu 8 месяцев назад
Родитель
Сommit
ddd3b0e73b
1 измененных файлов с 16 добавлено и 5 удалено
  1. 16 5
      frontend/src/pages/ChatBotPage.tsx

+ 16 - 5
frontend/src/pages/ChatBotPage.tsx

@@ -215,6 +215,9 @@ export const ChatBotPage = () => {
   const hasSentScenarioContextRef = useRef(false);
   // 特殊场景:用户点击录音后收起凳子上形象、恢复中间动画形象
   const [specialSceneAvatarDismissed, setSpecialSceneAvatarDismissed] = useState(false);
+  // 首句场景是否已确定:未确定前不显示中间动画,避免「先出现再消失」的闪烁;确定后按 default/特殊场景分别显示
+  const [hasGreetingScenarioResolved, setHasGreetingScenarioResolved] = useState(false);
+  const hasReceivedGreetingContextRef = useRef(false);
   const greetingContextRef = useRef(greetingContext);
   useEffect(() => {
     greetingContextRef.current = greetingContext;
@@ -2266,6 +2269,8 @@ export const ChatBotPage = () => {
     
     switch (message.type) {
       case 'greeting_context': {
+        hasReceivedGreetingContextRef.current = true;
+        setHasGreetingScenarioResolved(true); // 已收到场景,可直接按场景展示(中间动画或凳子上 GIF)
         const ctx = message as unknown as GreetingContextMessage;
         if (ctx.scene && ctx.scene !== 'default' && Array.isArray(ctx.topics) && ctx.topics.length > 0) {
           setGreetingContext({
@@ -2291,6 +2296,10 @@ export const ChatBotPage = () => {
         if (message.source === 'user') {
           break;
         }
+        // 首句为 default 时后端不发 greeting_context,收到 greeting_ 的 text_result 即表示场景已确定为 default
+        if (isGreetingMessage && !hasReceivedGreetingContextRef.current) {
+          setHasGreetingScenarioResolved(true);
+        }
         const cleaned = cleanSubtitleText(message.text);
         // 🔥 首句/切换话题首句消息:直接处理,不检查 activeRequestId
         if (isGreetingOrTopicFirstLine) {
@@ -2387,6 +2396,9 @@ export const ChatBotPage = () => {
       case 'emotion':
         // 🔥 首句/切换话题首句:直接处理,不检查 activeRequestId
         if (isGreetingOrTopicFirstLine) {
+          if (isGreetingMessage && !hasReceivedGreetingContextRef.current) {
+            setHasGreetingScenarioResolved(true); // 首句为 default 时先收到 emotion,场景已确定
+          }
           performanceLogger.recordEmotion(requestId, message.emotion, message.timestamp);
           setEmotion(message.emotion);
           // 🔥 仅缓存情绪,不立即切换 GIF,等待首句音频真正开始播放时再驱动动画
@@ -3305,9 +3317,8 @@ export const ChatBotPage = () => {
             );
       })}
 
-      {/* AnimaZone 动画形象 - 双 img 固定角色:default 层永远 default;对话层 emotion→talking,放下时静默换 default;
-          特殊场景下未点击录音时隐藏,点击录音后显示 */}
-      {!(greetingContext?.scene && greetingContext.scene !== 'default' && !specialSceneAvatarDismissed) && (
+      {/* AnimaZone 动画形象 - 场景未确定前不显示,避免特殊场景下「先出现再消失」;确定后:默认场景直接显示,特殊场景下未点击录音时隐藏、点击录音后显示 */}
+      {hasGreetingScenarioResolved && !(greetingContext?.scene && greetingContext.scene !== 'default' && !specialSceneAvatarDismissed) && (
         <div
           className="chatbot-anima-zone-avatar absolute"
           style={{
@@ -3459,8 +3470,8 @@ export const ChatBotPage = () => {
         </div>
       )}
 
-      {/* Yp 图片 - 设计稿 393×852,相对动画形象顶部 191,64×14;特殊场景下点击录音后再显示 */}
-      {!(greetingContext?.scene && greetingContext.scene !== 'default' && !specialSceneAvatarDismissed) && (
+      {/* Yp 图片 - 设计稿 393×852,相对动画形象顶部 191,64×14;场景确定后且非特殊场景未点录音时才显示 */}
+      {hasGreetingScenarioResolved && !(greetingContext?.scene && greetingContext.scene !== 'default' && !specialSceneAvatarDismissed) && (
         <div
           className="absolute"
           style={{