完善圈子
This commit is contained in:
@@ -33,6 +33,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
|
margin-top: 10px;
|
||||||
.text-area-outer-box{
|
.text-area-outer-box{
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
@@ -46,13 +47,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.comment-list-wrapper{
|
.comment-list-wrapper{
|
||||||
.comment-list-item{
|
.comment-item-wrapper{
|
||||||
margin-top: 30px;
|
margin-top: 30px;
|
||||||
.ope-btn-group{
|
width: 100%;
|
||||||
gap: 16px;
|
.comment-detail-wrapper{
|
||||||
color: gray;
|
flex: 1;
|
||||||
.reply-btn{
|
}
|
||||||
|
.comment-content{
|
||||||
|
margin: 10px 0;
|
||||||
|
}
|
||||||
|
.comment-bottom-wrapper{
|
||||||
|
color: rgb(211, 211, 211);
|
||||||
|
.bottom-btn{
|
||||||
|
margin-left: 10px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
&:hover, &.active{
|
||||||
|
color: #1677ff;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,26 +1,16 @@
|
|||||||
import { Button, Input } from 'antd'
|
import { Button, Input, Upload, Image } from 'antd'
|
||||||
import { useState, useEffect, FC } from 'react'
|
import { useState, useEffect, FC } from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from 'react-redux'
|
||||||
import { commentSave, getCommentList } from '../../service'
|
import { commentSave, getCommentList } from '../../service'
|
||||||
|
import { CommentOutlined, FileImageOutlined, PlusOutlined, SmileOutlined } from '@ant-design/icons'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
import { CommentOutlined, FileImageOutlined, SmileOutlined } from '@ant-design/icons'
|
|
||||||
|
|
||||||
const CommentList: FC<any> = props => {
|
const CommentInput = ({ momentId, getList, replyType, targetId = '' }) => {
|
||||||
|
const [comment, setComment] = useState<string>('')
|
||||||
const { userInfo } = useSelector(store => store.userInfo)
|
const { userInfo } = useSelector(store => store.userInfo)
|
||||||
const { momentId } = props
|
const userInfoStorage = localStorage.getItem('userInfo')
|
||||||
const [replyList, setReplyList] = useState([])
|
const { tokenValue = '' } = userInfoStorage ? JSON.parse(userInfoStorage) : {}
|
||||||
const [comment, setComment] = useState('')
|
const [imgList, setImgList] = useState([])
|
||||||
const getList = async () => {
|
|
||||||
const res = await getCommentList({ id: momentId })
|
|
||||||
if (res.success && res.data) {
|
|
||||||
setReplyList(res.data)
|
|
||||||
} else {
|
|
||||||
setReplyList([])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
useEffect(() => {
|
|
||||||
getList()
|
|
||||||
}, [])
|
|
||||||
|
|
||||||
const changeComment = e => {
|
const changeComment = e => {
|
||||||
setComment(e.target.value)
|
setComment(e.target.value)
|
||||||
@@ -29,83 +19,232 @@ const CommentList: FC<any> = props => {
|
|||||||
const saveComment = () => {
|
const saveComment = () => {
|
||||||
const params = {
|
const params = {
|
||||||
momentId,
|
momentId,
|
||||||
replyType: 2,
|
replyType,
|
||||||
content: comment,
|
content: comment,
|
||||||
targetId: 12
|
targetId
|
||||||
|
}
|
||||||
|
if (imgList.length) {
|
||||||
|
params.picUrlList = imgList.map(item => item.response.data)
|
||||||
}
|
}
|
||||||
commentSave(params).then(() => {
|
commentSave(params).then(() => {
|
||||||
|
setComment('')
|
||||||
|
setImgList([])
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const uploadButton = (
|
||||||
|
<button style={{ border: 0, background: 'none' }} type='button'>
|
||||||
|
<PlusOutlined />
|
||||||
|
</button>
|
||||||
|
)
|
||||||
|
const handleChange = ({ fileList }) => {
|
||||||
|
setImgList(fileList)
|
||||||
|
}
|
||||||
return (
|
return (
|
||||||
<div className='comment-list-box'>
|
<div className='comment-wrapper'>
|
||||||
<div className='top-arrow'></div>
|
<img src={userInfo?.avatar} className='avatar' />
|
||||||
<div className='comment-number'>评论 {replyList.length}</div>
|
<div className='text-area-outer-box'>
|
||||||
<div className='comment-wrapper'>
|
<div className='text-area-box'>
|
||||||
<img src={userInfo?.avatar} className='avatar' />
|
<Input.TextArea
|
||||||
<div className='text-area-outer-box'>
|
onChange={changeComment}
|
||||||
<div className='text-area-box'>
|
placeholder='和平发言'
|
||||||
<Input.TextArea
|
style={{ border: 'none', paddingLeft: 0 }}
|
||||||
onChange={changeComment}
|
maxLength={1000}
|
||||||
placeholder='和平发言'
|
value={comment}
|
||||||
style={{ border: 'none', paddingLeft: 0 }}
|
/>
|
||||||
/>
|
<Upload
|
||||||
|
name='uploadFile'
|
||||||
|
action='/oss/upload'
|
||||||
|
listType='picture-card'
|
||||||
|
fileList={imgList}
|
||||||
|
withCredentials
|
||||||
|
headers={{
|
||||||
|
satoken: 'jichi ' + tokenValue
|
||||||
|
}}
|
||||||
|
data={{
|
||||||
|
bucket: 'user',
|
||||||
|
objectName: 'icon'
|
||||||
|
}}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
{imgList.length >= 8 || imgList.length === 0 ? null : uploadButton}
|
||||||
|
</Upload>
|
||||||
|
</div>
|
||||||
|
<div className='comment-bottom'>
|
||||||
|
<div className='icon-box flex'>
|
||||||
|
<div style={{ marginRight: 20 }}>
|
||||||
|
<SmileOutlined />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Upload
|
||||||
|
name='uploadFile'
|
||||||
|
className='avatar-uploader'
|
||||||
|
accept='image/*'
|
||||||
|
showUploadList={false}
|
||||||
|
withCredentials
|
||||||
|
action='/oss/upload'
|
||||||
|
headers={{
|
||||||
|
satoken: 'jichi ' + tokenValue
|
||||||
|
}}
|
||||||
|
data={{
|
||||||
|
bucket: 'user',
|
||||||
|
objectName: 'icon'
|
||||||
|
}}
|
||||||
|
onChange={handleChange}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<FileImageOutlined />
|
||||||
|
{/* <span style={{ marginLeft: '8px' }}>图片</span> */}
|
||||||
|
</div>
|
||||||
|
</Upload>
|
||||||
|
{/* <FileImageOutlined /> */}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='comment-bottom'>
|
<div className='submit-btn-box flex'>
|
||||||
<div className='icon-box flex'>
|
<div className='text-num-box' style={{ marginRight: 20 }}>
|
||||||
<div style={{ marginRight: 20 }}>
|
{comment.length}/1000
|
||||||
<SmileOutlined />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<FileImageOutlined />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className='submit-btn-box flex'>
|
|
||||||
<div className='text-num-box'>1/1000</div>
|
|
||||||
<Button onClick={saveComment} type='primary'>
|
|
||||||
发送
|
|
||||||
</Button>
|
|
||||||
</div>
|
</div>
|
||||||
|
<Button onClick={saveComment} type='primary'>
|
||||||
|
发送
|
||||||
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatDistanceToNow(date) {
|
||||||
|
if (!date) return
|
||||||
|
const delta = Math.abs(Date.now() - date)
|
||||||
|
|
||||||
|
if (delta < 30 * 1000) {
|
||||||
|
return '刚刚'
|
||||||
|
} else if (delta < 5 * 60 * 1000) {
|
||||||
|
return Math.round(delta / 1000) + '秒前'
|
||||||
|
} else if (delta < 60 * 60 * 1000) {
|
||||||
|
return Math.round(delta / 60000) + '分钟前'
|
||||||
|
} else if (delta < 24 * 60 * 60 * 1000) {
|
||||||
|
return Math.round(delta / 3600000) + '小时前'
|
||||||
|
} else if (delta < 7 * 24 * 60 * 60 * 1000) {
|
||||||
|
return Math.round(delta / 86400000) + '天前'
|
||||||
|
} else {
|
||||||
|
return new Date(date).toLocaleDateString()
|
||||||
|
return '很久之前'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const CommentItem = ({
|
||||||
|
momentId,
|
||||||
|
id,
|
||||||
|
avatar,
|
||||||
|
userName,
|
||||||
|
content,
|
||||||
|
createdTime,
|
||||||
|
replyType,
|
||||||
|
getList,
|
||||||
|
children,
|
||||||
|
picUrlList
|
||||||
|
}) => {
|
||||||
|
const [active, setActive] = useState(false)
|
||||||
|
const toggleActive = () => {
|
||||||
|
setActive(!active)
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<div className={`comment-item-wrapper`}>
|
||||||
|
<div className='flex align-top'>
|
||||||
|
<img src={avatar} className='avatar' alt='头像' />
|
||||||
|
<div className='comment-detail-wrapper'>
|
||||||
|
<div className='title'>{userName}</div>
|
||||||
|
<div className='comment-content'>{content}</div>
|
||||||
|
{picUrlList?.length && (
|
||||||
|
<Image.PreviewGroup items={picUrlList}>
|
||||||
|
<div className='comment-img-list'>
|
||||||
|
{picUrlList.map((t: string) => (
|
||||||
|
<Image key={t} width={90} src={t} />
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</Image.PreviewGroup>
|
||||||
|
)}
|
||||||
|
<div className='comment-bottom-wrapper flex'>
|
||||||
|
<div>{formatDistanceToNow(createdTime) || '12小时前'}</div>
|
||||||
|
<div onClick={toggleActive} className={`bottom-btn ${active ? 'active' : ''}`}>
|
||||||
|
<CommentOutlined />
|
||||||
|
<span style={{ marginLeft: 5 }}>{replyType === 1 ? '评论' : '回复'}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{active && (
|
||||||
|
<CommentInput momentId={momentId} getList={getList} targetId={id} replyType={2} />
|
||||||
|
)}
|
||||||
|
{children?.length
|
||||||
|
? children?.map(item => {
|
||||||
|
return <CommentItem key={item.id} {...item} getList={getList} />
|
||||||
|
})
|
||||||
|
: ''}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function flattenNestedObjects(items) {
|
||||||
|
const result = []
|
||||||
|
|
||||||
|
function traverse(items) {
|
||||||
|
items.forEach(item => {
|
||||||
|
// 创建一个新对象来存储当前项的属性(除了 children)
|
||||||
|
const flatItem = {}
|
||||||
|
for (const key in item) {
|
||||||
|
if (key !== 'children') {
|
||||||
|
flatItem[key] = item[key]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将扁平化的对象添加到结果数组中
|
||||||
|
result.push(flatItem)
|
||||||
|
|
||||||
|
// 如果还有 children,则递归调用 traverse
|
||||||
|
if (item.children) {
|
||||||
|
traverse(item.children)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 从顶层对象开始遍历
|
||||||
|
traverse(items)
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
const CommentList: FC<any> = props => {
|
||||||
|
const { momentId, replyCount } = props
|
||||||
|
const [replyList, setReplyList] = useState<any[]>([])
|
||||||
|
const getList = async () => {
|
||||||
|
const res = await getCommentList({ id: momentId })
|
||||||
|
if (res.success && res.data) {
|
||||||
|
const data = res.data.map(item => {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
children: flattenNestedObjects(item.children || [])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
setReplyList(data)
|
||||||
|
} else {
|
||||||
|
setReplyList([])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
getList()
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='comment-list-box'>
|
||||||
|
<div className='top-arrow'></div>
|
||||||
|
<div className='comment-number'>评论 {replyCount}</div>
|
||||||
|
<CommentInput momentId={momentId} getList={getList} targetId={momentId} replyType={1} />
|
||||||
<div className='comment-list-wrapper'>
|
<div className='comment-list-wrapper'>
|
||||||
{replyList.map((item: Record<string, any>) => {
|
{replyList.map((item: Record<string, any>) => {
|
||||||
return (
|
return <CommentItem key={item.id} momentId={momentId} getList={getList} {...item} />
|
||||||
<div key={item.id} className='comment-list-item flex align-top'>
|
|
||||||
<img src={item.avatar} className='avatar' />
|
|
||||||
<div>
|
|
||||||
<div>{item.userName}</div>
|
|
||||||
<div style={{ margin: '10px 0' }}>{item.content}</div>
|
|
||||||
<div className='ope-btn-group flex'>
|
|
||||||
<div>12小时前</div>
|
|
||||||
<div className='reply-btn'>
|
|
||||||
<CommentOutlined />
|
|
||||||
评论
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{item.children?.length &&
|
|
||||||
item.children.map(child => {
|
|
||||||
return (
|
|
||||||
<div key={child.id} className='comment-list-item flex align-top'>
|
|
||||||
<img src={child.avatar} className='avatar' />
|
|
||||||
<div>
|
|
||||||
<div>{child.userName}</div>
|
|
||||||
<div style={{ margin: '10px 0' }}>{child.content}</div>
|
|
||||||
<div className='ope-btn-group flex'>
|
|
||||||
<div>12小时前</div>
|
|
||||||
<div className='reply-btn'>
|
|
||||||
<CommentOutlined />
|
|
||||||
评论
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -106,6 +106,8 @@ const Circle = () => {
|
|||||||
}
|
}
|
||||||
const res = await saveMoment(params)
|
const res = await saveMoment(params)
|
||||||
if (res.success) {
|
if (res.success) {
|
||||||
|
setComment('')
|
||||||
|
setImgList([])
|
||||||
getMomentList()
|
getMomentList()
|
||||||
return message.success('发布成功')
|
return message.success('发布成功')
|
||||||
}
|
}
|
||||||
@@ -154,6 +156,7 @@ const Circle = () => {
|
|||||||
className='top-text-area'
|
className='top-text-area'
|
||||||
onFocus={() => toggleFocus(false)}
|
onFocus={() => toggleFocus(false)}
|
||||||
onBlur={() => toggleFocus(true)}
|
onBlur={() => toggleFocus(true)}
|
||||||
|
value={comment}
|
||||||
/>
|
/>
|
||||||
<Upload
|
<Upload
|
||||||
name='uploadFile'
|
name='uploadFile'
|
||||||
@@ -228,7 +231,7 @@ const Circle = () => {
|
|||||||
description={item.content}
|
description={item.content}
|
||||||
/>
|
/>
|
||||||
{item.picUrlList?.length && (
|
{item.picUrlList?.length && (
|
||||||
<Image.PreviewGroup items={previewList.list}>
|
<Image.PreviewGroup items={item.picUrlList}>
|
||||||
<div className='img-list'>
|
<div className='img-list'>
|
||||||
{item.picUrlList.map((t: string) => (
|
{item.picUrlList.map((t: string) => (
|
||||||
<Image key={t} width={110} src={t} />
|
<Image key={t} width={110} src={t} />
|
||||||
@@ -237,7 +240,18 @@ const Circle = () => {
|
|||||||
</Image.PreviewGroup>
|
</Image.PreviewGroup>
|
||||||
)}
|
)}
|
||||||
<div className='card-footer'>
|
<div className='card-footer'>
|
||||||
<a key='share' className='footer-item'>
|
<a
|
||||||
|
key='share'
|
||||||
|
className='footer-item'
|
||||||
|
onClick={() =>
|
||||||
|
window.open(
|
||||||
|
'https://service.weibo.com/share/share.php?url=' +
|
||||||
|
encodeURIComponent(location.href) +
|
||||||
|
'&title=' +
|
||||||
|
item.content
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
<ShareAltOutlined />
|
<ShareAltOutlined />
|
||||||
<span style={{ marginLeft: 8 }}>分享</span>
|
<span style={{ marginLeft: 8 }}>分享</span>
|
||||||
</a>
|
</a>
|
||||||
@@ -253,7 +267,9 @@ const Circle = () => {
|
|||||||
</span>
|
</span>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{currentReplyCommentId === item.id && <CommentList momentId={item.id} />}
|
{currentReplyCommentId === item.id && (
|
||||||
|
<CommentList momentId={item.id} replyCount={item.replyCount} />
|
||||||
|
)}
|
||||||
</Card>
|
</Card>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
|
Reference in New Issue
Block a user