评论发布完整篇(react版)
此篇文章阐述评论的最新、最热之间的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
结果展示:
相关文章:

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

前端window.open的简单使用
JavaScript 中的 Window.open() 用法详解-CSDN博客 window.open("https://www.baidu.com/?tn49055317_12_hao_pg", _blank);...

基于开源软件构建存储解决方案的思考
近来看了一些IBM的存储产品的资料,有一些收获。 依据存储软件和搭配硬件,IBM存储产品的组合,大致分类如下: 自研存储软件,搭配自研专有硬件自研存储软件,搭配通用服务器硬件,比如IBM Storage S…...

【leetcode】动态规划::前缀和(二)
标题:【leetcode】前缀和(二) 水墨不写bug 正文开始: (一) 和为K的子数组 给你一个整数数组 nums 和一个整数 k ,请你统计并返回 该数组中和为 k 的子数组的个数 。 子数组是数组中元素的连续…...

SpringBoot自动装配原理之@Import注解解析
文章目录 1. 概述2. 使用2.1 导入普通Bean2.2 导入配置类2.3 导入 ImportSelector 实现类2.4 导入 ImportBeanDefinitionRegistrar 实现类 3. 区别 1. 概述 当谈及现代Java开发领域中的框架选择时,SpringBoot无疑是无与伦比的热门之选。其简化了开发流程࿰…...

49 样式迁移【李沐动手学深度学习v2课程笔记】
1. 样式迁移(Style Transfer) 计算机视觉的应用之一,将样式图片中的样式(比如油画风格等)迁移到内容图片(比如实拍的图片)上,得到合成图片 可以理解成为一个滤镜,但相对于滤镜来讲…...

Linux的学习之路:4、权限
一、Linux权限的概念 权限我们都熟悉,最常见的就是在看电视时需要vip这个就是权限,然后在Linux就是有两个权限,就是管理员也就是超级用户和普通的用户 命令:su [用户名] 功能:切换用户。 例如,要从root用户…...

自定义类型—结构体
目录 1 . 结构体类型的声明 1.1 结构的声明 1.2 结构体变量的创建与初始化 1.3 结构体的特殊声明 1.4 结构体的自引用 2. 结构体内存对齐 2.1 对齐规则 2.2 为什么存在内存对齐 2.3 修改默认对齐数 3. 结构体传参 4.结构体实现位段 4.1 位段的内存分配 4.3 位段的…...

【JavaWeb】Jsp基本教程
目录 JSP概述作用一个简单的案例:使用JSP页面输出当前日期 JSP处理过程JSP 生命周期编译阶段初始化阶段执行阶段销毁阶段案例 JSP页面的元素JSP指令JSP中的page指令Include指令示例 taglib指令 JSP中的小脚本与表达式JSP中的声明JSP中的注释HTML的注释JSP注释 JSP行…...

外包干了25天,技术退步明显.......
先说一下自己的情况,大专生,18年通过校招进入杭州某软件公司,干了接近4年的功能测试,今年年初,感觉自己不能够在这样下去了,长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…...

C++(14): STL条件变量std::condition_variable
1. 简述 在C的标准模板库(STL)中,std::condition_variable是一个非常重要的同步原语,用于在多线程编程中实现线程间的条件同步。它允许一个或多个线程等待某个条件成立,当条件成立时,等待的线程会被唤醒并继…...

Harmony与Android项目结构对比
主要文件对应 Android文件HarmonyOS文件清单文件AndroidManifest.xmlmodule.json5Activity/Fragmententryability下的ts文件XML布局pages下的ets文件resresourcesModule下的build.gradleModule下的build-profile.json5gradlehvigor根目录下的build.gradle根目录下的build-profi…...

langchain 学习笔记-FunctionCalling三种方式
ChatGPT 基于海量的训练数据生成答案,所以它无法回答训练数据中没有的信息或搜索信息 。人们希望 ChatGPT 具有对话以外的各种功能,例如“我想管理我的待办事项列表”。 函数调用是对此类请求的响应。 通过使用函数调用,ChatGPT 现在可以在生…...

CNAS软件测试公司有什么好处?如何选择靠谱的软件测试公司?
CNAS认可是中国合格评定国家认可委员会的英文缩写,由国家认证认可监督管理委员会批准设立并授权的国家认可机构,统一负责对认证机构、实验室和检验机构等相关机构的认可工作。 在软件测试行业,CNAS认可具有重要意义。它标志着一个软件测试公…...

Cohere推出全新升级版RAG大型AI模型:支持中文,搭载1040亿参数,现开源其权重!
4月5日,知名类ChatGPT平台Cohere在其官方网站上发布了一款全新的模型——Command R。 据官方消息,Command R拥有1040亿个参数,并且支持包括英语、中文、法语、德语在内的10种语言。这一模型的显著特点之一在于其对内置的RAG(检索增…...

搭建前后端的链接(java)
搭建前后端的链接(java) 一.前提 1.1 javaEE 搭建前后端的链接首先需要用到javaEE,也就是java企业版,也就是java后端(后端javaSE) 利用javaEE和前端交互,javaSE和数据库交互,javaSE和javaEE之间再进行交互就实现了前后端的交互…...

Java多路查找树(含面试大厂题和源码)
多路查找树(Multiway Search Tree),也称为B树或B树,是一种自平衡的树形数据结构,用于存储大量数据,通常用于数据库和文件系统中。它允许在查找、插入和删除操作中保持数据的有序性,同时优化了磁…...

day6 | 哈希表 part-2 | 454 四数相加II 、383. 赎金信、15. 三数之和、18. 四数之和
今日任务 454 四数相加II (题目: . - 力扣(LeetCode))383 赎金信 (题目: . - 力扣(LeetCode)) 454 四数相加II 题目:. - 力扣(LeetCode) 给你四个整数数组 nums1、num…...

Redis常见数据类型(2)
目录 String字符串 常见命令 SET GET MGET MSET SETNX 计数命令 INCR INCRBY DECR DECRBY INCRFLOAT 其它命令 APPEND GETRANGE SETRANGE STRLEN String字符串 字符串是Redis最基础的数据类型, 关于字符串需要特别注意: (1)首先Redis中所有的键的类型都是字符…...

SparkBug解决:Type mismatch; found : org.apache.spark.sql.Column required: Double
def assginFlag(aizmuth:Double):Option[Int] {val interval 0.5val index (aizmuth / interval ).toIntif (index > 0 && index < 720 ) Some(index 1) else None} assginFlag方法中的条件判断条件 (index > 0 && index < 720) 返回的是一个布…...

MQ之————如何保证消息的可靠性
MQ之保证消息的可靠性 1.消费端消息可靠性保证: 1.1 消息确认(Acknowledgements): 消费者在接收到消息后,默认情况下RabbitMQ会自动确认消息(autoAcktrue)。为保证消息可靠性,可以…...

TrollInstallerX官方一键安装巨魔商店
TrollInstallerX是巨魔官方开发的一款一键巨魔商店安装器,完美支持iOS 14.0 – 16.6.1的设备,操作非常简单,TrollInstallerX依然有个小小的限制,部分机型,还是要采用间接安装方法。 一,直接安装方法 通过…...

生成随机图片验证码
随着互联网的不断发展,安全性问题日益突出。为了保障用户账号的安全性,很多网站都引入了验证码机制。验证码是一种区分用户是计算机还是人的公共全自动程序,可以有效防止恶意攻击和自动化脚本的滥用。本文将介绍如何使用Python生成随机图片验…...

【0280】《数据库系统概论》阅读总结(附xmind思维导图)
0. 阅读进展 选择性地读取了《数据库系统概论》一书中的第13、14章节,并对这两章节中较为重点的内容作了总结和归纳;然后以xmind导图形式给出。 1. xmind思维导图 Xmind附件:...

数据结构(二)----线性表(顺序表,链表)
目录 1.线性表的概念 2.线性表的基本操作 3.存储线性表的方式 (1)顺序表 •顺序表的概念 •顺序表的实现 静态分配: 动态分配: 顺序表的插入: 顺序表的删除: 顺序表的按位查找: 顺序…...

为什么你选择成为一名程序员?
文章目录 ✍选择成为程序员:兴趣与职业发展的交汇💎1 兴趣的驱动💎2 职业发展的需求💎3 结语 ✍选择成为程序员:兴趣与职业发展的交汇 在当今数字化时代,程序员已经成为一个备受瞩目的职业。无论是因为对技…...

【Android】系统启动流程分析 —— SystemServer 处理过程
本文基于 Android 14.0.0_r2 的系统启动流程分析。 SystemServer 进程主要用于创建系统服务,我们熟知的 AMS、WMS 和 PMS 都是由它来创建的,因此掌握 SystemServer 进程是如何启动的,它在启动时做了哪些工作是十分必要的。 一、源码解析 Zyg…...

Web前端—属性描述符
属性描述符 假设有一个对象obj var obj {a:1 }观察这个对象,我们如何来描述属性a: 值为1可以重写可以遍历 我们可以通过Object.getOwnPropertyDescriptor得到它的属性描述符 var desc Object.getOwnPropertyDescriptor(obj, a); console.log(desc);我…...

SpringBoot及其特性
0.前言 Spring 框架提供了很多现成的功能。那么什么是 Spring Boot?使用 Spring 框架,我们可以避免编写基础框架并快速开发应用程序。为了让 Spring 框架提供基础框架,我们需要向 Spring 框架描述有关我们的应用程序及其组件的信息。 不只是…...

「JavaEE」初识进程
初识进程 🍉进程🍌操作系统的进程管理 🍉PCB 重要属性🍌进程的身份标识🍌内存指针🍌文件描述符表🍌进程的状态🍌优先级🍌记账信息🍌上下文 🍉内存…...