评论发布完整篇(react版)

        此篇文章阐述评论的最新、最热之间的tab标签切换(包括当前所在tab标签的高亮显示问题);当前评论的删除;除此之外还延伸了用户的评论实时发布功能。其中最新tab标签所展示的内容是根据当前评论点赞数来进行排序,点赞数量越多,此条评论越靠前;最新tab标签页所展示的内容是根据当前评论发布时间来进行排序,随着时间的推移,此条评论越靠前。

话不多说,上代码:

1.安装必要插件

classnamesdayjslodashnode-sasssass-loaderuuidlodash

安装命令如下:

classnames:npm install classnames或者 yarn add classnames

dayjs:npm install dayjs 或者 yarn add dayjs

lodash:npm install lodash 或者 yarn add lodash

安装scss相关包:npm install sassnpm 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

结果展示:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值