此篇文章阐述评论的最新、最热之间的tab标签切换(包括当前所在tab标签的高亮显示问题);当前评论的删除;除此之外还延伸了用户的评论实时发布功能。其中最新tab标签所展示的内容是根据当前评论点赞数来进行排序,点赞数量越多,此条评论越靠前;最新tab标签页所展示的内容是根据当前评论发布时间来进行排序,随着时间的推移,此条评论越靠前。
话不多说,上代码:
1.安装必要插件
classnames、dayjs、lodash、node-sass、sass-loader、uuid、lodash
安装命令如下:
classnames:npm install classnames或者 yarn add classnames
dayjs:npm install dayjs 或者 yarn add dayjs
lodash:npm install lodash 或者 yarn add lodash
安装scss相关包:npm install sass、npm install sass-resources-loader
uuid:npm install uuid或者 yarn add uuid
lodash:npm install lodash或者 yarn add lodash
安装好后可到package.json中查看相应版本信息
2.App.scss文件
.app {
width:100%;
height: 100%;
}
.reply-navigation{
.nav-bar{
list-style: none;
display: flex;
text-align: center;
height:20px;
line-height: 20px;
.nav-title{
margin-right:30px;
.nav-title-text{
font-size:25px;
font-weight: 700;
}
}
.nav-sort{
color:darkgray;
.nav-item{
width:35px;
cursor: pointer;
}
&>:first-child{
margin-right:35px;
border-right:2px solid darkgray;
padding-right:35px;
}
.active{
color:orange;
}
}
}
}
.reply-wrap{
height:100%;
background: url('./images/bg.jpg') no-repeat;
background-size: 100% 100%;
padding:20px;
.box-normal{
display: flex;
justify-content: flex-start;
margin-bottom: 20px;
.reply-box-avator{
.bili-avator{
width:80px;
height: 80px;
.bili-avator-img{
border-radius: 50%;
width:100%;
height: 100%;
}
}
}
.reply-box-wrap{
display: flex;
height: 80px;
margin-left:20px;
overflow: hidden;
.reply-box-textarea{
width: 400px;
margin-top: 15px;
height: 50px;
line-height: 50px;
text-align: center;
background-color:#E5E5E5;
}
.reply-box-send{
width:80px;
height: 55px;
line-height: 55px;
margin-top: 15px;
background-color: cornflowerblue;
color:#fff;
text-align: center;
margin-left:15px;
border-radius: 4px;
cursor: pointer;
.send-text{
font-size: 20px;
border: none;
}
}
}
}
.reply-list{
.reply-item{
width:100%;
height: 130px;
display: flex;
justify-content: flex-start;
margin-top:10px;
.root-reply-avator{
width:5%;
.bili-avator{
.bili-avator-img{
width:60px;
height:60px;
border-radius:50%;
}
}
}
.content-wrap{
width:95%;
overflow: hidden;
border-bottom: 2px solid darkgray;
.user-info{
.user-name{
font-size: 16px;
color:darkgray;
font-weight: 500;
}
}
.root-reply{
margin-top:25px;
.reply-content{
font-size: 20px;
color:chocolate;
font-weight: 700;
}
.reply-info{
font-size: 14px;
color:darkgray;
font-weight: 500;
margin-top:15px;
.reply-time{
margin-right:25px;
}
.reply-count{
margin-right:25px;
}
.reply-delete{
cursor: pointer;
}
.reply-delete:hover{
color:cornflowerblue;
}
}
}
}
}
}
}
3.App.js文件
import { useRef, useState } from 'react'
import './App.scss'
import avator from './images/avator.jpg'
import _ from 'lodash'
import classNames from 'classnames'
import { v4 as uuidV4 } from 'uuid'
import dayjs from 'dayjs'
//评论列表数据
const list = [
{
rpid: 1, //评论id
user: {
//用户信息
uid: '13258165',
avator: 'http://toutiao.itheima.net/resources/images/9.jpg',
uname: '周杰伦',
},
content: '哎呦,不错哦', //评论内容
ctime: '10-25 12:15', //评论时间
like: 88,
},
{
rpid: 2, //评论id
user: {
//用户信息
uid: '36080105',
avator: 'http://toutiao.itheima.net/resources/images/98.jpg',
uname: '许嵩',
},
content: '我寻你千百度,日出到迟暮', //评论内容
ctime: '04-12 08:45', //评论时间
like: 98,
},
{
rpid: 3, //评论id
user: {
//用户信息
uid: '30009257',
avator: 'http://toutiao.itheima.net/resources/images/56.jpg',
uname: '王心凌',
},
content: '或许失败过,但从未认输', //评论内容
ctime: '12-18 19:02', //评论时间
like: 120,
},
{
rpid: 4, //评论id
user: {
//用户信息
uid: '19858625',
avator: 'http://toutiao.itheima.net/resources/images/67.jpg',
uname: '徐凯',
},
content: '没有永远的敌人', //评论内容
ctime: '11-20 20:15', //评论时间
like: 120,
},
{
rpid: 5, //评论id
user: {
//用户信息
uid: '30009257',
avator: 'http://toutiao.itheima.net/resources/images/37.jpg',
uname: '杨幂',
},
content: '不只玫瑰有爱意', //评论内容
ctime: '07-20 20:15', //评论时间
like: 456,
},
{
rpid: 6, //评论id
user: {
//用户信息
uid: '30009257',
avator: 'http://toutiao.itheima.net/resources/images/28.jpg',
uname: '徐良',
},
content: '女骑士', //评论内容
ctime: '03-14 20:15', //评论时间
like: 253,
},
]
//当前登录用户信息
const user = {
//用户id
uid: '30009257',
//用户头像
avator,
//用户昵称
uname: '徐凯工作室',
}
//导航tab数组
const tabs = [
{ type: 'hot', text: '最热' },
{ type: 'time', text: '最新' },
]
const App = () => {
const [commentList, setCommentList] = useState(list)
//删除事件
const deleteHandle = (id) => {
//根据id删除
setCommentList(commentList.filter((item) => item.rpid !== id))
console.log(id)
}
//存储type
const [type, setType] = useState('hot')
//tabs标签切换事件,根据 type切换
const typeHandlerChange = (type) => {
setType(type)
console.log(type, '切换tab项')
if (type === 'time') {
//允许在集合commentList中,指定迭代函数ctime进行倒序排序desc
setCommentList(_.orderBy(commentList, 'ctime', 'desc'))
} else {
//允许在集合commentList中,指定迭代函数like进行倒序排序desc
setCommentList(_.orderBy(commentList, 'like', 'desc'))
}
}
const [content, setContent] = useState('')
const inputRef = useRef(null)
const sendHandler = () => {
setCommentList([
...commentList,
{
rpid: uuidV4(), //唯一识别id,通过uuid生成器产生随机id
user: {
//用户信息
uid: '30009257',
avator: 'http://toutiao.itheima.net/resources/images/28.jpg',
uname: '徐良',
},
content: content, //评论内容
ctime: dayjs(new Date()).format('MM-DD hh:mm'), //格式化 月-日 时:分
like: 253,
},
])
setContent('') //清空输入框内的值
inputRef.current.focus() //再次聚焦输入框
console.dir(inputRef.current)
}
return (
<div className="app">
{/* 导航 Tab*/}
<div className="reply-navigation">
<ul className="nav-bar">
<li className="nav-title">
<span className="nav-title-text">评论</span>
{/* 评论数量 */}
<span className="total-reply">{10}</span>
</li>
{/* 高亮类名:active */}
<li className="nav-sort">
{tabs.map((item) => (
<span
className={classNames('nav-item', {
active: type === item.type,
})}
key={item.type}
onClick={() => typeHandlerChange(item.type)}
>
{item.text}
</span>
))}
</li>
</ul>
</div>
<div className="reply-wrap">
{/* 发表评论 */}
<div className="box-normal">
{/* 当前用户头像 */}
<div className="reply-box-avator">
<div className="bili-avator">
<img
className="bili-avator-img"
alt="用户头像"
src={user.avator}
/>
</div>
</div>
<div className="reply-box-wrap">
{/* 评论框 */}
<textarea
className="reply-box-textarea"
placeholder="发一条友善的评论"
value={content}
onChange={(e) => {
setContent(e.target.value)
}}
ref={inputRef}
></textarea>
{/* 发布按钮 */}
<div className="reply-box-send">
<div className="send-text" onClick={sendHandler}>
发布
</div>
</div>
</div>
</div>
{/* 评论列表 */}
<div className="reply-list">
{/* 评论项 */}
{commentList.map((item) => (
<div className="reply-item" key={item.rpid}>
{/*头像 */}
<div className="root-reply-avator">
<div className="bili-avator">
<img
className="bili-avator-img"
alt=""
src={item.user.avator}
/>
</div>
</div>
<div className="content-wrap">
{/*用户名 */}
<div className="user-info">
<div className="user-name">{item.user.uname}</div>
</div>
{/*评论内容 */}
<div className="root-reply">
<span className="reply-content">{item.content}</span>
<div className="reply-info">
{/*评论时间 */}
<span className="reply-time">{item.ctime}</span>
{/*评论数量 */}
<span className="reply-count">点赞数:{item.like}</span>
{/*删除 */}
{/*删除条件 */}
{user.uid === item.user.uid && (
<span
className="reply-delete"
onClick={() => {
deleteHandle(item.rpid)
}}
>
{'删除'}
</span>
)}
</div>
</div>
</div>
</div>
))}
</div>
</div>
</div>
)
}
export default App
结果展示: