1、完善样式,用来显示帖子详情以及评论信息等,编辑app/assets/stylesheets/posts.scss文件,在原有代码下面添加代码:
.head { font-size: 18px; font-weight: 700; padding: 10px 0; } .time_right { float: right; font-size: 12px; color: #959595; .fa { font-size: 20px; padding-right: 10px; } } .padding-thumb { padding-top: 8px; } .reply { position: relative; overflow: hidden; padding-top: 10px; /*margin-bottom: 10px;*/ .body { padding: 8px; border-radius: 5px; position: relative; overflow: visible; float: left; width: 85%; border: 1px solid #ddd; line-height: 26px; &::before { content: ""; display: block; position: absolute; top: 16px; left: -6px; width: 10px; height: 10px; background: #fff; border-left: 1px solid #cad5e0; border-top: 1px solid #cad5e0; -moz-transform: rotate(-45deg); -webkit-transform: rotate(-45deg); } .datetime { float: right; font-size: 12px; color: #959595; } .delete-content { color:#959595; font-style:italic; } } .avatar { float: left; margin-right: 18px; margin-top: 12px; position: relative; overflow: visible; text-align: center; .image-circle { width: 75px; border-radius: 50%; } } .reply-from { width: 93%; float: right; padding-right: 30px; } } .re-reply { border-top: 1px solid #e3e3e3; font-size: 13px; position: relative; overflow: hidden; padding-top: 6px; padding-bottom: 6px; clear:both; } .comment-form { padding-top: 15px; .comment-text { width: 88%; border-radius: 5px; border: 1px solid #959595; } .comment-submit { width: 10%; float:right; } } input::-webkit-input-placeholder { font-size: 13px; }
2、编辑routes.rb文件,添加帖子详情页面的路由,并且传递post_id参数,post_id为被查看帖子的id
get 'posts/show_posts/:post_id' => 'posts#show_posts'
3、在posts_controller.rb中添加对应action方法show_posts
#进入帖子详情/评论页面 def show_posts post_id = params[:post_id] @post = Post.find(post_id) #as_type为0时代表帖子的评论,为1时代表评论的回复 @comments = Comment.where(post_id:post_id,as_type:0) end
4、创建一个partial文件views/home/_get_thumbs.html.erb
partial文件是什么?
partial文件是局部样板文件,一般用来保存不同页面的相同代码,避免代码冗余,partial文件名字的前面需要加下划线_区分。用render加载partial文件。
举例:<%= render :partial => 'account' %>
就是渲染当前目录下的 _account.html.erb文件
点赞部分的代码,不仅在将views/home/index.html.erb页面中显示,还需要在帖子详情页面(views/posts/show_posts.html.erb后面将创建)显示。如果不将这部分代码剪贴到partial文件中,这部分代码将会重复,造成代码冗余,增加后期维护难度。
所以我们在views/home/index.html.erb文件中,将点赞部分的代码剪切到这个partial文件中。后面我们会用<%= render :partial => "/home/get_thumbs"%>来调用partial文件。在partial文件views/home/_get_thumbs.html.erb中添加下列内容:
<!-- 获取用户是否为此帖子点过赞,分别显示不同的图标 --> <% if @current_user && p.get_thumb_info(@current_user.id) %> <a data-remote="true" href="/posts/create_thumb/<%= p.id %>/0" id="reduce" class="fa fa-thumbs-up" onclick="praiseReplay(this)"> <%= "#{p.get_thumbs}"%> </a> <%elsif @current_user%> <a data-remote="true" href="/posts/create_thumb/<%= p.id %>/1" id="increase" class="fa fa-thumbs-o-up" onclick="praiseReplay(this)"> <%= "#{p.get_thumbs}"%> </a> <% else %> <!-- 没有账户登录时的情况 --> <a data-remote="true" href="/home/login_in" class="fa fa-thumbs-o-up" onclick="alert('您还未登录,请先登录')"> <%= "#{p.get_thumbs}"%> </a> <%end%> <script type="text/javascript"> function praiseReplay(oldTotal){ if(oldTotal.className == "fa fa-thumbs-up") { oldTotal.className = "fa fa-thumbs-o-up"; var oldValue = oldTotal.innerHTML; oldTotal.innerHTML = " " + (parseInt(oldValue) - 1); href = oldTotal.href oldTotal.href = href.substring(0, href.length - 1) + "0" } else { oldTotal.className = "fa fa-thumbs-up"; var oldValue = oldTotal.innerHTML; oldTotal.innerHTML = " " + (parseInt(oldValue) + 1); href = oldTotal.href oldTotal.href = href.substring(0, href.length - 1) + "1" } } </script>
5、在views/home/index.html.erb文件中将上面在partial文件中粘贴的内容去掉,在去掉的地方加上引用partial文件的代码<%= render :partial => "/home/get_thumbs", :locals => { :p => p } %>。
目前index.html.erb文件中的代码如下:
<div class="home-banner" data-stretch="<%= image_url '/assets/timg.jpeg' %>"> <div class="banner-inner container clearfix"> <div class="home-banner-links"> <%= link_to "发布新帖", "/posts/new", class: "banner-btn btn" %> </div> <div class="home-banner-links" style="left: 100px;top: 100px;"> <%= link_to "个人中心", "#", class: "banner-btn btn" %> </div> <div class="home-banner-links" style="left: 350px;top: 150px;"> <%= link_to "流浪猫救助活动", "#", class: "banner-btn btn" %> </div> </div> </div> <div class="issue-list-header"> <div class="container clearfix"> <h1 class="issue-list-heading"></h1> </div> </div> <div class="container clearfix"> <div class="issue-list"> <% @posts.each do |p| %> <article class="issue clearfix"> <div class="avatar body"> <!-- 获取发帖用户的名字,get_account_name是在post.rb文件中定义的实例方法 --> <a class="read-more" href="#"><%= p.get_account_name %></a> <!-- 获取发帖的时间 --> <p class="time"><%= p.get_updated_at %></p> </div> <div class="body"> <% if p.as_type == 2 %> <div class="icon-top" title="置顶">置顶</div> <% elsif p.as_type == 1 %> <div class="icon-good" title="精">精</div> <% end %> <h5 class="title"> <%= link_to "#{p.head}", "#" %> </h5> <a class="as_sb" href="#"><%= p.body %></a> </div> <div class="issue-comment-count"> <a data-remote="true"><i class="fa fa-comment-o" padding-right: 15px;"> <!-- 获取评论数,方法还没完善,先设为1 --> <%= "#{p.get_post_items}" %> </i></a> <!-- 添加的代码,加载partial文件_get_thumbs.html.erb --> <%= render :partial => "/home/get_thumbs", :locals => { :p => p } %> </div> </article> <% end %> </div> </div>
代码解析:
<%= render :partial => "/home/get_thumbs", :locals => { :p => p } %>
将与partial文件中重复的代码去掉后,加上这一行代码,意思是将partial文件中的代码加载到这个位置,:locals表示需要从index.html.erb文件传递到_get_thumbs.html.erb文件的参数。
6、在app/models/comment.rb文件中添加下列方法
用来获取评论用户的用户名以及评论的创建时间,我们会在帖子详情页面(后面会创建views/posts/show_posts.html.erb)直接用Comment实例对象调用这些方法。
#通过评论实例获取该评论客户的用户名 def get_account_name account = Account.find_by(id:self.account_id) if account name = account.name else name = "用户不存在" end end # 获取评论创建时间 # 当小于60秒的时候返回时间为xx秒前; # 当小于60分钟大于等于60秒时返回xx分钟前; # 当小于24小时大于等于60分钟时返回xx小时前; # 当大于等于1天的时候,显示xxxx-xx-xx xx-xx时间; def get_created_at created_at = self.created_at now = Time.now #时间间隔秒数 time_distance = (now - created_at).to_i if time_distance == 0 date = "刚刚" elsif time_distance < 60 date = "#{time_distance}秒前" #判断时间间隔是否小于60分钟 elsif time_distance/60 < 60 date = "#{time_distance/60}分钟前" #判断时间间隔是否小于24小时 elsif time_distance/(60*60) < 24 date = "#{time_distance/(60*60)}小时前" #时间间隔大于1天,会进入else语句下面的代码 else date = created_at.strftime("%Y-%m-%d %H:%M") end date end
7、编辑app/models/post.rb文件中的get_post_items方法,来获取实际的评论数(评论数包括回复的数量)
#获取评论数 def get_post_items num = Comment.where(post_id:self.id).count end
8、创建views/posts/show_posts.html.erb文件,用来显示帖子详情页面,粘贴下列内容:
<div class="container" style="width: 60%"> <article class="clearfix"> <div class="avatar body"> <!-- 获取发帖用户的名字,get_account_name是在post.rb文件中定义的实例方法 --> <a class="read-more" href="#"><%= @post.get_account_name %></a> <!-- 获取用户的角色信息,ROLE是在account.rb文件中定义的常量数组,通过Account::ROLE调用该数组,Account::ROLE[0]取到的值为普通用户 --> <p class="time"><%= Account::ROLE[Account.find(@post.account_id).role] %></p> </div> <div class="head"><%= @post.head %></div> <div> <%= @post.body %> </div> <div class="time_right padding-thumb"> <!-- 锚点定位,点击会定位到id为co-point的元素 --> <a href="#co-point"><i class="fa fa-comment-o"> <!-- 获取评论数,get_post_items方法在app/models/post.rb中定义 --> <%= "#{@post.get_post_items}" %> </i></a> <!-- 加载partial文件,传递@post参数 --> <%= render :partial => "/home/get_thumbs", :locals => { :p => @post } %> <!-- 帖子最后的修改时间 --> <%= @post.updated_at.strftime ("%Y-%m-%d %H:%M") %> </div> </article> <div id="data_content"> <!-- 遍历as_type为0的评论对象集合@comments --> <% @comments.each do |comment| %> <div class="reply clearfix"> <div class="avatar"> <!-- get_account_name方法在comment.rb文件中已经定义,用来获取评论者的用户名 --> <a><%= comment.get_account_name %></a> </div> <div class="body"> <!-- 评论status为0时代表正常显示,不为0是代表已经被删除,被删除的评论需要显示为「该评论已删除」 --> <span id="content_<%= comment.id %>"> <% if comment.status == 0 %> <div><%= comment.content %></div> <% else %> <div class="delete-content">该评论已删除</div> <% end %> </span> <div class="time_right" id="time_<%= comment.id %>"> <!-- 获取评论的创建时间 --> <%= comment.get_created_at %> <!-- 已被删除的帖子后面不显示回复按钮 --> <% if comment.status == 0 %> <a id="reply<%= comment.id %>" onclick="outIn(<%=comment.id%>,<%=comment.id%>)">回复</a> <% end %> </div> <div id="reply_page_<%= comment.id %>"> <!-- 可以通过re_comment_id字段找到,对应本次遍历的评论对象的所有回复 --> <% @reply = Comment.where(re_comment_id:comment.id,as_type:1) %> <!-- 我们只默认展示两条回复,需要查看更多回复,需要点击查看更多回复 @reply.limit(2)的意思是只选择查询结果的前两条数据 --> <% @reply.limit(2).each do |re| %> <div class="re-reply"> <a><%= re.get_account_name %></a> <!-- 如果回复的是评论的回复,该回复用户名后面需要跟被回复用户的用户名,re_reply_id字段保存被回复用户的id;如果直接回复评论,那么回复用户名后面直接跟回复内容,re_reply_id字段为空。--> <% if re.re_reply_id.blank? %> : <% else %> 回复 <a><%= Comment.find(re.re_reply_id).get_account_name %></a> : <% end %> <span id="content_<%= re.id %>"> <% if re.status == 0 %> <span><%= re.content %></span> <% else %> <span class="delete-content">该评论已删除</span> <% end %> </span> <div class="time_right"> <%= re.get_created_at %> <span id="time_<%= re.id %>"> <% if re.status == 0 %> <!-- outIn方法控制回复框,当客户点击回复按钮,出现回复框, 回复变成取消回复,点击取消回复,回复框收起 --> <a id="reply<%= re.id %>" onclick="outIn(<%= comment.id %>,<%=re.id%>)"> 回复</a> <% end %> </span> </div> </div> <% end %> </div> <!-- 当该评论的回复大于两条时,下面会有「查看更多回复」的链接,点击会查看到更多回复 主要通过js的控制点击查看更多回复,后面会讲到 --> <% if @reply.count > 2 %> <a id="spread-out" name="1" data-remote="true" href="#">更多<%= @reply.count - 2 %>条回复 ↓</a> <% end %> </div> <!-- 回复框的内容 --> <%= form_for Comment.new,url: "#" do |f| %> <!-- 给每个评论的回复框的id都加上comment.id,这样每个评论都有唯一的id,这样才能通过js控制回复框出现在相应的评论下 --> <div class="comment-form reply-from" id="co-reply<%= comment.id %>" style="display:none;"> <input type="text" name="comment" placeholder="写下你的回复..." class="comment-text"> <div class="comment-submit"> <input type="submit" value="回复" class="submit-issue-button btn btn-primary"> </div> </div> <% end %> </div> <%end%> </div> <!-- 评论框的内容 --> <%= form_for Comment.new,url: "#" do |f| %> <!-- 评框的id为co-point,id后面不需要加上每个评论的id,因为评论框会出现在页面最下方,与每个评论的位置没有关系 --> <div class="comment-form" name="co-point" id="co-point"> <input type="text" name="comment" placeholder="写下你的评论..." class="comment-text"> <div class="comment-submit"> <input type="submit" value="发布" class="submit-issue-button btn btn-primary"> </div> </div> <% end %> </div>
9、编辑views/posts/show_posts.html.erb文件,实现点击回复按钮显示回复框的功能,用js实现。
(1)实现js方法。
点击回复<a>标签时,执行下面的js方法outIn(),先判断当前是否有用户登录,如果没有用户登录,需要提示「您还未登录,请先登录!」。
如果有用户登录,判断coReply回复框的显示状态,如果是未显示状态,将状态改为显示oReply.style.display = "block";,回复改为取消回复coA.innerHTML = "取消回复";。如果是显示状态,将状态改为未显示,取消回复改为回复。
<script type="text/javascript"> function outIn(comment_id,reply_id){ <% if @current_user %> //coReply为回复框对象 var coReply = document.getElementById("co-reply" + comment_id); //coA为回复a标签对象 var coA = document.getElementById("reply" + reply_id); if(coReply.style.display == "none"){ coReply.style.display = "block"; coA.innerHTML = "取消回复"; } else{ coReply.style.display = "none"; coA.innerHTML = "回复"; } <% else %> alert("您还未登录,请先登录!"); <% end %> } </script>
(2)编辑views/posts/show_posts.html.erb文件,将js方法添加到回复<a>
标签的onclick元素中。
<!--原代码--> <a id="reply<%= comment.id %>" onclick="">回复</a> <!--改为--> <a id="reply<%= comment.id %>" onclick="outIn(<%=comment.id%>,<%=comment.id%>)">回复</a> <!--原代码--> <a id="reply<%= re.id %>" onclick=""> 回复</a> <!--改为--> <a id="reply<%= re.id %>" onclick="outIn(<%= comment.id %>,<%=re.id%>)"> 回复</a>
代码解析:
<a id="reply<%= comment.id %>" onclick="outIn(<%=comment.id%>,
<%=comment.id%>)">回复</a>
其中id="reply<%= comment.id %>",嵌入了ruby代码,如果当前遍历的comment对象的id为20,则id=“reply20”
其中onclick="outIn(<%=comment.id%>,<%=comment.id%>)"给outIn()方法传递了两个参数,第一个参数是为了找到coReply回复框对象,第二个参数是为了找到coA回复a标签对象评论框对象
10、修改home/index.html.erb文件,将帖子标题、内容加上帖子详情页面的链接
<!--原代码--> <%= link_to "#{p.head}", "#" %> <!--改为--> <%= link_to "#{p.head}", "/posts/show_posts/#{p.id}" %> <!--原代码--> <a class="as_sb" href="#"><%= p.body %></a> <!--改为--> <a class="as_sb" href="/posts/show_posts/<%= p.id %>"><%= p.body %></a>