ChatHeader.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. "use client";
  2. import { formatConversationTime } from "@/utils/format";
  3. import { Button } from "@/components/ui/button";
  4. import { Separator } from "@/components/ui/separator";
  5. interface ChatHeaderProps {
  6. conversationId: number;
  7. lastSeenAt?: string | null;
  8. unreadCount: number;
  9. onMarkAllRead: () => void;
  10. onRefresh: () => void;
  11. includeAIMessages?: boolean;
  12. onToggleAIMessages?: () => void;
  13. soundEnabled?: boolean;
  14. onToggleSound?: () => void;
  15. hideAIToggle?: boolean; // 内部对话时隐藏「显示 AI 消息」切换
  16. }
  17. export function ChatHeader({
  18. conversationId,
  19. lastSeenAt,
  20. unreadCount,
  21. onMarkAllRead,
  22. onRefresh,
  23. includeAIMessages = false,
  24. onToggleAIMessages,
  25. soundEnabled = false,
  26. onToggleSound,
  27. hideAIToggle = false,
  28. }: ChatHeaderProps) {
  29. return (
  30. <div className="h-16 flex items-center justify-between px-4 bg-background flex-shrink-0 relative">
  31. <div className="z-10">
  32. <div className="font-semibold text-foreground">对话 #{conversationId}</div>
  33. <div className="text-xs text-muted-foreground mt-0.5">
  34. {lastSeenAt
  35. ? `last seen ${formatConversationTime(lastSeenAt)}`
  36. : "last seen 未知"}
  37. </div>
  38. </div>
  39. <div className="flex items-center gap-2">
  40. {/* 显示/隐藏 AI 消息切换按钮(内部对话不显示,默认始终包含 AI 消息) */}
  41. {onToggleAIMessages && !hideAIToggle && (
  42. <Button
  43. variant={includeAIMessages ? "default" : "outline"}
  44. size="sm"
  45. onClick={onToggleAIMessages}
  46. title={includeAIMessages ? "隐藏 AI 消息" : "显示 AI 消息"}
  47. className="text-xs"
  48. >
  49. {includeAIMessages ? "隐藏 AI 消息" : "显示 AI 消息"}
  50. </Button>
  51. )}
  52. {/* 声音开关按钮 */}
  53. {onToggleSound && (
  54. <Button
  55. variant="ghost"
  56. size="icon"
  57. title={soundEnabled ? "关闭声音提示" : "开启声音提示"}
  58. onClick={onToggleSound}
  59. >
  60. {soundEnabled ? (
  61. <svg
  62. className="w-5 h-5"
  63. fill="none"
  64. stroke="currentColor"
  65. viewBox="0 0 24 24"
  66. >
  67. <path
  68. strokeLinecap="round"
  69. strokeLinejoin="round"
  70. strokeWidth={2}
  71. d="M15.536 8.464a5 5 0 010 7.072m2.828-9.9a9 9 0 010 12.728M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"
  72. />
  73. </svg>
  74. ) : (
  75. <svg
  76. className="w-5 h-5"
  77. fill="none"
  78. stroke="currentColor"
  79. viewBox="0 0 24 24"
  80. >
  81. <path
  82. strokeLinecap="round"
  83. strokeLinejoin="round"
  84. strokeWidth={2}
  85. d="M5.586 15H4a1 1 0 01-1-1v-4a1 1 0 011-1h1.586l4.707-4.707C10.923 3.663 12 4.109 12 5v14c0 .891-1.077 1.337-1.707.707L5.586 15z"
  86. />
  87. <path
  88. strokeLinecap="round"
  89. strokeLinejoin="round"
  90. strokeWidth={2}
  91. d="M17 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2"
  92. />
  93. </svg>
  94. )}
  95. </Button>
  96. )}
  97. <Button
  98. variant="ghost"
  99. size="icon"
  100. title="标记全部已读"
  101. onClick={onMarkAllRead}
  102. disabled={unreadCount === 0}
  103. >
  104. <svg
  105. className="w-5 h-5"
  106. fill="none"
  107. stroke="currentColor"
  108. viewBox="0 0 24 24"
  109. >
  110. <path
  111. strokeLinecap="round"
  112. strokeLinejoin="round"
  113. strokeWidth={2}
  114. d="M5 13l4 4L19 7"
  115. />
  116. </svg>
  117. </Button>
  118. <Button
  119. variant="ghost"
  120. size="icon"
  121. title="刷新"
  122. onClick={onRefresh}
  123. >
  124. <svg
  125. className="w-5 h-5 text-muted-foreground"
  126. fill="none"
  127. stroke="currentColor"
  128. viewBox="0 0 24 24"
  129. >
  130. <path
  131. strokeLinecap="round"
  132. strokeLinejoin="round"
  133. strokeWidth={2}
  134. d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
  135. />
  136. </svg>
  137. </Button>
  138. </div>
  139. <Separator className="absolute bottom-0 left-0 right-0" />
  140. </div>
  141. );
  142. }