ChatModeSelector.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. "use client";
  2. import { useState, useEffect } from "react";
  3. import { Button } from "@/components/ui/button";
  4. import { Card } from "@/components/ui/card";
  5. import { fetchPublicAIModels, type AIConfig } from "@/features/agent/services/aiConfigApi";
  6. export type ChatModeType = "human" | "ai" | "image";
  7. interface ChatModeSelectorProps {
  8. onSelect: (mode: ChatModeType, aiConfigId?: number) => void;
  9. loading?: boolean;
  10. }
  11. export function ChatModeSelector({ onSelect, loading }: ChatModeSelectorProps) {
  12. const [aiModels, setAiModels] = useState<AIConfig[]>([]);
  13. const [imageModels, setImageModels] = useState<AIConfig[]>([]);
  14. const [loadingModels, setLoadingModels] = useState(true);
  15. const [selectedAIModel, setSelectedAIModel] = useState<number | null>(null);
  16. const [selectedImageModel, setSelectedImageModel] = useState<number | null>(null);
  17. // 加载开放的 AI(文本)与生图模型列表
  18. useEffect(() => {
  19. async function loadModels() {
  20. try {
  21. const [textModels, imgModels] = await Promise.all([
  22. fetchPublicAIModels("text"),
  23. fetchPublicAIModels("image"),
  24. ]);
  25. setAiModels(textModels);
  26. setImageModels(imgModels);
  27. if (textModels.length > 0) setSelectedAIModel(textModels[0].id);
  28. if (imgModels.length > 0) setSelectedImageModel(imgModels[0].id);
  29. } catch (error) {
  30. console.error("加载模型列表失败:", error);
  31. } finally {
  32. setLoadingModels(false);
  33. }
  34. }
  35. loadModels();
  36. }, []);
  37. const handleSelectHuman = () => onSelect("human");
  38. const handleSelectAI = () => {
  39. if (selectedAIModel) onSelect("ai", selectedAIModel);
  40. else if (aiModels.length > 0) onSelect("ai", aiModels[0].id);
  41. };
  42. const handleSelectImage = () => {
  43. if (selectedImageModel) onSelect("image", selectedImageModel);
  44. else if (imageModels.length > 0) onSelect("image", imageModels[0].id);
  45. };
  46. return (
  47. <div className="flex flex-col items-center justify-center min-h-screen bg-background p-4">
  48. <div className="w-full max-w-2xl">
  49. <h1 className="text-3xl font-bold text-center mb-2 text-gray-800">
  50. 欢迎使用客服系统
  51. </h1>
  52. <p className="text-center text-gray-600 mb-8">
  53. 请选择您需要的服务方式
  54. </p>
  55. <div className="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6">
  56. {/* 人工客服 */}
  57. <Card className="p-6 cursor-pointer hover:shadow-lg transition-shadow border-2 hover:border-primary">
  58. <div className="flex flex-col items-center text-center">
  59. <div className="w-16 h-16 rounded-full bg-blue-100 flex items-center justify-center mb-4">
  60. <svg className="w-8 h-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  61. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" />
  62. </svg>
  63. </div>
  64. <h3 className="text-xl font-semibold mb-2">人工客服</h3>
  65. <p className="text-sm text-gray-600 mb-4">由专业客服人员为您提供一对一服务</p>
  66. <Button onClick={handleSelectHuman} disabled={loading} className="w-full">
  67. {loading ? "连接中..." : "选择人工客服"}
  68. </Button>
  69. </div>
  70. </Card>
  71. {/* AI 客服 */}
  72. <Card className="p-6 cursor-pointer hover:shadow-lg transition-shadow border-2 hover:border-primary">
  73. <div className="flex flex-col items-center text-center">
  74. <div className="w-16 h-16 rounded-full bg-purple-100 flex items-center justify-center mb-4">
  75. <svg className="w-8 h-8 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  76. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" />
  77. </svg>
  78. </div>
  79. <h3 className="text-xl font-semibold mb-2">AI 客服</h3>
  80. <p className="text-sm text-gray-600 mb-4">智能 AI 助手,24 小时在线为您服务</p>
  81. {loadingModels ? (
  82. <div className="w-full py-2 text-sm text-gray-500">加载模型中...</div>
  83. ) : aiModels.length === 0 ? (
  84. <div className="w-full py-2 text-sm text-red-500">暂无可用的 AI 模型</div>
  85. ) : (
  86. <>
  87. <select
  88. value={selectedAIModel || ""}
  89. onChange={(e) => setSelectedAIModel(Number(e.target.value))}
  90. className="w-full mb-4 px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary"
  91. disabled={loading}
  92. >
  93. {aiModels.map((m) => (
  94. <option key={m.id} value={m.id}>{m.provider} - {m.model}{m.description ? ` (${m.description})` : ""}</option>
  95. ))}
  96. </select>
  97. <Button onClick={handleSelectAI} disabled={loading || !selectedAIModel} variant="default" className="w-full">
  98. {loading ? "连接中..." : "选择 AI 客服"}
  99. </Button>
  100. </>
  101. )}
  102. </div>
  103. </Card>
  104. {/* 生图绘画 */}
  105. <Card className="p-6 cursor-pointer hover:shadow-lg transition-shadow border-2 hover:border-primary">
  106. <div className="flex flex-col items-center text-center">
  107. <div className="w-16 h-16 rounded-full bg-amber-100 flex items-center justify-center mb-4">
  108. <svg className="w-8 h-8 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
  109. <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z" />
  110. </svg>
  111. </div>
  112. <h3 className="text-xl font-semibold mb-2">生图绘画</h3>
  113. <p className="text-sm text-gray-600 mb-4">通过对话描述,AI 生成图片</p>
  114. {loadingModels ? (
  115. <div className="w-full py-2 text-sm text-gray-500">加载模型中...</div>
  116. ) : imageModels.length === 0 ? (
  117. <div className="w-full py-2 text-sm text-red-500">暂无可用的生图模型</div>
  118. ) : (
  119. <>
  120. <select
  121. value={selectedImageModel || ""}
  122. onChange={(e) => setSelectedImageModel(Number(e.target.value))}
  123. className="w-full mb-4 px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary"
  124. disabled={loading}
  125. >
  126. {imageModels.map((m) => (
  127. <option key={m.id} value={m.id}>{m.provider} - {m.model}{m.description ? ` (${m.description})` : ""}</option>
  128. ))}
  129. </select>
  130. <Button onClick={handleSelectImage} disabled={loading || !selectedImageModel} variant="default" className="w-full">
  131. {loading ? "连接中..." : "选择生图绘画"}
  132. </Button>
  133. </>
  134. )}
  135. </div>
  136. </Card>
  137. </div>
  138. </div>
  139. </div>
  140. );
  141. }