评论插件

基于jquery 的评论小插件

github项目地址:https://github.com/fancaixia/jquery-comment

001.png
案例思路:

[后台建立两个表]

  • comment_list 评论表 id,userID,userName,articleID,content
  • reply_list 回复表 id,talkID,content,fromID,fromUser,toID,toUser

[查询评论]

  • 后台获取 comment_list 表数据
  • 检索reply_list表中item.talkID == comment_list中 item.id, 并存入comment_list中对应item.child

[发送评论]

  • 前台发送用户id,用户名称, 文章id, 评论内容到后台
  • 后台将评论存储到comment_list中

[删除评论]

  • 前台发送此条评论ID到后台,
  • 后台reply_list判断 talkId == id 进行删除
  • 同时查询comment_list判断 id 相等的数据并进行删除

[回复评论]

  • 前台发送评论id, 内容, fromID, fromUser, toID, toUser 到后台
  • 后台将消息存储到reply_list中

[删除回复]

  • 前台发送此条回复ID到后台,
  • 后台reply_list判断对应 id 进行删除

[前台判断]

  • 根据当前登录用户id判断评论或回复用户是否一致 (不可给自己评论)
  • 划过某条评论时判断是不是当前用户的评论,(只能删除自己的评论)

代码结构

comment_static

config.js            接口文档
common.js        封装 ajax 请求及提示信息
index.html         主文件
index.js              业务逻辑

模拟多个用户可复制index.html,并修改index.js 中currentUserID和currentUserName

comment_mysql_server
app.js        主文件 连接mysql配置
route

activity_talk        后台逻辑处理

项目启动

  1. cd / comment_mysql_server
  2. app.js 更改数据库连接信息
  3. npm / cnpm install
  4. npm run dev

代码片段:

server / route / activity_talk.js

const express = require('express');
const uuid = require('uuid')
const route_talk = express.Router();

route_talk.use((req,res,next)=>{
    
    //解决跨域请求
    res.header("Access-Control-Allow-Origin","*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With,Origin,Content-Type,Accept");  
    res.header("Access-Control-Allow-Methods","PUT,POST,GET,DELETE,OPTIONS");  
    next()

})

// 查询评论数据
route_talk.post('/get',(req,res)=>{
    let articleID = req.body.articleID;
    let talkID = req.body.talkID;
    let replyCount = req.body.replyCount;   //查询评论对应回复的条数  默认3  查询所有时设置为10000
    req.pool.query(`SELECT * FROM comment_list WHERE articleID=${articleID}`,(err,data)=>{
        if(err){
            res.send({code:1,msg:"服务端错误"}).end();
        }else{
            //文章列表查询评论成功后  遍历数组  回复表根根talkID查 回复数据 
            
            function makePromise(item, index) {
                return new Promise((resolve) => {
                    let newitem = JSON.parse(JSON.stringify(item));
                    //如果传入 talkID  那么此评论对应回复查询所有
                    if(talkID && talkID == newitem.id){
                        replyCount = 10000;
                    }else{
                        replyCount = 3;
                    }
                    
                    req.pool.query(`SELECT * FROM reply_list WHERE talkID="${newitem.id}" LIMIT 0,${replyCount}`,(err,replydata)=>{
                        if(err){
                            res.send({code:1,msg:"服务端错误"}).end();
                        }else{
                            //模拟查询用户表数据
                            newitem.img="./images/reply.jpg";
                            newitem.time = "2017-07-06  12:45";
                            newitem.child = JSON.parse(JSON.stringify(replydata));

                            //遍历回复数据  模拟查询用户表  添加用户信息
                            newitem.child.forEach((item,index) => {
                                item.fromImg = './images/reply_01.jpg';   //模拟查询用户表数据
                                item.time = "2017-07-06  12:45";
                            });
                        

                            req.pool.query(`SELECT COUNT(*) AS count FROM reply_list WHERE talkID="${newitem.id}"`,(err,num)=>{
                                if(err){
                                    res.send({code:1,msg:"服务端错误"}).end();
                                }else{
                                    newitem.replytotal = num[0].count;
                                    resolve(newitem)
        
                                }
                            })
                    

                        }
                    })

                });
            }
            
            
            Promise.all(data.map((v, k) => makePromise(v, k))).then(data => {
                res.send({code:0,data,}).end();
            });
            
           
            
        }
    })
})
//添加评论  
route_talk.post('/add',(req,res)=>{

    const uuid_str = uuid.v4().replace(/-/g,'');
    req.pool.query(`insert into comment_list (id,userID,userName,articleID,content) VALUES("${uuid_str}","${req.body.userID}","${req.body.userName}","${req.body.articleID}","${req.body.content}")`,(err,state)=>{
        if(err){
            res.send({code:1,msg:"添加失败"}).end();
        }else{
            //添加成功   返回评论数据
            res.send({code:0,msg:"添加成功"}).end();
        }
    })

})
//删除评论   首先删除评论对应回复  DELETE FROM reply_list WHERE talkID=""
route_talk.post('/remove',(req,res)=>{
    req.pool.query(`delete from reply_list where talkID="${req.body.id}"`,(err,state)=>{
        if(err){
            res.send({code:1,msg:"删除回复失败"}).end();
        }else{
            req.pool.query(`delete from comment_list where id="${req.body.id}"`,(err,data)=>{
                if(err){
                    res.send({code:1,msg:"删除评论失败"}).end();
                }else{
                    res.send({code:0,msg:"ok"}).end();
                }
            })
        }
    })

})
//添加回复
route_talk.post('/addreply',(req,res)=>{

    const uuid_str = uuid.v4().replace(/-/g,'');
    req.pool.query(`insert into reply_list (id,talkID,content,fromID,fromUser,toID,toUser) VALUES("${uuid_str}","${req.body.talkID}","${req.body.content}","${req.body.fromID}","${req.body.fromUser}","${req.body.toID}","${req.body.toUser}")`,(err,state)=>{
        if(err){
            res.send({code:1,msg:"回复失败"}).end();
        }else{
            res.send({code:1,msg:"ok"}).end();

        }
    })

})
//删除回复
route_talk.post('/removereply',(req,res)=>{
    req.pool.query(`delete from reply_list where id="${req.body.id}"`,(err,data)=>{
        if(err){
            res.send({code:1,msg:"删除回复失败"}).end();
        }else{
            res.send({code:1,msg:"删除回复成功"}).end();
        }
    })

})

module.exports = route_talk;

static / index.js

let replySize = 3;  //回复默认显示3条  当点击更多 则为评论相关回复total总数
const replyCount = 3; //回复条数大于3  隐藏
const currentUserID = '1549bd4753424d88a4f0d5839da33f96'; 
let currentUserName = '小财神';
// const currentUserID = '25b92f4e04d54ca5a159b46309255f7e';
// let currentUserName = '小迷糊'; 


let replyUser = '';   //回复人XXX
let replyUserID = '';  //回复人ID
let seeAlltalkID = '';   //点击查看按钮时  储存点击的评论ID  
let articleID = '0'    // 假设此篇文章ID 为 0 (根据文章ID 请求对应评论数据)

$('#current_user').html('当前用户:'+currentUserName)
//初始化渲染评论数据
getTalkData();
//初始化渲染评论数据
function getTalkData(id){
  let talkID = id || '';

  let data = {
    articleID,  
    talkID:talkID,
    replyCount:replyCount
  };
  commonfun.request(serverApi.getTalk,data,renderData,renderFail)
}

//获取数据成功回调
function renderData(data){
      if(data.code != 0){
          tipObj.setErrmsg(data.msg,1);
          return;
      }

      let talkStr = '';  //整个评论信息
      let moreStr = '';

      data.data.forEach((talkitem,index)=>{
        //判断此评论是否有回复
        let replyStr = '';  //回复文本
        if(talkitem.child.length > 0){
          replyStr = '<div class="comment_reply">';
          talkitem.child.forEach((item,index)=>{
                //渲染评论数据
                replyStr += 
      '<div data-fromID='+item.fromID+' data-id='+item.id+' class="one_reply '+(index==talkitem.child.length-1?'one_reply_noborder':'')+'">'+
          '<div class="comment_people_info">'+
              '<img class="fl_left" src="'+item.fromImg+'" alt="">'+
              '<span class="first_reply fl_left"><span class="first_reply_aa">'+item.fromUser+'</span>'+
                '<span class="first_reply_text"> 回复: </span>'+
                
              '<span class="first_reply_aa">'+item.toUser+'</span></span>'+
              '<span class="date fl_right">'+item.time+'</span></div>'+
          '<div class="comment_people_content">'+
              '<span class="fl_left">'+item.content+'</span>'+
              
              '<span data-userID='+item.fromID+' data-userName='+item.fromUser+' class=" reply_fn reply_btn fl_right">回复</span>'+
              '<span id="reply_remove" class="reply_remove reply_btn fl_right reply_btn_cont none">删除</span>'+
              '</div></div>'
          })
          moreStr = '<div class="comment_more '+(talkitem.replytotal>replyCount?'':'none')+'">'+
      '<span>剩余'+(seeAlltalkID== talkitem.id? talkitem.replytotal-replySize : talkitem.replytotal-replyCount)+'条评论</span>  <span id="see_reply" data-total='+talkitem.replytotal+' class="green see">'+(talkitem.replytotal-replySize == 0?'收起':"点击查看")+'</span></div>';
          replyStr += moreStr+'</div>'
        }

          talkStr += '<div data-id='+talkitem.id+' data-userID='+talkitem.userID+' class="comment">'+
      '<div class="comment_people">'+
        '<div class="comment_people_titleBox">'+
          '<div class="comment_people_info">'+
              '<img class="fl_left" src="'+talkitem.img+'" alt="">'+
              '<span class="first_reply fl_left">'+talkitem.userName+'</span>'+
              '<span class="date fl_left">'+talkitem.time+'</span>'+
              '<span data-userID='+talkitem.userID+' data-userName='+talkitem.userName+' class="reply_fn fl_right reply_btn">回复</span></div>'+
          '<div class="comment_people_content">'+
          '<span class="fl_left">'+talkitem.content+'</span>'+
              '<span id="talk_remove" class="reply_btn fl_right none reply_btn_cont">删除</span>'+
              '</div></div>'+replyStr+'</div></div>';
            
      })
      $('#talk_content').html(talkStr);

    }
// 渲染数据失败
function renderFail(err){
  tipObj.setErrmsg("请求失败,请重试",1);
}

//对文章评论   ++++++++++++++++++++++
$('#textareaBox').on('focus',function () {
   $('#cancelbtn').removeClass('none')
})
//发布评论
$('#talk_submit').on('click',function(){
  //提交请求
  let content = $.trim($('#textareaBox').val())
  if(content.length > 0){

    let data = {
      userID:currentUserID,
      userName:currentUserName,
      articleID,
      content,
    };
    commonfun.request(serverApi.addTalk,data,addTalkfun,addTalkFail)

  }else{
    //提示层
    tipObj.setErrmsg('请输入评论内容',1);
  }
})
//添加评论成功
function addTalkfun(data){
  $('#textareaBox').val('')
  $('#cancelbtn').addClass('none')
  getTalkData();
}
//添加评论失败
function addTalkFail(err){
  tipObj.setErrmsg("请求失败,请重试",1);
}
//发布评论回车事件处理
$('#textareaBox').keypress(function(e){ 
  if(e.ctrlKey && e.which == 13 || e.which == 10 || e.which == 13) { 
    $('#talk_submit').trigger('click')
    e.preventDefault(); //屏蔽enter对系统作用。按后增加\r\n等换行
  } 
});

//取消评论
$('#cancelbtn').on('click',function(){
  $('#textareaBox').val('')
  $(this).addClass('none')
})

//滑过评论控件  显示删除按钮
$('#talk_content').on('mouseover','.comment_people_titleBox ',function(){
  if(currentUserID == $(this).parents('.comment').attr('data-userID')){
   
    $(this).find('#talk_remove').removeClass('none')
  }
});

//移出评论控件  隐藏删除按钮
$('#talk_content').on('mouseout','.comment_people_titleBox',function(){
  $(this).find('#talk_remove').addClass('none')
})

//删除评论
$('#talk_content').on('click','#talk_remove',function(){
  let id = $(this).parents('.comment').attr('data-id')

  let data = {
    id,
  }
  commonfun.request(serverApi.removeTalk,data,removeTalkfun,removeTalkFail)
})

//删除评论成功回调
function removeTalkfun(){
    //初始化查询评论数据
    getTalkData();
}
//删除评论失败回调
function removeTalkFail(){
  tipObj.setErrmsg("请求失败,请重试",1);
}

//滑过回复控件  显示删除
$('#talk_content').on('mouseover','.one_reply ',function(){
    if(currentUserID == $(this).attr('data-fromid')){
      $(this).find('#reply_remove').removeClass('none')
    }
});
//移出回复控件  隐藏删除
$('#talk_content').on('mouseout','.one_reply',function(){
    $(this).find('#reply_remove').addClass('none')
})
//点击回复 
// 判断是否对自己回复  对自己回复则 return
// 判断是否存在评论框  不存在创建 同时设置文本域值 @xxxx ++++++++++++++++++++++
$('#talk_content').on('click','.reply_fn',function(){

  if($(this).attr('data-userid') == currentUserID){
    return;
  }

  let areachild = $('#talk_content').find('.talk_box_s')

  if($(areachild).length == 0){
    let textBox = setchildTextarea();
    $(this).parents('.comment').append(textBox)

  }else{

    $('#talk_content').find('.talk_box_s').remove();
    let textBox = setchildTextarea();
    $(this).parents('.comment').append(textBox)
  }

  let val = $(this).attr('data-username');
  $('#talk_content').find('#textareaBox_s').val('@'+val+' ')

  replyUser = val;
  replyUserID = $(this).attr('data-userid');

})
//回复发布   
$('#talk_content').on('click','#talk_submit_s',function(){

  // 替换 @ xxx 为空
  let content = ($.trim($('#textareaBox_s').val())).replace((replyUser),'')
  content = content.replace('@','')

  let talkID = $(this).parents('.comment').attr('data-id')

  if(content.length > 0){

    let data = {
        talkID,
        content,
        fromID:currentUserID,
        fromUser:currentUserName,
        toID:replyUserID,
        toUser:replyUser,
    }
    commonfun.request(serverApi.addreply,data,replyfun,replyfail)

  }else{
    tipObj.setErrmsg('请输入评论内容',1);
  }

})
//回复发布成功
function replyfun(){
  getTalkData();
  $(this).parents('.talk_box_s').remove();
}
//回复发布失败
function replyfail(){
  tipObj.setErrmsg('请求失败,请重试',1)
}

//回复发布  回车事件处理
$('#talk_content').keypress('#textareaBox_s',function(e){ 
  if(e.ctrlKey && e.which == 13 || e.which == 10 || e.which == 13) { 
    $('#talk_content').find('#talk_submit_s').trigger('click')
    e.preventDefault();     //屏蔽enter对系统作用。按后增加\r\n等换行
  } 
});

//删除回复
$('#talk_content').on('click','#reply_remove',function(){

  let id = $(this).parents('.one_reply').attr('data-id')
  let data = {
    id:id
  }
  commonfun.request(serverApi.removereply,data,removeReplyfun,removeReplyfail)
})

//删除回复成功
function removeReplyfun(){
  //初始化查询评论数据
  getTalkData();
}
//删除回复失败
function removeReplyfail(){
  tipObj.setErrmsg('请求失败,请重试',1)
}

//点击查看  获取评论并且根据talkID获取全部评论 
$('#talk_content').on('click','#see_reply',function(){

  seeAlltalkID = $(this).parents('.comment').attr('data-id')
  let replyTotal = $(this).attr('data-total') 
  if(replySize == replyTotal){
    replySize = replyCount;
    getTalkData();
  }else{
    replySize = replyTotal;
    //初始化查询评论数据
    getTalkData(seeAlltalkID);
  }
  

})

//回复取消
$('#talk_content').on('click','#cancelbtn_s',function(){
  $(this).parents('.talk_box_s').remove();
})

//回复评论框
function setchildTextarea(){
  return '<div class="talk_box talk_box_s">'+
  '<textarea name="talk" id="textareaBox_s" placeholder="写下你的评论..."></textarea>'+
  '<button id="talk_submit_s">发布</button><button id="cancelbtn_s" class="cancelbtn">取消</button></div>'
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,222评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,455评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,720评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,568评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,696评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,879评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,028评论 3 409
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,773评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,220评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,550评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,697评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,360评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,002评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,782评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,010评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,433评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,587评论 2 350

推荐阅读更多精彩内容