@@ -1,29 +1,25 @@
import React , { useState , useEffect } from 'react' ;
import React , { useState , useEffect , useRef , useCallback } from 'react' ;
import { useSearchParams , useNavigate } from 'react-router-dom' ;
import {
Facebook ,
MessageSquare ,
Instagram ,
Linkedin ,
CheckCircle ,
XCircle ,
MoreHorizontal ,
ExternalLink ,
BookOpen ,
ThumbsUp ,
ThumbsDown ,
Minus ,
AlertTriangle ,
User ,
Award ,
Briefcase ,
Youtube ,
Hash ,
Filter ,
ChevronDown ,
ArrowLeft
ArrowLeft ,
Instagram ,
Linkedin ,
Youtube
} from 'lucide-react' ;
import CommentPreview from './CommentPreview' ;
import { Spin , Empty } from 'antd' ;
import { Comment } from '../types' ;
// 定义后端返回的评论类型
interface ApiComment {
@@ -39,26 +35,21 @@ interface ApiComment {
full_name : string ;
avatar_url : string ;
} ;
post ? : {
title : string ;
post_id : string ;
platform : string ;
post_url : string ;
description : string ;
published_at : string ;
influencer_id : string ;
} ;
}
// 定义前端使用的评论类型
interface FrontendComment {
id : string ;
content : string ;
author : string ;
authorType : 'user' | 'kol' | 'official' ;
platform : 'facebook' | 'threads' | 'instagram' | 'linkedin' | 'xiaohongshu' | 'youtube' ;
contentType ? : 'post' | 'reel' | 'video' | 'short' ;
timestamp : string ;
sentiment : string ;
status : string ;
replyStatus? : string ;
language? : string ;
articleTitle? : string ;
postAuthor? : string ;
postAuthorType? : string ;
url? : string ;
}
// 使用导入的Comment类型, 不再需要FrontendComment接口
// interface FrontendComment {
// ...
// }
interface CommentListProps {
postId? : string ; // 可选的帖子 ID, 如果提供则只获取该帖子的评论
@@ -70,6 +61,8 @@ interface PostData {
description? : string ;
platform : string ;
post_url? : string ;
author? : string ;
authorType? : string ;
}
const CommentList : React.FC < CommentListProps > = ( ) = > {
@@ -77,23 +70,34 @@ const CommentList: React.FC<CommentListProps> = () => {
const navigate = useNavigate ( ) ;
const postId = searchParams . get ( 'post_id' ) ;
const [ comments , setComments ] = useState < Frontend Comment[ ] > ( [ ] ) ;
const [ comments , setComments ] = useState < Comment [ ] > ( [ ] ) ;
const [ post , setPost ] = useState < PostData | null > ( null ) ; // Store post data
const [ loading , setLoading ] = useState < boolean > ( true ) ;
const [ selectedComment , setSelectedComment ] = useState < Frontend Comment | null > ( null ) ;
const [ selectedComment , setSelectedComment ] = useState < Comment | null > ( null ) ;
const [ error , setError ] = useState < string | null > ( null ) ;
// 过滤和分页状态
const [ platformFilter , setPlatformFilter ] = useState < string > ( 'all' ) ;
const [ statusFilter , setStatusFilter ] = useState < string > ( 'all' ) ;
const [ sentimentFilter , setSentimentFilter ] = useState < string > ( 'all' ) ;
const [ sentimentFilter ] = useState < string > ( 'all' ) ;
const [ replyStatusFilter , setReplyStatusFilter ] = useState < string > ( 'all' ) ;
const [ languageFilter , setLanguageFilter ] = useState < string > ( 'all' ) ;
const [ searchQuery , setSearchQuery ] = useState < string > ( '' ) ;
const [ currentPage , setCurrentPage ] = useState < number > ( 1 ) ;
const [ pageSize , setPageSize ] = useState < number > ( 1 0) ;
const [ offset , setOffset ] = useState < number > ( 0 ) ;
const [ pageSize ] = useState < number > ( 2 0) ;
const [ totalComments , setTotalComments ] = useState < number > ( 0 ) ;
const [ showFilters , setShowFilters ] = useState < boolean > ( false ) ;
const [ hasMore , setHasMore ] = useState < boolean > ( true ) ;
const [ isLoadingMore , setIsLoadingMore ] = useState < boolean > ( false ) ;
// 无限滚动相关的引用
const observer = useRef < IntersectionObserver | null > ( null ) ;
const loadMoreRef = useRef < HTMLDivElement | null > ( null ) ;
// 添加日志, 查看是否正确获取到postId
useEffect ( ( ) = > {
console . log ( 'CommentList - postId from URL:' , postId ) ;
} , [ postId ] ) ;
// Fetch post details if postId is provided
useEffect ( ( ) = > {
@@ -103,19 +107,95 @@ const CommentList: React.FC<CommentListProps> = () => {
try {
setLoading ( true ) ;
// Mock post data
const mockPost = {
id : postId ,
title : 'Sample Post Title' ,
content : 'This is a sample post content for demonstration purposes.' ,
platform : 'Facebook' ,
url : 'https://facebook.com/sample-post'
} ;
// 获取认证token
const token = await getAuthToken ( ) ;
if ( ! token ) {
console . error ( 'Authentication token not found' ) ;
setError ( 'Authentication required. Please log in again.' ) ;
setLoading ( false ) ;
return ;
}
// 从后端API获取帖子详情
const response = await fetch ( ` http://localhost:4000/api/posts/ ${ postId } ` , {
method : 'GET' ,
headers : {
'Content-Type' : 'application/json' ,
'Authorization' : ` Bearer ${ token } `
}
} ) ;
if ( ! response . ok ) {
throw new Error ( ` API request failed with status: ${ response . status } ` ) ;
}
const data = await response . json ( ) ;
if ( data ) {
console . log ( 'Post details from API:' , data ) ;
// 将API返回的平台值映射到Comment类型中定义的平台值
let mappedPlatform : Comment [ 'platform' ] = 'facebook' ; // 默认值
const platformFromApi = data . platform || 'unknown' ;
// 映射平台值
switch ( platformFromApi . toLowerCase ( ) ) {
case 'facebook' :
mappedPlatform = 'facebook' ;
break ;
case 'instagram' :
mappedPlatform = 'instagram' ;
break ;
case 'linkedin' :
mappedPlatform = 'linkedin' ;
break ;
case 'youtube' :
mappedPlatform = 'youtube' ;
break ;
case 'threads' :
mappedPlatform = 'threads' ;
break ;
case 'twitter' : // 添加对Twitter的支持
mappedPlatform = 'twitter' ; // 直接映射到twitter
break ;
case 'xiaohongshu' :
case '小红书' :
mappedPlatform = 'xiaohongshu' ;
break ;
default :
// 如果无法映射,使用默认值
console . warn ( ` Unknown platform: ${ platformFromApi } , using default: facebook ` ) ;
mappedPlatform = 'facebook' ;
}
setPost ( {
id : data.post_id ,
title : data.title || 'Untitled Post' ,
description : data.description || '' ,
platform : mappedPlatform , // 使用映射后的平台值
post_url : data.post_url ,
author : data.influencer?.name || 'Unknown Author' ,
authorType : 'kol'
} ) ;
} else {
// 如果找不到帖子, 使用帖子ID作为标题
setPost ( {
id : postId ,
title : ` Post ${ postId } ` ,
platform : 'facebook' // 使用有效的平台值
} ) ;
}
setPost ( mockPost ) ;
setLoading ( false ) ;
} catch ( error ) {
console . error ( 'Error fetching post details:' , error ) ;
// 如果获取帖子详情失败, 使用帖子ID作为标题
setPost ( {
id : postId ,
title : ` Post ${ postId } ` ,
platform : 'facebook' // 使用有效的平台值
} ) ;
setLoading ( false ) ;
}
} ;
@@ -123,54 +203,394 @@ const CommentList: React.FC<CommentListProps> = () => {
fetchPostDetails ( ) ;
} , [ postId ] ) ;
// Fetch comments
// 加载更多评论的函数
const loadMoreComments = useCallback ( async ( ) = > {
if ( isLoadingMore || ! hasMore ) return ;
console . log ( 'Loading more comments from offset:' , offset ) ;
setIsLoadingMore ( true ) ;
try {
// 获取认证token
const token = await getAuthToken ( ) ;
if ( ! token ) {
console . error ( 'Authentication token not found' ) ;
setError ( 'Authentication required. Please log in again.' ) ;
setIsLoadingMore ( false ) ;
return ;
}
// 构建查询参数
const params = new URLSearchParams ( ) ;
// 如果URL中有post_id参数, 则只获取该帖子的评论
if ( postId ) {
console . log ( 'Loading more comments for specific post:' , postId ) ;
params . append ( 'post_id' , postId ) ;
}
params . append ( 'limit' , pageSize . toString ( ) ) ;
params . append ( 'offset' , offset . toString ( ) ) ;
if ( platformFilter !== 'all' ) {
params . append ( 'platform' , platformFilter ) ;
}
if ( statusFilter !== 'all' ) {
params . append ( 'status' , statusFilter ) ;
}
if ( sentimentFilter !== 'all' ) {
params . append ( 'sentiment' , sentimentFilter ) ;
}
if ( replyStatusFilter !== 'all' ) {
params . append ( 'reply_status' , replyStatusFilter ) ;
}
if ( searchQuery ) {
params . append ( 'search' , searchQuery ) ;
}
console . log ( 'Fetching more comments with params:' , params . toString ( ) ) ;
// 从后端API获取评论数据
const response = await fetch ( ` http://localhost:4000/api/comments? ${ params . toString ( ) } ` , {
method : 'GET' ,
headers : {
'Content-Type' : 'application/json' ,
'Authorization' : ` Bearer ${ token } `
}
} ) ;
if ( ! response . ok ) {
throw new Error ( ` API request failed with status: ${ response . status } ` ) ;
}
const data = await response . json ( ) ;
console . log ( 'Received more comments:' , data ? . comments ? . length || 0 ) ;
if ( data && Array . isArray ( data . comments ) ) {
// 转换API返回的数据为前端需要的格式
const newComments : Comment [ ] = data . comments . map ( ( apiComment : ApiComment ) = > {
// 计算情感分析结果
let sentiment : 'positive' | 'negative' | 'neutral' | 'mixed' = 'neutral' ;
if ( apiComment . sentiment_score > 0.3 ) {
sentiment = 'positive' ;
} else if ( apiComment . sentiment_score < - 0.3 ) {
sentiment = 'negative' ;
}
// 检测语言
const language = detectLanguage ( apiComment . content ) ;
// 使用API返回的post数据中的platform, 如果没有则使用post对象中的platform
const platformFromApi = apiComment . post ? . platform || ( post ? . platform || 'unknown' ) ;
console . log ( 'Comment platform from API:' , platformFromApi , 'Post platform:' , post ? . platform ) ;
// 将API返回的平台值映射到Comment类型中定义的平台值
let mappedPlatform : Comment [ 'platform' ] = 'facebook' ; // 默认值
// 映射平台值
switch ( platformFromApi . toLowerCase ( ) ) {
case 'facebook' :
mappedPlatform = 'facebook' ;
break ;
case 'instagram' :
mappedPlatform = 'instagram' ;
break ;
case 'linkedin' :
mappedPlatform = 'linkedin' ;
break ;
case 'youtube' :
mappedPlatform = 'youtube' ;
break ;
case 'threads' :
mappedPlatform = 'threads' ;
break ;
case 'twitter' : // 添加对Twitter的支持
mappedPlatform = 'twitter' ; // 直接映射到twitter
break ;
case 'xiaohongshu' :
case '小红书' :
mappedPlatform = 'xiaohongshu' ;
break ;
default :
// 如果无法映射,使用默认值
console . warn ( ` Unknown platform: ${ platformFromApi } , using default: facebook ` ) ;
mappedPlatform = 'facebook' ;
}
return {
id : apiComment.comment_id ,
content : apiComment.content ,
author : apiComment.user_profile?.full_name || 'Anonymous User' ,
authorType : 'user' , // 默认为普通用户
platform : mappedPlatform , // 使用映射后的平台值
timestamp : apiComment.created_at ,
sentiment : sentiment ,
status : 'approved' , // 默认状态
replyStatus : 'none' ,
language : language as Comment [ 'language' ] ,
articleTitle : apiComment.post?.title || 'Untitled Post' ,
postAuthor : post?.author || 'Unknown Author' ,
postAuthorType : 'kol' , // 默认为KOL
url : apiComment.post?.post_url || '#'
} ;
} ) ;
// 更新状态
setComments ( prevComments = > [ . . . prevComments , . . . newComments ] ) ;
setTotalComments ( data . count || totalComments ) ;
setOffset ( prevOffset = > prevOffset + newComments . length ) ;
// 检查是否还有更多数据
setHasMore ( newComments . length === pageSize ) ;
console . log ( 'Updated comments count:' , comments . length + newComments . length ) ;
} else {
// 如果没有更多数据
console . log ( 'No more comments available' ) ;
setHasMore ( false ) ;
}
} catch ( err ) {
console . error ( 'Error fetching more comments:' , err ) ;
setError ( 'Failed to fetch more comments.' ) ;
} finally {
setIsLoadingMore ( false ) ;
}
} , [ offset , platformFilter , statusFilter , sentimentFilter , replyStatusFilter , searchQuery , postId , pageSize , isLoadingMore , hasMore , totalComments , comments . length , post ] ) ;
// 设置Intersection Observer来监测滚动
useEffect ( ( ) = > {
if ( loading ) return ;
if ( observer . current ) {
observer . current . disconnect ( ) ;
}
observer . current = new IntersectionObserver ( entries = > {
if ( entries [ 0 ] . isIntersecting && hasMore && ! isLoadingMore ) {
console . log ( 'Intersection observed, loading more comments...' ) ;
loadMoreComments ( ) ;
}
} , {
rootMargin : '100px' , // 提前100px触发加载
threshold : 0.1 // 降低阈值,使其更容易触发
} ) ;
if ( loadMoreRef . current ) {
observer . current . observe ( loadMoreRef . current ) ;
}
return ( ) = > {
if ( observer . current ) {
observer . current . disconnect ( ) ;
}
} ;
} , [ loading , hasMore , isLoadingMore , loadMoreComments ] ) ;
// Fetch comments - 修改为初始加载
useEffect ( ( ) = > {
const fetchComments = async ( ) = > {
try {
setLoading ( true ) ;
setComments ( [ ] ) ;
setOffset ( 0 ) ;
setHasMore ( true ) ;
// Mock comments data
const mockComments = [
{
id : '1' ,
content : 'Great post! I really enjoyed reading this.' ,
author : 'John Smith' ,
timestamp : '2023-05-15T10:30:00Z' ,
platform : 'Facebook' ,
sentiment : 'positive' ,
status : 'approved'
} ,
{
id : '2' ,
content : 'This was very helpful, thanks for sharing!' ,
author : 'Sarah Johnson' ,
timestamp : '2023-05-14T14:45:00Z' ,
platform : 'Twitter' ,
sentiment : 'positive' ,
status : 'pending'
} ,
{
id : '3' ,
content : 'I have a question about the third point you mentioned...' ,
author : 'Michael Brown' ,
timestamp : '2023-05-13T09:15:00Z' ,
platform : 'Instagram' ,
sentiment : 'neutral' ,
status : 'approved'
// 获取认证token
const token = await getAuthToken ( ) ;
if ( ! token ) {
console . error ( 'Authentication token not found' ) ;
setError ( 'Authentication required. Please log in again.' ) ;
setLoading ( false ) ;
return ;
}
// 构建查询参数
const params = new URLSearchParams ( ) ;
// 如果URL中有post_id参数, 则只获取该帖子的评论
if ( postId ) {
console . log ( 'Fetching comments for specific post:' , postId ) ;
params . append ( 'post_id' , postId ) ;
} else {
console . log ( 'Fetching all comments (no specific post)' ) ;
}
params . append ( 'limit' , pageSize . toString ( ) ) ;
params . append ( 'offset' , '0' ) ;
if ( platformFilter !== 'all' ) {
params . append ( 'platform' , platformFilter ) ;
}
if ( statusFilter !== 'all' ) {
params . append ( 'status' , statusFilter ) ;
}
if ( sentimentFilter !== 'all' ) {
params . append ( 'sentiment' , sentimentFilter ) ;
}
if ( replyStatusFilter !== 'all' ) {
params . append ( 'reply_status' , replyStatusFilter ) ;
}
if ( searchQuery ) {
params . append ( 'search' , searchQuery ) ;
}
console . log ( 'Fetching initial comments with params:' , params . toString ( ) ) ;
// 从后端API获取评论数据
const response = await fetch ( ` http://localhost:4000/api/comments? ${ params . toString ( ) } ` , {
method : 'GET' ,
headers : {
'Content-Type' : 'application/json' ,
'Authorization' : ` Bearer ${ token } `
}
} ) ;
if ( ! response . ok ) {
throw new Error ( ` API request failed with status: ${ response . status } ` ) ;
}
const data = await response . json ( ) ;
if ( data && Array . isArray ( data . comments ) ) {
// 转换API返回的数据为前端需要的格式
const frontendComments : Comment [ ] = data . comments . map ( ( apiComment : ApiComment ) = > {
// 计算情感分析结果
let sentiment : 'positive' | 'negative' | 'neutral' | 'mixed' = 'neutral' ;
if ( apiComment . sentiment_score > 0.3 ) {
sentiment = 'positive' ;
} else if ( apiComment . sentiment_score < - 0.3 ) {
sentiment = 'negative' ;
}
// 检测语言
const language = detectLanguage ( apiComment . content ) ;
// 使用API返回的post数据中的platform, 如果没有则使用post对象中的platform
const platformFromApi = apiComment . post ? . platform || ( post ? . platform || 'unknown' ) ;
console . log ( 'Comment platform from API:' , platformFromApi , 'Post platform:' , post ? . platform ) ;
// 将API返回的平台值映射到Comment类型中定义的平台值
let mappedPlatform : Comment [ 'platform' ] = 'facebook' ; // 默认值
// 映射平台值
switch ( platformFromApi . toLowerCase ( ) ) {
case 'facebook' :
mappedPlatform = 'facebook' ;
break ;
case 'instagram' :
mappedPlatform = 'instagram' ;
break ;
case 'linkedin' :
mappedPlatform = 'linkedin' ;
break ;
case 'youtube' :
mappedPlatform = 'youtube' ;
break ;
case 'threads' :
mappedPlatform = 'threads' ;
break ;
case 'twitter' : // 添加对Twitter的支持
mappedPlatform = 'twitter' ; // 直接映射到twitter
break ;
case 'xiaohongshu' :
case '小红书' :
mappedPlatform = 'xiaohongshu' ;
break ;
default :
// 如果无法映射,使用默认值
console . warn ( ` Unknown platform: ${ platformFromApi } , using default: facebook ` ) ;
mappedPlatform = 'facebook' ;
}
return {
id : apiComment.comment_id ,
content : apiComment.content ,
author : apiComment.user_profile?.full_name || 'Anonymous User' ,
authorType : 'user' , // 默认为普通用户
platform : mappedPlatform , // 使用映射后的平台值
timestamp : apiComment.created_at ,
sentiment : sentiment ,
status : 'approved' , // 默认状态
replyStatus : 'none' ,
language : language as Comment [ 'language' ] ,
articleTitle : apiComment.post?.title || 'Untitled Post' ,
postAuthor : post?.author || 'Unknown Author' ,
postAuthorType : 'kol' , // 默认为KOL
url : apiComment.post?.post_url || '#'
} ;
} ) ;
setComments ( frontendComments ) ;
setTotalComments ( data . count || frontendComments . length ) ;
setOffset ( frontendComments . length ) ;
setHasMore ( frontendComments . length === pageSize ) ;
console . log ( 'Initial comments loaded:' , frontendComments . length ) ;
} else {
// 如果没有评论数据
setComments ( [ ] ) ;
setTotalComments ( 0 ) ;
setHasMore ( false ) ;
}
] ;
setComments ( mockComments ) ;
setTotalComments ( mockComments . length ) ;
setLoading ( false ) ;
} catch ( error ) {
console . error ( 'Error fetching comments:' , error ) ;
setError ( 'Failed to fetch comments. Please try again later.' ) ;
setLoading ( false ) ;
// 如果获取评论失败,使用空数组
setComments ( [ ] ) ;
setTotalComments ( 0 ) ;
setHasMore ( false ) ;
}
} ;
console . log ( 'Fetching initial comments data...' ) ;
fetchComments ( ) ;
} , [ postId , currentPage , pageSize , statusFilter , platformFilter , sentimentFilter ] ) ;
// 组件卸载时清理
return ( ) = > {
if ( observer . current ) {
observer . current . disconnect ( ) ;
}
} ;
} , [ postId , pageSize , statusFilter , platformFilter , sentimentFilter , replyStatusFilter , searchQuery , post ] ) ;
// 获取Supabase会话和token的函数
const getAuthToken = async ( ) = > {
try {
// 尝试从localStorage中查找Supabase token
// Supabase通常将token存储在以sb-开头的键中
const keys = Object . keys ( localStorage ) ;
const supabaseTokenKey = keys . find ( key = > key . startsWith ( 'sb-' ) && localStorage . getItem ( key ) ? . includes ( 'access_token' ) ) ;
if ( supabaseTokenKey ) {
try {
const supabaseData = JSON . parse ( localStorage . getItem ( supabaseTokenKey ) || '{}' ) ;
return supabaseData . access_token ;
} catch ( e ) {
console . error ( 'Error parsing Supabase token:' , e ) ;
}
}
// 如果没有找到Supabase token, 尝试使用常规token
return localStorage . getItem ( 'token' ) ;
} catch ( error ) {
console . error ( 'Error getting auth token:' , error ) ;
return null ;
}
} ;
// 简单的语言检测
const detectLanguage = ( text : string ) : 'zh-TW' | 'zh-CN' | 'en' = > {
@@ -193,6 +613,62 @@ const CommentList: React.FC<CommentListProps> = () => {
navigate ( '/posts' ) ;
} ;
// 根据平台类型返回相应的图标和名称
const getPlatformIcon = ( platform : string ) = > {
// 确保platform是字符串并转换为小写
const platformLower = ( platform || '' ) . toLowerCase ( ) ;
switch ( platformLower ) {
case 'facebook' :
return {
icon : < Facebook className = "w-5 h-5 text-blue-600" / > ,
name : 'Facebook'
} ;
case 'instagram' :
return {
icon : < Instagram className = "w-5 h-5 text-pink-600" / > ,
name : 'Instagram'
} ;
case 'linkedin' :
return {
icon : < Linkedin className = "w-5 h-5 text-blue-800" / > ,
name : 'LinkedIn'
} ;
case 'youtube' :
return {
icon : < Youtube className = "w-5 h-5 text-red-600" / > ,
name : 'YouTube'
} ;
case 'threads' :
return {
icon : < MessageSquare className = "w-5 h-5 text-black" / > ,
name : 'Threads'
} ;
case 'twitter' : // 添加对Twitter的支持
return {
icon : < MessageSquare className = "w-5 h-5 text-blue-400" / > ,
name : 'Twitter'
} ;
case 'xiaohongshu' :
case '小红书' :
return {
icon : < div className = "flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-red-500 rounded-full" > 小 < / div > ,
name : '小红书'
} ;
case 'tiktok' :
return {
icon : < div className = "flex items-center justify-center w-5 h-5 text-xs font-bold text-white bg-black rounded-full" > T < / div > ,
name : 'TikTok'
} ;
default :
console . log ( 'Unknown platform:' , platform ) ;
return {
icon : < MessageSquare className = "w-5 h-5 text-gray-600" / > ,
name : platform || 'Unknown'
} ;
}
} ;
// 显示加载状态
if ( loading ) {
return (
@@ -235,24 +711,22 @@ const CommentList: React.FC<CommentListProps> = () => {
return (
< div className = "flex flex-1 overflow-hidden" >
< div className = "flex flex-col flex-1 overflow-hidden" >
< div className = "bg-white p-4 border-b flex items-center justify-between" >
< div className = "flex items-center justify-between p-4 bg-white border-b " >
< div className = "flex items-center" >
{ postId && (
< button
onClick = { handleBackToPosts }
className = "mr-4 p-1 hover:bg-gray-100 rounded-full transition-colors duration-2 00"
className = "p-1 mr-4 transition-colors duration-200 rounded-full hover:bg-gray-1 00"
>
< ArrowLeft className = "h -5 w -5 text-gray-500" / >
< ArrowLeft className = "w -5 h -5 text-gray-500" / >
< / button >
) }
< h2 className = "text-lg font-semibold" >
{ post ? ` ${ post . title } 的评论 ` : '所有评论' }
{ postId ? ( post ? ` " ${ post . title } " 的评论` : '加载帖子中...' ) : '所有评论' }
< / h2 >
{ post && (
< span className = "ml-2 text-sm text-gray-500" >
( { totalComments } 条 评 论 )
< / span >
) }
< / div >
< div className = "flex items-center" >
@@ -260,7 +734,7 @@ const CommentList: React.FC<CommentListProps> = () => {
< input
type = "text"
placeholder = "搜索评论..."
className = "px-4 py-2 border rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
className = "px-4 py-2 text-sm border rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
value = { searchQuery }
onChange = { ( e ) = > setSearchQuery ( e . target . value ) }
/ >
@@ -271,9 +745,9 @@ const CommentList: React.FC<CommentListProps> = () => {
showFilters ? 'bg-blue-100 text-blue-700' : 'hover:bg-gray-100'
} ` }
>
< Filter className = "h -4 w -4 mr-1" / >
< Filter className = "w -4 h -4 mr-1" / >
筛 选
< ChevronDown className = "h -4 w -4 ml-1" / >
< ChevronDown className = "w -4 h -4 ml-1" / >
< / button >
< / div >
< / div >
@@ -342,7 +816,9 @@ const CommentList: React.FC<CommentListProps> = () => {
{ /* Mobile comment list */ }
< div className = "block md:hidden" >
< div className = "space-y-4" >
{ comments . map ( ( comment ) = > (
{ comments . map ( ( comment ) = > {
const platformInfo = getPlatformIcon ( comment . platform ) ;
return (
< div
key = { comment . id }
className = "overflow-hidden bg-white rounded-lg shadow cursor-pointer"
@@ -351,8 +827,8 @@ const CommentList: React.FC<CommentListProps> = () => {
< div className = "p-4" >
< div className = "flex items-start justify-between mb-3" >
< div className = "flex items-center" >
< Facebook className = "w-5 h-5 text-blue-600" / >
< span className = "ml-2 text-sm font-medium" > Facebook < / span >
{ platformInfo . icon }
< span className = "ml-2 text-sm font-medium" > { platformInfo . name } < / span >
< / div >
< / div >
< p className = "mb-2 text-sm text-gray-900" > { comment . content } < / p >
@@ -364,7 +840,8 @@ const CommentList: React.FC<CommentListProps> = () => {
< / div >
< / div >
< / div >
) ) }
) ;
} ) }
< / div >
< / div >
@@ -385,7 +862,9 @@ const CommentList: React.FC<CommentListProps> = () => {
< / tr >
< / thead >
< tbody className = "bg-white divide-y divide-gray-200" >
{ comments . map ( ( comment ) = > (
{ comments . map ( ( comment ) = > {
const platformInfo = getPlatformIcon ( comment . platform ) ;
return (
< tr
key = { comment . id }
className = "cursor-pointer hover:bg-gray-50"
@@ -394,9 +873,9 @@ const CommentList: React.FC<CommentListProps> = () => {
< td className = "px-6 py-4 whitespace-nowrap" >
< div className = "flex flex-col" >
< div className = "flex items-center" >
< Facebook className = "w-5 h-5 text-blue-600" / >
{ platformInfo . icon }
< span className = "ml-2 text-sm text-gray-900" >
Facebook
{ platformInfo . name }
< / span >
< / div >
< / div >
@@ -477,7 +956,8 @@ const CommentList: React.FC<CommentListProps> = () => {
< / button >
< / td >
< / tr >
) ) }
) ;
} ) }
< / tbody >
< / table >
< / div >
@@ -489,6 +969,26 @@ const CommentList: React.FC<CommentListProps> = () => {
< CommentPreview comment = { selectedComment } onClose = { ( ) = > setSelectedComment ( null ) } / >
< / div >
) }
{ /* 在评论列表的底部添加无限滚动加载指示器 */ }
< div
ref = { loadMoreRef }
style = { {
textAlign : 'center' ,
padding : '20px 0' ,
visibility : loading || ! hasMore ? 'hidden' : 'visible'
} }
>
{ isLoadingMore && (
< div className = "py-4" >
< Spin tip = "Loading more comments..." / >
< / div >
) }
{ ! hasMore && comments . length === 0 && ! loading && (
< Empty description = "No comments found" / >
) }
< / div >
< / div >
) ;
} ;