page.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
  1. "use client";
  2. import { useEffect, useState } from "react";
  3. import { useRouter } from "next/navigation";
  4. import { API_BASE_URL } from "@/lib/config";
  5. // 对话类型定义
  6. interface Conversation {
  7. id: number;
  8. visitor_id: number;
  9. agent_id: number;
  10. status: string;
  11. created_at: string;
  12. updated_at: string;
  13. }
  14. export default function ConversationsPage() {
  15. const [conversations, setConversations] = useState<Conversation[]>([]);
  16. const [loading, setLoading] = useState(true);
  17. const [username, setUsername] = useState<string>("");
  18. const [role, setRole] = useState<string>("");
  19. const router = useRouter();
  20. // 检查是否已登录
  21. useEffect(() => {
  22. const userId = localStorage.getItem("agent_user_id");
  23. const savedUsername = localStorage.getItem("agent_username");
  24. const savedRole = localStorage.getItem("agent_role");
  25. if (!userId || !savedUsername) {
  26. // 未登录,跳转到登录页面
  27. router.push("/");
  28. return;
  29. }
  30. setUsername(savedUsername);
  31. setRole(savedRole || "");
  32. }, [router]);
  33. // 拉取对话列表
  34. const fetchConversations = async () => {
  35. try {
  36. const res = await fetch(`${API_BASE_URL}/conversations`);
  37. if (res.ok) {
  38. const data = await res.json();
  39. if (Array.isArray(data)) {
  40. setConversations(data);
  41. }
  42. } else {
  43. console.error("获取对话列表失败");
  44. }
  45. } catch (error) {
  46. console.error("获取对话列表失败:", error);
  47. } finally {
  48. setLoading(false);
  49. }
  50. };
  51. // 页面加载时拉取对话列表
  52. useEffect(() => {
  53. const userId = localStorage.getItem("agent_user_id");
  54. if (userId) {
  55. fetchConversations();
  56. }
  57. }, []);
  58. // 退出登录
  59. const handleLogout = async () => {
  60. try {
  61. await fetch(`${API_BASE_URL}/logout`, {
  62. method: "POST",
  63. });
  64. } catch (error) {
  65. console.error("退出登录失败:", error);
  66. } finally {
  67. // 清空本地存储
  68. localStorage.removeItem("agent_user_id");
  69. localStorage.removeItem("agent_username");
  70. localStorage.removeItem("agent_role");
  71. // 跳转到登录页面
  72. router.push("/");
  73. }
  74. };
  75. // 格式化时间显示
  76. const formatTime = (dateStr: string) => {
  77. const date = new Date(dateStr);
  78. const now = new Date();
  79. const diff = now.getTime() - date.getTime();
  80. // 今天:只显示时间
  81. if (diff < 24 * 3600 * 1000 && date.getDate() === now.getDate()) {
  82. return date.toLocaleTimeString("zh-CN", {
  83. hour: "2-digit",
  84. minute: "2-digit",
  85. });
  86. }
  87. // 更早:显示日期+时间
  88. return date.toLocaleString("zh-CN", {
  89. month: "2-digit",
  90. day: "2-digit",
  91. hour: "2-digit",
  92. minute: "2-digit",
  93. });
  94. };
  95. // 点击对话,跳转到聊天页面
  96. const handleConversationClick = (conversationId: number) => {
  97. router.push(`/agent/chat/${conversationId}`);
  98. };
  99. if (loading) {
  100. return (
  101. <div className="flex justify-center items-center min-h-screen">
  102. <div className="text-lg">加载中...</div>
  103. </div>
  104. );
  105. }
  106. return (
  107. <div className="flex flex-col h-screen bg-gray-50">
  108. {/* 顶部标题栏 */}
  109. <div className="bg-gradient-to-r from-blue-500 via-purple-500 to-pink-500 text-white p-4 shadow-md">
  110. <div className="flex justify-between items-center">
  111. <div>
  112. <h1 className="text-xl font-bold">对话列表</h1>
  113. <div className="text-sm opacity-90 mt-1">
  114. {username} ({role === "admin" ? "管理员" : "客服"})
  115. </div>
  116. </div>
  117. <button
  118. onClick={handleLogout}
  119. className="px-4 py-2 bg-white bg-opacity-20 hover:bg-opacity-30 rounded-lg transition-colors text-sm"
  120. >
  121. 退出登录
  122. </button>
  123. </div>
  124. </div>
  125. {/* 对话列表区域 */}
  126. <div className="flex-1 overflow-y-auto p-4">
  127. {conversations.length === 0 ? (
  128. <div className="text-center text-gray-400 mt-8">
  129. 暂无对话
  130. </div>
  131. ) : (
  132. <div className="space-y-2">
  133. {conversations.map((conv) => (
  134. <div
  135. key={conv.id}
  136. onClick={() => handleConversationClick(conv.id)}
  137. className="bg-white p-4 rounded-lg shadow-sm border border-gray-200 hover:shadow-md cursor-pointer transition-shadow"
  138. >
  139. <div className="flex justify-between items-start">
  140. <div className="flex-1">
  141. <div className="flex items-center gap-2 mb-1">
  142. <span className="font-medium text-gray-800">
  143. 对话 #{conv.id}
  144. </span>
  145. <span
  146. className={`px-2 py-1 rounded text-xs ${
  147. conv.status === "open"
  148. ? "bg-green-100 text-green-700"
  149. : "bg-gray-100 text-gray-700"
  150. }`}
  151. >
  152. {conv.status === "open" ? "进行中" : conv.status}
  153. </span>
  154. </div>
  155. <div className="text-sm text-gray-600">
  156. 访客ID: {conv.visitor_id}
  157. </div>
  158. <div className="text-xs text-gray-400 mt-1">
  159. 创建时间: {formatTime(conv.created_at)}
  160. </div>
  161. </div>
  162. <div className="text-xs text-gray-400">
  163. 最后更新: {formatTime(conv.updated_at)}
  164. </div>
  165. </div>
  166. </div>
  167. ))}
  168. </div>
  169. )}
  170. </div>
  171. </div>
  172. );
  173. }