import React, { Component } from "react"; import PropTypes from "prop-types"; import { Icon, Tooltip, Popconfirm, Popover } from "antd"; import dayjs from "dayjs"; import "dayjs/locale/zh-cn"; // import 'dayjs/locale/es'; import relativeTime from "dayjs/plugin/relativeTime"; import intl from "react-intl-universal"; import AvatarHoverCard from "./AvatarHoverCard"; import Comment from "../../Comment"; import CommentInput from "../CommentInput"; import avatar from "../../avatar"; import { renderContent } from "../../helper"; import { IMAGE_SPLIT } from "../../constant"; import "./index.css"; import ImagePreviewer from "../ImagePreviewer/ImagePreviewer"; import AudioPlayer from "../AudioPlayer"; // dayjs.locale("zh-cn"); dayjs.extend(relativeTime); const LOCALES = { "zh-CN": "zh-cn" }; class CommentItem extends Component { constructor(props) { super(props); this.state = { showInput: false, showPreviewer: false, previewerIndex: 0, popoverVisible: false }; this.handleToggleInput = this.handleToggleInput.bind(this); this.renderTextWithReply = this.renderTextWithReply.bind(this); this.showPreviewer = this.showPreviewer.bind(this); this.hidePreviewer = this.hidePreviewer.bind(this); this.handleVisibleChange = this.handleVisibleChange.bind(this); this.handleUserAvaClick = this.handleUserAvaClick.bind(this); } showPreviewer(index) { this.setState({ showPreviewer: true, previewerIndex: index }); } hidePreviewer() { this.setState({ showPreviewer: false }); } handleToggleInput() { this.setState({ showInput: !this.state.showInput }); } handleVisibleChange(visible) { this.setState({ popoverVisible: visible }); } handleUserAvaClick() { const { user_id } = this.props; const { userAvaClick } = this.props.app; if (userAvaClick) { userAvaClick(user_id); } } renderTextWithReply(text, content) { let newText = text; const { reply } = content; if (reply) { // newText = `${newText} //@<a href="/${reply.user_id}">${ // reply.user_name // }</a> ${reply.content}`; newText = `${newText} //@${reply.user_name} ${reply.content}`; // newText = ( // <span> // {newText} // @<a href={`/${reply.user_id}`}>{reply.user_name}</a>{reply.content} // </span> // ) if (reply.reply) { return this.renderTextWithReply(newText, reply); } } return newText; } render() { const { commentId, replyId, content, action, showReply, onShowReply, app, user_id, page } = this.props; const { medias, is_speak: isSpeak } = content; const { locale, showHoverCard, showEdit } = this.props.app; const { showInput } = this.state; let newContent = content.content; let images = ""; if (newContent.indexOf(IMAGE_SPLIT) !== -1) { newContent = newContent.split(IMAGE_SPLIT); images = newContent.pop(); newContent = newContent.join(""); } const imageList = images.split(","); // 在3, 7前需要换行 const needClear = imageList.length === 5 || imageList.length === 6 || imageList.length === 9; const imgs = [...imageList]; if (needClear) { if (imgs.length > 6) { imgs.splice(3, 0, { type: "divider" }); imgs.splice(7, 0, { type: "divider" }); } else { imgs.splice(3, 0, { type: "divider" }); } } const IconColor = content.favor_count > 0 ? "#71C135" : "#4a90e2"; const showDivider = content.reply_count || (showEdit && app.userId === content.user_id) || app.userId === content.user_id; return ( <div className="comment-item-box"> <div className="comment-item-left"> {showHoverCard ? ( <Popover content={ <AvatarHoverCard {...this.props.app} user_id={user_id} handleVisibleChange={this.handleVisibleChange} image={content.user_avatar || avatar} /> } // placement={this.props.placement} // trigger="click" visible={this.state.popoverVisible} onVisibleChange={this.handleVisibleChange} overlayClassName="avatar-hover-card-overlay" > <div className="comment-item-avatar" style={{ backgroundImage: `url(${content.user_avatar || avatar})` }} onClick={this.handleUserAvaClick} /> </Popover> ) : ( <div className="comment-item-avatar" style={{ backgroundImage: `url(${content.user_avatar || avatar})` }} onClick={this.handleUserAvaClick} /> )} </div> <div className="comment-item-right"> <div> {/* <a href={`/${content.user_id}`}> {content.user_name || "暂无昵称"} </a> */} <strong onClick={this.handleUserAvaClick} className="comment-item-name" style={{ cursor: "pointer" }} > {content.user_name || intl.get("comment.tourist")} </strong> <span className="comment-item-date" style={{ marginLeft: 10 }}> <Tooltip placement="top" title={dayjs(content.created * 1000).format( "YYYY-MM-DD HH:mm:ss" )} > {LOCALES[locale] ? dayjs(content.created * 1000) .locale(LOCALES[locale]) .fromNow() : dayjs(content.created * 1000).fromNow()} </Tooltip> </span> </div> <div className="comment-item-content" dangerouslySetInnerHTML={{ __html: renderContent( this.renderTextWithReply(newContent, content) ) }} /> {isSpeak && ( <div className="comment-item-speak"> <span className="comment-item-speak-message"> [{intl.get("comment.speakComment")}] </span> <div className="comment-item-speak-audio-wrapper"> <AudioPlayer src={medias && medias[0] && medias[0].url} /> </div> </div> )} {// image为空时不渲染comment-item-image imageList.length > 0 && imageList[0] !== "" && ( <div className="comment-item-image"> {!this.state.showPreviewer && imgs.map((item, index) => { if (item.type === "divider") { return ( <div className="comment-item-image-wrapper" key={index}> <div className="comment-img-divider" /> {/* <img src={item} alt={item} className="comment-img" /> */} </div> ); } return ( <div className="comment-item-image-wrapper" key={index} onClick={() => { let i = index; if (needClear) { if (index > 3) { i -= 1; } if (index > 7) { i -= 1; } } this.showPreviewer(i); }} > <div style={{ backgroundImage: `url(${item})` }} className="comment-img-thumbnail" /> {/* <img src={item} alt={item} className="comment-img" /> */} </div> ); })} {this.state.showPreviewer && ( <ImagePreviewer list={imageList} index={this.state.previewerIndex} onFold={this.hidePreviewer} /> )} <div className="clearfix" /> </div> )} <div className="comment-item-bottom"> {content.reply_count ? ( <div> <a className="comment-item-bottom-left" onClick={onShowReply}> {/* {content.reply_count} 条回复 */} {intl.get("reply.totalReply", { total: content.reply_count })} {showReply ? <Icon type="up" /> : <Icon type="down" />} </a> </div> ) : null} {showEdit && !isSpeak && app.userId === content.user_id && ( <i className="comment-item-edit" onClick={() => this.props.app.handleEdit({ action, replyId, commentId, userId: content.user_id, content, replyPage: page }) } /> )} {app.userId === content.user_id && ( <Popconfirm // title="确定要删除吗?" title={intl.get("popConfirm.title")} onConfirm={() => { if (replyId) { app.sDeleteReply(content.id, commentId); return; } app.sDeleteComment(content.id); }} okText={intl.get("popConfirm.ok")} cancelText={intl.get("popConfirm.cancel")} > {/* <a className="comment-item-bottom-right"> {intl.get("popConfirm.delete")} </a> */} <i className="comment-item-delete" /> </Popconfirm> )} {showDivider && <span className="comment-item-divider" />} <div className="comment-item-bottom-right" onClick={() => { if (replyId) { // 如果有 replyId,则说明是评论的回复 app.sReplyFavor(content.id, commentId, content.favored); return; } app.sCommentFavor(content.id, content.favored); }} style={{ color: `${IconColor}` }} > <i className={ content.favored ? "comment-item-like" : "comment-item-unlike" } /> </div> <span> {content.favor_count} </span> <div onClick={this.handleToggleInput} className="comment-item-reply" > {intl.get("comment.reply")} </div> </div> </div> {showInput && ( <CommentInput content={app.children} action={action} replyId={replyId} commentId={commentId} userId={content.user_id} callback={this.handleToggleInput} /> )} </div> ); } } CommentItem.propTypes = { content: PropTypes.object.isRequired, // comment 评论 // reply 评论的回复 // replyToReply 回复的回复 action: PropTypes.oneOf(["comment", "reply", "replyToReply"]), onShowReply: PropTypes.func, showEdit: PropTypes.bool }; CommentItem.defaultProps = { action: "comment", onShowReply: () => {}, showEdit: false }; export default Comment(CommentItem);