hongjli
2025-04-10 36d9b7b4959bcd44acf0f15cd270ea275f4ee831
src/app/ai-scene/chat/page.tsx
@@ -2,9 +2,19 @@
import { useState, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { useSearchParams } from 'next/navigation';
import { useRouter } from 'next/navigation';
// 导入场景数据
const defaultServices = [
  {
    title: '插单2.0',
    description: '智能评估需求插单对产能,原材料和交付服务的影响,提升客户满意度',
    imageUrl: '/images/xuqiu.jpg',
    chatbotId: 'zO9YQDEHdIApG9zC',
    background: '在生产计划执行过程中,常常会遇到紧急订单需要插单的情况。AI系统可以快速评估插单对现有生产计划的影响,并提供最优的插单方案。',
    instructions: '请提供需要插单的订单信息,包括产品类型、数量和期望交付时间,AI助手将为您分析可行性并给出具体的插单建议。'
  },
  {
    title: '补料',
    description: '智能动态分析产线,工位的缺料情况,降低停线风险',
@@ -12,10 +22,12 @@
    chatbotId: 'JELkWpPLHQfRNhEH',
  },
  {
    title: '插单',
    title: '插单1.0',
    description: '智能评估需求插单对产能,原材料和交付服务的影响,提升客户满意度',
    imageUrl: '/images/xuqiu.jpg',
    chatbotId: 'DfH4cIzujVGvn5iR',
    background: '在生产计划执行过程中,常常会遇到紧急订单需要插单的情况。AI系统可以快速评估插单对现有生产计划的影响,并提供最优的插单方案。',
    instructions: '请提供需要插单的订单信息,包括产品类型、数量和期望交付时间,AI助手将为您分析可行性并给出具体的插单建议。'
  },
  {
    title: '科沃斯销售推荐小助手',
@@ -32,8 +44,14 @@
];
export default function AISceneChatPage() {
  const [services, setServices] = useState(defaultServices);
  const [selectedScene, setSelectedScene] = useState(services[0]);
  const searchParams = useSearchParams();
  const sceneId = searchParams.get('scene');
  const router = useRouter();
  // 根据URL参数找到对应的场景
  const initialScene = defaultServices.find(s => s.chatbotId === sceneId) || defaultServices[0];
  const [services] = useState([initialScene]);
  const [selectedScene, setSelectedScene] = useState(initialScene);
  const [iframeKey, setIframeKey] = useState(0);
  // 切换场景时重新加载iframe
@@ -42,61 +60,51 @@
    setIframeKey(prev => prev + 1);
  };
  // 添加新场景
  const handleAddNewScene = () => {
    const newScene = {
      title: `新场景 ${services.length + 1}`,
      description: '这是一个新的AI场景',
      imageUrl: '/images/robot.jpg', // 默认图片
      chatbotId: `new-scene-${Date.now()}`, // 生成唯一ID
    };
    setServices(prev => [...prev, newScene]);
  };
  return (
    <div className="h-screen flex bg-white">
      {/* 左侧场景选项卡 */}
      <div className="w-64 bg-white border-r flex flex-col">
        {/* 固定头部 */}
        <div className="p-4 pt-20 bg-white">
          <h2 className="text-xl font-bold flex items-center mb-4">
            <span className="text-gray-900">
              AI场景
            </span>
            <motion.span
              className="ml-2 inline-block w-2 h-2 rounded-full bg-[#6ADBFF]"
              animate={{
                scale: [1, 1.5, 1],
                opacity: [0.7, 1, 0.7]
              }}
              transition={{
                duration: 2,
                repeat: Infinity,
                ease: "easeInOut"
              }}
            />
          </h2>
          {/* 新增场景按钮 */}
          <button
            onClick={handleAddNewScene}
            className="w-full p-3 bg-[#EEF3FD] text-[#4080FF] rounded-lg font-medium transition-all duration-300 flex items-center hover:bg-[#E1E9FA] group"
          >
            <svg
              className="w-5 h-5 mr-2"
              viewBox="0 0 24 24"
              fill="none"
              stroke="currentColor"
            >
              <path
                d="M12 5v14M5 12h14"
                strokeWidth="2"
                strokeLinecap="round"
                strokeLinejoin="round"
          <div className="flex items-center justify-between mb-4">
            <h2 className="text-xl font-bold flex items-center">
              <span className="text-gray-900">
                AI场景
              </span>
              <motion.span
                className="ml-2 inline-block w-2 h-2 rounded-full bg-[#6ADBFF]"
                animate={{
                  scale: [1, 1.5, 1],
                  opacity: [0.7, 1, 0.7]
                }}
                transition={{
                  duration: 2,
                  repeat: Infinity,
                  ease: "easeInOut"
                }}
              />
            </svg>
            开启新场景
          </button>
            </h2>
            <button
              onClick={() => router.push('/ai-scene')}
              className="group flex items-center px-4 py-2 text-sm text-red-500 hover:text-white bg-red-50 hover:bg-red-500 rounded-lg transition-all duration-300 shadow-sm hover:shadow-md"
            >
              <svg
                xmlns="http://www.w3.org/2000/svg"
                className="h-4 w-4 mr-1.5 transition-transform duration-300 group-hover:-translate-x-0.5"
                fill="none"
                viewBox="0 0 24 24"
                stroke="currentColor"
              >
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M10 19l-7-7m0 0l7-7m-7 7h18"
                />
              </svg>
              <span className="font-medium">返回</span>
            </button>
          </div>
        </div>
        {/* 滚动内容区 */}
@@ -109,7 +117,7 @@
              >
                <motion.button
                  onClick={() => handleSceneChange(scene)}
                  className={`w-full p-4 rounded-lg text-left transition-all duration-500 relative
                  className={`w-full p-4 rounded-lg text-left transition-all duration-500 relative cursor-pointer
                    ${selectedScene.chatbotId === scene.chatbotId 
                      ? 'text-[#6ADBFF] bg-gray-100 shadow-sm' 
                      : 'text-gray-600 hover:text-[#6ADBFF] hover:bg-gray-50'
@@ -138,8 +146,20 @@
                      />
                      <div className="absolute inset-0 bg-gradient-to-br from-black/20 to-transparent"></div>
                    </div>
                    <div className="relative max-w-[120px]">
                      <span className="font-medium truncate block w-full">{scene.title}</span>
                    <div className="relative max-w-[120px] group/text">
                      <span className="font-medium truncate block w-full" ref={(el) => {
                        if (el) {
                          el.dataset.truncated = (el.scrollWidth > el.clientWidth).toString();
                        }
                      }}>{scene.title}</span>
                      {/* Tooltip - 只在文本被截断时显示 */}
                      <div
                        className="absolute left-1/2 -translate-x-1/2 -top-2 -translate-y-full bg-gray-800/95 text-white text-sm px-3 py-2 rounded-lg opacity-0 invisible data-[show=true]:group-hover/text:opacity-100 data-[show=true]:group-hover/text:visible transition-all duration-200 whitespace-nowrap shadow-lg z-[1000]"
                        data-show={scene.title.length > 8}
                      >
                        <div className="absolute left-1/2 -translate-x-1/2 top-full border-[6px] border-transparent border-t-gray-800/95"></div>
                        {scene.title}
                      </div>
                    </div>
                  </div>
                  {selectedScene.chatbotId === scene.chatbotId && (
@@ -152,13 +172,6 @@
                    />
                  )}
                </motion.button>
                {/* Tooltip */}
                <div
                  className="absolute left-1/2 -translate-x-1/2 -top-2 -translate-y-full bg-gray-800/95 text-white text-sm px-3 py-2 rounded-lg opacity-0 invisible group-hover:opacity-100 group-hover:visible transition-all duration-200 whitespace-nowrap shadow-lg z-[1000]"
                >
                  <div className="absolute left-1/2 -translate-x-1/2 top-full border-[6px] border-transparent border-t-gray-800/95"></div>
                  {scene.title}
                </div>
              </motion.div>
            ))}
          </div>
@@ -166,40 +179,16 @@
      </div>
      {/* 中间聊天区域 */}
      <div className="flex-1 flex flex-col bg-white">
        {/* 顶部标题栏 */}
        <div className="h-16 border-b flex items-center px-6 bg-white">
          <AnimatePresence mode="wait">
            <motion.div
              key={selectedScene.chatbotId}
              initial={{ opacity: 0, y: -20 }}
              animate={{ opacity: 1, y: 0 }}
              exit={{ opacity: 0, y: 20 }}
              transition={{ duration: 0.3 }}
              className="flex items-center"
            >
              <div className="w-8 h-8 rounded-lg overflow-hidden mr-3 relative">
                <img
                  src={selectedScene.imageUrl}
                  alt={selectedScene.title}
                  className="w-full h-full object-cover"
                />
                <div className="absolute inset-0 bg-gradient-to-br from-black/20 to-transparent"></div>
              </div>
              <div>
                <h1 className="text-xl font-bold text-gray-800">
                  {selectedScene.title}
                </h1>
                <p className="text-sm text-gray-500">
                  {selectedScene.description}
                </p>
              </div>
            </motion.div>
          </AnimatePresence>
      <div className="flex-1 flex flex-col bg-white pt-20 overflow-hidden">
        {/* 场景标题 */}
        <div className="h-16 bg-white flex items-center justify-center px-6 border-b">
          <h1 className="text-lg font-medium text-gray-900">
            {selectedScene.title}
          </h1>
        </div>
        {/* 聊天窗口 */}
        <div className="flex-1 relative">
        <div className="flex-1 relative [&::-webkit-scrollbar]:w-1 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:bg-[#E5E6EB] [&::-webkit-scrollbar-thumb]:rounded-full hover:[&::-webkit-scrollbar-thumb]:bg-[#C9CDD4]">
          <AnimatePresence mode="wait">
            <motion.div
              key={iframeKey}