环境准备+版块相关功能
版块列表 1(个请求)
ForumAction.list()
显示单个版块(主题列表) 1
ForumAction.show()
------
发新帖 2
TopicAction.addUI()
TopicAction.add()
显示单个主题(主帖+回帖列表) 1
TopicAction.show()
------
回帖 2
ReplyAction.addUI()
ReplyAction.add()
步骤:创建Actionà写里面的方法(方法名,返回值) à创建页面à配置(Action注释和struts.xml)à创建serviceà配置(ServiceImpl注释和在BaseAction中注入service) à写具体的Action方法和service方法à套用静态页面
1.先实现版块列表功能:
(这里ForumAction和ForumManageAction公用service)
ForumAction:
import cn.itcast.oa.base.BaseAction; import cn.itcast.oa.domain.Forum; @Controller @Scope("prototype") public class ForumAction extends BaseAction<Forum>{
/** 版块列表 */ public String list() throws Exception { List<Forum> forumList = forumService.findAll(); ActionContext.getContext().put("forumList", forumList); return "list"; }
/** 显示单个版块(主题列表) */ public String show() throws Exception { return "show"; } } |
TopicAction:
@Controller @Scope("prototype") public class TopicAction extends BaseAction<Topic>{
/** 显示单个主题 */ public String show() throws Exception { return "show"; }
/** 发新帖页面*/ public String addUI() throws Exception { return "addUI"; }
/** 发新帖 */ public String add() throws Exception { return "toShow"; //转到当前这个 新主题的页面 } } |
ReplyAction:
@Controller @Scope("prototype") public class ReplyAction extends BaseAction<Reply>{
/** 发表回复页面 */ public String addUI() throws Exception { return "addUI"; }
/** 发表回复 */ public String add() throws Exception { return "toTopicShow"; //转到当前回复所属的主题显示页面 } } |
(ForumAction对应的)list.jsp:
<div class="ForumPageTableBorder" style="margin-top: 25px;"> <s:iterator value="forumList"> <table width="100%" border="0" cellspacing="0" cellpadding="0">
<!--表头--> <tr align="center" valign="middle"> <td colspan="3" class="ForumPageTableTitleLeft">版块</td> <td width="80" class="ForumPageTableTitle">主题数</td> <td width="80" class="ForumPageTableTitle">文章数</td> <td width="270" class="ForumPageTableTitle">最后发表的主题</td> </tr> <tr height="1" class="ForumPageTableTitleLine"><td colspan="9"></td></tr> <tr height="3"><td colspan="9"></td></tr>
<!--版面列表--> <tbody class="dataContainer" datakey="forumList"> <tr height="78" align="center" class="template"> <td width="3"></td> <td width="75" class="ForumPageTableDataLine"> <img src="${pageContext.request.contextPath}/style/images/forumpage3.gif" /> </td> <td class="ForumPageTableDataLine"> <ul class="ForumPageTopicUl"> <li class="ForumPageTopic"> <s:a cssClass="ForumPageTopic" action="forum_show?id=%{id}">${name}</s:a></li> <li class="ForumPageTopicMemo">${description}</li> </ul> </td> <td class="ForumPageTableDataLine"><b>${topicCount}</b></td> <td class="ForumPageTableDataLine"><b>${articleCount}</b></td> <td class="ForumPageTableDataLine"> <ul class="ForumPageTopicUl"> <li><font color="#444444">┌ 主题:</font> <s:a cssClass="ForumTitle" action="topic_show?id=%{lastTopic.id }">${lastTopic.title}</s:a> </li> <li><font color="#444444">├ 作者:</font> ${lastTopic.author.name}</li> <li><font color="#444444">└ 时间:</font> <s:date name="lastTopic.postTime" format="yyyy年MM月dd日 HH:mm:ss"/></li> </ul> </td> <td width="3"></td> </tr> </tbody> <!-- 版面列表结束 -->
<tr height="3"><td colspan="9"></td></tr> </table> </s:iterator> </div> |
Struts.xml:
<!-- 论坛:版块相关 --> <action name="forum_*" class="forumAction" method="{1}"> <result name="list">/WEB-INF/jsp/forumAction/list.jsp</result> <result name="show">/WEB-INF/jsp/forumAction/show.jsp</result> </action>
<!-- 论坛:主题相关 --> <action name="topic_*" class="topicAction" method="{1}"> <result name="show">/WEB-INF/jsp/topicAction/show.jsp</result> <result name="addUI">/WEB-INF/jsp/topicAction/addUI.jsp</result> <result name="toShow" type="redirectAction">topic_show?id=${id}</result> </action>
<!-- 论坛:回复相关 --> <action name="reply_*" class="replyAction" method="{1}"> <result name="addUI">/WEB-INF/jsp/replyAction/addUI.jsp</result> <result name="toTopicShow" type="redirectAction">topic_show?id=${id}</result> </action> |
效果:
2.再实现单个版块(主题列表)功能:(暂时不做分页)
ForumAction:
@Controller @Scope("prototype") public class ForumAction extends BaseAction<Forum>{
/** 版块列表 */ public String list() throws Exception { List<Forum> forumList = forumService.findAll(); ActionContext.getContext().put("forumList", forumList); return "list"; }
/** 显示单个版块(主题列表) */ public String show() throws Exception { //准备数据:forum Forum forum = forumService.getById(model.getId()); ActionContext.getContext().put("forum", forum);
//准备数据:topicList List<Topic> topicList = topicService.findByForum(forum); ActionContext.getContext().put("topicList", topicList); return "show"; } } |
topicServiceImpl:
@Service @Transactional @SuppressWarnings("unchecked") public class TopicServiceImpl extends DaoSupportImpl<Topic> implements TopicService{
/** * 查询指定版块的主题列表,最新状态的排到前面,置顶帖排到最前面 */ public List<Topic> findByForum(Forum forum) { return getSession().createQuery(// "from Topic t where t.forum = ? order by (case t.type when 2 then 2 else 0 end) desc, t.lastUpdateTime desc")// .setParameter(0, forum)// .list(); } } |
其中Topic.java是这样的:
/** * 实体:主贴 * @author Tan * */ public class Topic extends Article{
/** 普通贴 */ public static final int TYPE_NORMAL = 0; /** 精品贴 */ public static final int TYPE_BEST = 1; /** 置顶贴 */ public static final int TYPE_TOP = 2;
private String title; //标题 private int type; //类型 private int replyCount; //回复数量 private Date lastUpdateTime; //最后文章的发表时间
private Forum forum; //版块 主贴与版块:多对一 private Set<Reply> replies = new HashSet<Reply>(); //回复 主贴与回复:多对一 private Reply lastReply; //最后回复 …… } |
Topic还继承了Article.java:
/** * 实体:文章 * @author Tan * */ public abstract class Article {
private Long id; private String content; //内容(TEXT类型) private Date postTime; //发表时间 private String ipAddr; //ip地址
private User author; //作者 …… |
需求页面是这样的:
ForumAction中的show.jsp:
<!-- 标题显示 --> <div id="Title_bar"> <div id="Title_bar_Head"> <div id="Title_Head"></div> <div id="Title"><!--页面标题--> <img border="0" width="13" height="13" src="${pageContext.request.contextPath}/style/images/title_arrow.gif"/> 【${forum.name }】中的主题列表 </div> <div id="Title_End"></div> </div> </div>
<div id="MainArea"> <div id="PageHead"></div> <center> <div class="ItemBlock_Title1" style="width: 98%;"> <font class="MenuPoint"> > </font> <s:a action="forum_list">论坛</s:a> <font class="MenuPoint"> > </font> ${forum.name } <span style="margin-left:30px;"><a href="${pageContext.request.contextPath}/BBS_Topic/saveUI.html"> <img align="absmiddle" src="${pageContext.request.contextPath}/style/blue/images/button/publishNewTopic.png"/></a> </span> </div>
<div class="ForumPageTableBorder"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <!--表头--> <tr align="center" valign="middle"> <td width="3" class="ForumPageTableTitleLeft"> <img border="0" width="1" height="1" src="${pageContext.request.contextPath}/style/images/blank.gif" /> </td> <td width="50" class="ForumPageTableTitle"><!--状态/图标--> </td> <td class="ForumPageTableTitle">主题</td> <td width="130" class="ForumPageTableTitle">作者</td> <td width="100" class="ForumPageTableTitle">回复数</td> <td width="130" class="ForumPageTableTitle">最后回复</td> <td width="3" class="ForumPageTableTitleRight"> <img border="0" width="1" height="1" src="${pageContext.request.contextPath}/style/images/blank.gif" /> </td> </tr> <tr height="1" class="ForumPageTableTitleLine"><td colspan="8"></td></tr> <tr height=3><td colspan=8></td></tr>
<!--主题列表--> <tbody class="dataContainer" datakey="topicList"> <s:iterator value="topicList"> <tr height="35" id="d0" class="template"> <td></td> <td class="ForumTopicPageDataLine" align="center"><img src="${pageContext.request.contextPath}/style/images/topicType_${topic.type}.gif" /></td> <td class="Topic"><s:a cssClass="Default" action="topic_show?id=%{id}">${title}</s:a></td> <td class="ForumTopicPageDataLine"> <ul class="ForumPageTopicUl"> <li class="Author">{author.name}</li> <li class="CreateTime"><s:date name="postTime" format="yyyy年MM月dd日 HH:mm:ss"/> </li> </ul> </td> <td class="ForumTopicPageDataLine Reply" align="center"><b>${replyCount}</b></td> <td class="ForumTopicPageDataLine"> <ul class="ForumPageTopicUl"> <li class="Author">${lastReply.author.name }</li> <li class="CreateTime"><s:date name="lastReply.postTime" format="yyyy年MM月dd日 HH:mm:ss"/></li> </ul> </td> <td></td> </tr> </s:iterator> </tbody> <!--主题列表结束--> |
效果页面:
主题相关功能
之前已经实现了版块列表和单个版块展示(主题列表)的功能,因为还没有数据,所以主题展示的功能暂且不做,先做主题添加的功能(发新帖).
主题添加(发新帖):
添加页面:
发新帖的请求是从版块的单个版块展示(主题列表)发出的:
还要修改一下ForumAction对应的show.jsp中的链接地址:
<div class="ItemBlock_Title1" style="width: 98%;"> <font class="MenuPoint"> > </font> <s:a action="forum_list">论坛</s:a> <font class="MenuPoint"> > </font> ${forum.name } <span style="margin-left:30px;"> <s:a action="topic_addUI?forumId=%{#forum.id }"> <img align="absmiddle" src="${pageContext.request.contextPath}/style/blue/images/button/publishNewTopic.png"/> </s:a> </span> </div> |
- Tips: topic_addUI?forumId=%{#forum.id }
这里要将forum的id传递给TopicAction.虽然本页面是ForumAction对应的show.jsp页面,但因为此链接要转到TopicAction,而此id并不是Topic的id,所以要写成forumId,绝不能写成Id. forumId.%{#forum.id}这是OGNL表达式,加#是因为在ForumAction,forum被put进了map域中.
Forum forum = forumService.getById(model.getId()); ActionContext.getContext().put("forum", forum); |
相应的,在TopicAction中要用属性驱动的方式封装这个参数
@Controller @Scope("prototype") public class TopicAction extends BaseAction<Topic>{
private Long forumId;
/** 显示单个主题 */ public String show() throws Exception { return "show"; }
/** 发新帖页面*/ public String addUI() throws Exception { //准备数据:forum Forum forum = forumService.getById(forumId); ActionContext.getContext().put("forum", forum); return "addUI"; }
/** 发新帖 */ public String add() throws Exception { return "toShow"; //转到当前这个 新主题的页面 } //----------------------------------- public Long getForumId() { return forumId; }
public void setForumId(Long forumId) { this.forumId = forumId; } } |
TopicAction对应的add UI.jsp:
…… <!--显示表单内容--> <div id="MainArea"> <s:form action="topic_add" cssStyle="margin: 0; padding: 0;"> <s:hidden name="forumId"></s:hidden> <div id="PageHead"></div> <center> <div class="ItemBlock_Title1"> <div width=85% style="float:left"> <font class="MenuPoint"> > </font> <s:a action="forum_list">论坛</s:a> <font class="MenuPoint"> > </font> <s:a action="forum_show?id=%{#forum.id}">${forum.name}</s:a> <font class="MenuPoint"> >> </font> 发表新主题 </div> </div> <div class="ItemBlockBorder"> <table border="0" cellspacing="1" cellpadding="1" width="100%" id="InputArea"> <tr> <td class="InputAreaBg" height="30"><div class="InputTitle">标题</div></td> <td class="InputAreaBg"><div class="InputContent"> <s:textfield name="title" cssClass="InputStyle" cssStyle="width:100%"/></div> </td> </tr> <tr height="240"> <td class="InputAreaBg"><div class="InputTitle">内容</div></td> <td class="InputAreaBg"><div class="InputContent"><s:textarea name="content" cssStyle="width:600px;height:200px"></s:textarea></div></td> </tr> <tr height="30"> <td class="InputAreaBg" colspan="2" align="center"> <input type="image" src="${pageContext.request.contextPath}/style/blue/images/button/submit.PNG" style="margin-right:15px;"/> <a href="javascript:history.go(-1);"><img src="${pageContext.request.contextPath}/style/blue/images/button/goBack.png"/></a> </td> </tr> </table> </div> </center> </s:form> …… |
- Tips: <s:a action="forum_show?id=%{#forum.id}">${forum.name}</s:a>
同理,这里要将forum的id传递给ForumAction对应的show(),虽然本页面是TopicAction对应的addUI页面,但因为此链接要转到ForumAction,而此id这好是forum的id,所以写成id即可,不用再写成forumId了. %{#forum.id}这是OGNL表达式,加#是因为在TopicAction中forum被put进了map域中.
Forum forum = forumService.getById(forumId); ActionContext.getContext().put("forum", forum); |
主题添加:
在发表新主题的同时,版块的一些属性也会受到影响,所以还要维护版块的某些属性.
特殊属性的说明
Forum | topicCount | 主题数量 |
articleCount | 文章数量(主题+回复) | |
lastTopic | 最后发表的主题 | |
Topic | replyCount | 回复数量 |
lastReply | 最后发表的回复 | |
lastUpdateTime | 最后更新时间(主题发表的时间或最后回复的时间) |
特殊属性的维护
发表新主题 | 发表新回复 | ||
Forum | topicCount | 加1 | |
articleCount | 加1 | 加1 | |
lastTopic | 更新为当前的新主题 | ||
Topic | replyCount | 0 | 加1 |
lastReply | null | 更新为当前的新回复 | |
lastUpdateTime | 本主题的发表时间 | 更新为当前新回复的发表时间 |
TopicAction:
@Controller @Scope("prototype") public class TopicAction extends BaseAction<Topic>{
private Long forumId;
/** 显示单个主题 */ public String show() throws Exception { return "show"; }
/** 发新帖页面*/ public String addUI() throws Exception { //准备数据:forum Forum forum = forumService.getById(forumId); ActionContext.getContext().put("forum", forum); return "addUI"; }
/** 发新帖 */ public String add() throws Exception { //封装对象 Topic topic = new Topic(); //需要在action中封装的数据: //>>a.表单中的参数 topic.setTitle(model.getTitle()); topic.setContent(model.getContent()); topic.setForum(forumService.getById(forumId));//addUI.jsp表单中的隐藏域,把forumId传递过来了 //>>b.在显示层才能获得的数据 topic.setAuthor(getCurrentUser()); //当前登录的用户 topic.setIpAddr(getRequestIp()); //客户端的IP地址
//调用业务方法 topicService.save(topic);
return "toShow"; //转到当前这个 新主题的页面 } … } |
Tips:
在action中就封装这些属性即可:表单中的参数和在显示层才能获取到的数据,其他数据到业务层再设置.看需求页面分析从页面传递过来的值:
GetCurrentUser()和getRequestIp()比较常用,所以封装到BaseAction中比较好.
//--------------------工具方法---------------- /** * 获取当前用户 */ public User getCurrentUser() throws Exception { return (User) ActionContext.getContext().getSession().get("user"); }
/** * 获取客户端的ip地址 */ public String getRequestIp() throws Exception { return ServletActionContext.getRequest().getRemoteAddr(); } |
TopicServiceImpl:
@Service @Transactional @SuppressWarnings("unchecked") public class TopicServiceImpl extends DaoSupportImpl<Topic> implements TopicService{
/** * 查询指定版块的主题列表,最新状态的排到前面,置顶帖排到最前面 */ public List<Topic> findByForum(Forum forum) { return getSession().createQuery(// "from Topic t where t.forum = ? order by (case t.type when 2 then 2 else 0 end) desc, t.lastUpdateTime desc")// .setParameter(0, forum)// .list(); }
/** * 重写save() */ public void save(Topic topic) { //设置属性并保存 topic.setType(topic.TYPE_NORMAL); //普通帖 topic.setReplyCount(0); topic.setLastReply(null); topic.setPostTime(new Date()); //当前时间 topic.setLastUpdateTime(topic.getPostTime()); //默认为主题的发表时间
getSession().save(topic);
//更新相关信息 Forum forum = topic.getForum(); forum.setTopicCount(forum.getTopicCount()+1); //主题数量 forum.setArticleCount(forum.getArticleCount()+1); //回复数量(主题+回复) forum.setLastTopic(topic); //最后发表主题
getSession().update(forum); } } |
因为topicaction中的show()和对用的页面还没写,所以发完新帖后提交,显示的是空白页面,此时在空白页面中右键属性:
Id为空值,所以要在TopicAction中传id过去:
…… /** 发新帖 */ public String add() throws Exception { //封装对象 Topic topic = new Topic(); //需要在action中封装的数据: //>>a.表单中的参数 topic.setTitle(model.getTitle()); topic.setContent(model.getContent()); topic.setForum(forumService.getById(forumId)); //>>b.在显示层才能获得的数据 topic.setAuthor(getCurrentUser()); //当前登录的用户 topic.setIpAddr(getRequestIp()); //客户端的IP地址
//调用业务方法 topicService.save(topic);
ActionContext.getContext().put("topicId", topic.getId()); return "toShow"; //转到当前这个 新主题的页面 } |
Struts.xml:
<!-- 论坛:主题相关 --> <action name="topic_*" class="topicAction" method="{1}"> <result name="show">/WEB-INF/jsp/topicAction/show.jsp</result> <result name="addUI">/WEB-INF/jsp/topicAction/addUI.jsp</result> <result name="toShow" type="redirectAction">topic_show?id=${#topicId}</result> </action> |
这样再提交时,便有了id值:
显示置顶帖,精华帖,普通贴图标的小技巧:
将相应图片名称改成相应type值:
在ForumAction对应的show.jsp页面中引入图片时即可这么写:
<img src="${pageContext.request.contextPath}/style/images/topicType_${type}.gif" /> |
效果图:
主题展示(TopicAction中的show()):
TopicAction:
…… public class TopicAction extends BaseAction<Topic>{ …… /** 显示单个主题 */ public String show() throws Exception { //准备数据:topic Topic topic = topicService.getById(model.getId()); ActionContext.getContext().put("topic", topic);
//准备数据:replyList List<Reply> replyList = topicService.findByTopic(topic); ActionContext.getContext().put("replyList", replyList);
return "show"; } …… } |
TopicServiceImpl:
public class TopicServiceImpl extends DaoSupportImpl<Topic> implements TopicService{ …… /** * 查询指定主题的回复,最新回复排到最后 */ public List<Reply> findByTopic(Topic topic) { return getSession().createQuery(// "from Reply r where r.topic = ? order by r.postTime")// .setParameter(0, topic)// .list(); } …… } |
对应静态页面:
TopicAction对应的show.jsp:
…… <!--内容显示--> <div id="MainArea"> <div id="PageHead"></div> <center> <div class="ItemBlock_Title1" style="width: 98%"> <font class="MenuPoint"> > </font> <s:a action="forum_list">论坛</s:a> <font class="MenuPoint"> > </font> <s:a action="forum_show?id=%{#topic.forum.id}">${topic.forum.name }</s:a> <font class="MenuPoint"> >> </font> 帖子阅读 <span style="margin-left:30px;"><s:a action="topic_addUI?forumId=%{#topic.forum.id}"> <img align="absmiddle" src="${pageContext.request.contextPath}/style/blue/images/button/publishNewTopic.png"/></s:a> </span> </div>
<div class="ForumPageTableBorder dataContainer" datakey="replyList">
<!--显示主题标题等--> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr valign="bottom"> <td width="3" class="ForumPageTableTitleLeft"> </td> <td class="ForumPageTableTitle"><b>本帖主题:${topic.title }</b></td> <td class="ForumPageTableTitle" align="right" style="padding-right:12px;"> <s:a class="detail" action="reply_addUI?topicId=%{#topic.id}"><img border="0" src="${pageContext.request.contextPath}/style/images/reply.gif" />回复</s:a> <a href="moveUI.html"><img border="0" src="${pageContext.request.contextPath}/style/images/edit.gif" />移动到其他版块</a> <a href="#" onClick="return confirm(‘要把本主题设为精华吗?‘)"><img border="0" src="${pageContext.request.contextPath}/style/images/forum_hot.gif" />精华</a> <a href="#" onClick="return confirm(‘要把本主题设为置顶吗?‘)"><img border="0" src="${pageContext.request.contextPath}/style/images/forum_top.gif" />置顶</a> <a href="#" onClick="return confirm(‘要把本主题设为普通吗?‘)"><img border="0" src="${pageContext.request.contextPath}/style/images/forum_comm.gif" />普通</a> </td> <td width="3" class="ForumPageTableTitleRight"> </td> </tr> <tr height="1" class="ForumPageTableTitleLine"><td colspan="4"></td></tr> </table>
<!-- ~~~~~~~~~~~~~~~ 显示主帖 ~~~~~~~~~~~~~~~ --> <div class="ListArea"> <table border="0" cellpadding="0" cellspacing="1" width="100%"> <tr> <td rowspan="3" width="130" class="PhotoArea" align="center" valign="top"> <!--作者头像--> <div class="AuthorPhoto"> <img border="0" width="110" height="110" src="${pageContext.request.contextPath}/style/images/defaultAvatar.gif" onerror="this.onerror=null; this.src=‘${pageContext.request.contextPath}/style/images/defaultAvatar.gif‘;" /> </div> <!--作者名称--> <div class="AuthorName">${topic.author.name }</div> </td> <td align="center"> <ul class="TopicFunc"> <!--操作列表--> <li class="TopicFuncLi"> <a class="detail" href="${pageContext.request.contextPath}/BBS_Topic/saveUI.html"><img border="0" src="${pageContext.request.contextPath}/style/images/edit.gif" />编辑</a> <a class="detail" href="#" onClick="return confirm(‘确定要删除本帖吗?‘)"><img border="0" src="${pageContext.request.contextPath}/style/images/delete.gif" />删除</a> </li> <!-- 文章标题 --> <li class="TopicSubject"> <s:property value="#topic.title" escape="true"/> </li> </ul> </td> </tr> <tr><!-- 文章内容 --> <td valign="top" align="center"> <div class="Content">${topic.content }</div> </td> </tr> <tr><!--显示楼层等信息--> <td class="Footer" height="28" align="center" valign="bottom"> <ul style="margin: 0px; width: 98%;"> <li style="float: left; line-height:18px;"><font color=#C30000>[楼主]</font> <s:date name="#topic.postTime" format="yyyy-MM-dd HH:mm:ss"/> </li> <li style="float: right;"><a href="javascript:scroll(0,0)"> <img border="0" src="${pageContext.request.contextPath}/style/images/top.gif" /></a> </li> </ul> </td> </tr> </table> </div> <!-- ~~~~~~~~~~~~~~~ 显示主帖结束 ~~~~~~~~~~~~~~~ -->
<!-- ~~~~~~~~~~~~~~~ 显示回复列表 ~~~~~~~~~~~~~~~ --> <s:iterator value="replyList" status="status"> <div class="ListArea template"> <table border="0" cellpadding="0" cellspacing="1" width="100%"> <tr> <td rowspan="3" width="130" class="PhotoArea" align="center" valign="top"> <!--作者头像--> <div class="AuthorPhoto"> <img border="0" width="110" height="110" src="${pageContext.request.contextPath}/style/images/defaultAvatar.gif" onerror="this.onerror=null; this.src=‘${pageContext.request.contextPath}/style/images/defaultAvatar.gif‘;" /> </div> <!--作者名称--> <div class="AuthorName">${author.name }</div> </td> <td align="center"> <ul class="TopicFunc"> <!--操作列表--> <li class="TopicFuncLi"> <a class="detail" href="${pageContext.request.contextPath}/BBS_Topic/saveUI.html"><img border="0" src="${pageContext.request.contextPath}/style/images/edit.gif" />编辑</a> <a class="detail" href="#" onClick="return confirm(‘确定要删除本帖吗?‘)"><img border="0" src="${pageContext.request.contextPath}/style/images/delete.gif" />删除</a> </li> <%-- <!-- 文章表情与标题 --> <li class="TopicSubject"> <img width="19" height="19" src="${pageContext.request.contextPath}/style/images/face/${reply.faceIcon}"/> ${reply.title} </li> --%> </ul> </td> </tr> <tr><!-- 文章内容 --> <td valign="top" align="center"> <div class="Content">${content}</div> </td> </tr> <tr><!--显示楼层等信息--> <td class="Footer" height="28" align="center" valign="bottom"> <ul style="margin: 0px; width: 98%;"> <li style="float: left; line-height:18px;"><font color=#C30000>[${status.count}楼]</font> <s:date name="postTime" format="yyyy-MM-dd HH:mm:ss"/> </li> <li style="float: right;"><a href="javascript:scroll(0,0)"> <img border="0" src="${pageContext.request.contextPath}/style/images/top.gif" /></a> </li> </ul> </td> </tr> </table> </div> </s:iterator> <!-- ~~~~~~~~~~~~~~~ 显示回复列表结束 ~~~~~~~~~~~~~~~ --> </div>
<div class="ForumPageTableBorder" style="margin-top: 25px;"> <table width="100%" border="0" cellspacing="0" cellpadding="0"> <tr valign="bottom"> <td width="3" class="ForumPageTableTitleLeft"> </td> <td class="ForumPageTableTitle"><b>快速回复</b></td> <td width="3" class="ForumPageTableTitleRight"> </td> </tr> <tr height="1" class="ForumPageTableTitleLine"> <td colspan="3"></td> </tr> </table> </div> </center>
<!--快速回复--> <div class="QuictReply"> <form action=""> <div style="padding-left: 3px;"> <table border="0" cellspacing="1" width="98%" cellpadding="5" class="TableStyle"> <!-- <tr height="30" class="Tint"> <td width="50px" class="Deep"><b>标题</b></td> <td class="no_color_bg"> <input type="text" name="title" class="InputStyle" value="回复:昨天发现在表单里删除的图片" style="width:90%"/> </td> </tr> --> <tr class="Tint" height="200"> <td valign="top" rowspan="2" class="Deep"><b>内容</b></td> <td valign="top" class="no_color_bg"> <textarea name="content" style="width: 95%; height: 300px"></textarea> </td> </tr> <tr height="30" class="Tint"> <td class="no_color_bg" colspan="2" align="center"> <input type="image" src="${pageContext.request.contextPath}/style/blue/images/button/submit.PNG" style="margin-right:15px;"/> </td> </tr> </table> </div> </form> </div> </div> …… |
Tips: <s:property value="#topic.title" escape="true"/>
escape="true"这是转义的意思.
主题显示页面的顶部,有快速回复的按钮:
相应链接action="reply_addUI?topicId=%{#topic.id}
既然是回复,就要指定要回复的主题.所以要传递topicId.在ReplyAction中以属性驱动的方式封装该参数:
private Long topicId; public Long getTopicId() { return topicId; }
public void setTopicId(Long topicId) { this.topicId = topicId; } |
回复
回复和发帖差不多.
ReplyAction:
@Controller @Scope("prototype") public class ReplyAction extends BaseAction<Reply>{
private Long topicId;
/** 发表回复页面 */ public String addUI() throws Exception { //准备数据 Topic topic = topicService.getById(topicId); ActionContext.getContext().put("topic", topic); return "addUI"; }
/** 发表回复 */ public String add() throws Exception { Reply reply = new Reply(); //封装对象 //a.表单中的参数 reply.setContent(model.getContent()); reply.setTopic(topicService.getById(topicId));
//b.在显示层才能获取的信息 reply.setAuthor(getCurrentUser()); //当前登录的用户 reply.setIpAddr(getRequestIp()); //客户的ip地址
//调用业务方法 replyService.save(reply);
return "toTopicShow"; //转到当前回复所属的主题显示页面 }
//--------------------- public Long getTopicId() { return topicId; }
public void setTopicId(Long topicId) { this.topicId = topicId; } } |
ReplyServiceImpl:
@Service @Transactional public class ReplyServiceImpl extends DaoSupportImpl<Reply> implements ReplyService{
/** * 重写save(),处理特殊属性 */ public void save(Reply reply) { reply.setPostTime(new Date()); //发表时间为当前时间 reply.setDeleted(false); //默认为未删除
getSession().save(reply); //处理Forum和Topic中的特殊属性 Topic topic = reply.getTopic(); Forum forum = topic.getForum();
forum.setArticleCount(forum.getArticleCount()+1); //版块的文章数量(主题+回复)
topic.setReplyCount(topic.getReplyCount()+1); //主题回复数量 topic.setLastReply(reply); //主题的最后回复 topic.setLastUpdateTime(reply.getPostTime()); //主题的最后更新时间 默认为最后回复时间
getSession().update(forum); getSession().update(topic); } } |
页面需求: (其中表情和标题是不需要的)
ReplyAction对应的addUI.jsp:
…… <!--显示表单内容--> <div id="MainArea"> <s:form action="reply_add" sccStyle="margin: 0; padding: 0;"> <s:hidden name="topicId"></s:hidden> <div id="PageHead"></div> <center> <div class="ItemBlock_Title1"> <div width=85% style="float:left"> <font class="MenuPoint"> > </font> <s:a action="forum_list">论坛</s:a> <font class="MenuPoint"> > </font> <s:a action="forum_show?id=%{#topic.forum.id}">${topic.forum.name }</s:a> <font class="MenuPoint"> >> </font> 帖子回复 </div> </div> <div class="ItemBlockBorder"> <table border="0" cellspacing="1" cellpadding="1" width="100%" id="InputArea"> <tr> <td class="InputAreaBg" height="30"><div class="InputTitle">帖子主题</div></td> <td class="InputAreaBg"><div class="InputContent">${topic.title }</div></td> </tr> <tr height="240"> <td class="InputAreaBg"><div class="InputTitle">内容</div></td> <td class="InputAreaBg"><div class="InputContent"><s:textarea name="content" cssStyle="width:650px;height:200px;"></s:textarea></div></td> </tr> <tr height="30"> <td class="InputAreaBg" colspan="2" align="center"> <input type="image" src="${pageContext.request.contextPath}/style/blue/images/button/submit.PNG" style="margin-right:15px;"/> <a href="javascript:history.go(-1);"><img src="${pageContext.request.contextPath}/style/blue/images/button/goBack.png"/></a> </td> </tr> </table> </div> </center> </s:form> </div> …… |
在主题展示列表页面的底部,有快速回复的功能,所以得修改TopicAction对应的show.jsp:
<!--快速回复--> <div class="QuictReply"> <s:form action="reply_add?topicId=%{#topic.id}"> <div style="padding-left: 3px;"> <table border="0" cellspacing="1" width="98%" cellpadding="5" class="TableStyle"> <tr class="Tint" height="200"> <td valign="top" rowspan="2" class="Deep"><b>内容</b></td> <td valign="top" class="no_color_bg"> <s:textarea name="content" cssStyle="width: 95%; height: 300px"></s:textarea> </td> </tr> <tr height="30" class="Tint"> <td class="no_color_bg" colspan="2" align="center"> <input type="image" src="${pageContext.request.contextPath}/style/blue/images/button/submit.PNG" style="margin-right:15px;"/> </td> </tr> </table> </div> </s:form> </div> |
Struts.xml:
<!-- 论坛:主题相关 --> <action name="topic_*" class="topicAction" method="{1}"> <result name="show">/WEB-INF/jsp/topicAction/show.jsp</result> <result name="addUI">/WEB-INF/jsp/topicAction/addUI.jsp</result> <result name="toShow" type="redirectAction">topic_show?id=${#topicId}</result> </action>
<!-- 论坛:回复相关 --> <action name="reply_*" class="replyAction" method="{1}"> <result name="addUI">/WEB-INF/jsp/replyAction/addUI.jsp</result> <result name="toTopicShow" type="redirectAction">topic_show?id=${topicId}</result> </action> |
Tips: topic_show?id=${#topicId}
这两个模块,发帖和发表回复的请求发出时,页面跳转的过程是一样的:addUI.jspàaction中的add()àshow.jsp
最后跳转到show.jsp时,都需要topic的id值.
当发表新主题时,从addUI.jsp开始,就一直没有将topic的id值传递过来(传递的是forumId),所以才在TopicAction中将topicId给put进map域中,所以取出时要加#;
而发表回复时,从addUI.jsp开始,就传递了topicId,所以直接取出来就好,不需要加#.
小技巧:eclipse有自动编译的功能,但有时候会失灵.因此某些时候,看着代码没错,但页面显示就是不对.这时可以在代码中空白位置加个空格再保存,目的是让eclipse再次自动编译,然后刷新页面就可以看到效果了.
四.在BBS中使用FCKeditor
在线编辑器:
老版:FCKeditor(Html + Css + JavaScript + Servlet处理上传的文件)
新版:CKeditor
就是用JS实现的一个网页版的html代码生成器,最终的结果是一段html代码。
原理是动态生成html代码。
使用步骤:
1.导入fckeditor.js文件à2.显示文本域à3.显示为FCKeditor
测试_20130706.html:
<html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8">
<!-- 1,导入js文件 --> <script type="text/javascript" src="fckeditor.js"></script>
</head> <body>
<!-- 2,显示文本域 --> <form action=""> <textarea name="content"> </textarea>
<!-- 3,显示为FCKeditor --> <script type="text/javascript"> var oFCKeditor = new FCKeditor( ‘content‘ ) ; // 参数就是提交表单时的参数名(name的值)
// 必须要指定的属性。 //用于指定fckeditor的核心文件的存放路径(editor文件夹所在的路径)。 // 要求一定要以‘/‘结尾。 oFCKeditor.BasePath = "./";
// 一些配置 oFCKeditor.Height = 300 ; oFCKeditor.Width = 700 ; //oFCKeditor.Value = ‘itcast‘ ; oFCKeditor.ToolbarSet = "bbs"; // 提定工具栏的功能项配置,默认是Default
// oFCKeditor.Create() ; // 创建FCKeditor并在当前位置显示出来。 oFCKeditor.ReplaceTextarea(); // 替换指定的textarea为FCKeditor </script>
<br><input type="submit" value="提交"> </form> <hr> <a href="#">return</a> </body> </html> |
效果如下:
Tips:FCKeditor有自己默认的配置文件,要修改一些属性(比如功能选项,表情等)的话,通常都要采用这样的方式:自己建一个配置文件,如myconfig.js,在默认配置文件fckconfig.js中声明还要加载myconfig.js,然后再把想要修改的配置写到
myconfig.js中(不想改的不写),就好了.
原理:默认先加载默认配置文件,声明后还会加载自己写的配置文件,这时前面加载的配置会被后面加载的相同的配置所覆盖.
在默认配置文件中声明还要加载自己写的配置文件:
myonfig.js:
// 1. 自定义 ToolbarSet FCKConfig.ToolbarSets["simple"] = [ [‘Bold‘,‘Italic‘,‘Underline‘], [‘Link‘,‘Unlink‘], [‘Image‘,‘Smiley‘,‘SpecialChar‘], [‘FontName‘], [‘FontSize‘], [‘TextColor‘,‘BGColor‘], ] ;
FCKConfig.ToolbarSets["bbs"] = [ [‘NewPage‘,‘RemoveFormat‘], [‘Bold‘,‘Italic‘,‘Underline‘], [‘Subscript‘,‘Superscript‘], [‘JustifyLeft‘,‘JustifyCenter‘,‘JustifyRight‘], [‘Link‘,‘Unlink‘], [‘Image‘,‘Smiley‘,‘SpecialChar‘], [‘Table‘], [‘OrderedList‘,‘UnorderedList‘,‘-‘,‘Outdent‘,‘Indent‘], [‘FontName‘], [‘FontSize‘], [‘TextColor‘,‘BGColor‘], [‘FitWindow‘] ] ;
FCKConfig.ToolbarSets["admin"] = [ [‘Source‘,‘DocProps‘,‘-‘,‘Save‘,‘NewPage‘,‘Preview‘,‘-‘,‘Templates‘], [‘Cut‘,‘Copy‘,‘Paste‘,‘PasteText‘,‘PasteWord‘,‘-‘,‘Print‘,‘SpellCheck‘], [‘Undo‘,‘Redo‘,‘-‘,‘Find‘,‘Replace‘,‘-‘,‘SelectAll‘,‘RemoveFormat‘], [‘Bold‘,‘Italic‘,‘Underline‘,‘StrikeThrough‘,‘-‘,‘Subscript‘,‘Superscript‘], [‘OrderedList‘,‘UnorderedList‘,‘-‘,‘Outdent‘,‘Indent‘,‘Blockquote‘,‘CreateDiv‘], [‘JustifyLeft‘,‘JustifyCenter‘,‘JustifyRight‘,‘JustifyFull‘], [‘Form‘,‘Checkbox‘,‘Radio‘,‘TextField‘,‘Textarea‘,‘Select‘,‘Button‘,‘ImageButton‘,‘HiddenField‘], [‘Link‘,‘Unlink‘,‘Anchor‘], [‘Image‘,‘Flash‘,‘Table‘,‘Rule‘,‘Smiley‘,‘SpecialChar‘,‘PageBreak‘], [‘Style‘,‘FontFormat‘,‘FontName‘,‘FontSize‘], [‘TextColor‘,‘BGColor‘], [‘FitWindow‘,‘ShowBlocks‘,‘-‘,‘About‘] // No comma for the last row. ] ;
// 是否开启简单功能与高级功能显示 if(typeof(FCKConfig.EnableAdvanceTable) == "undefined"){ // 在页面中调用fckeditor时指定的 EnableAdvanceTable 的值会先被调用。 FCKConfig.EnableAdvanceTable = false; // 默认为false } FCKConfig.AdvanceTableNum = 0; FCKConfig.AdvanceTable = [1,3,7,8,9,12];
// 2. 添加中文字体与大小 FCKConfig.FontNames =‘宋体;楷体_GB2312;黑体;隶书;Times New Roman;Arial‘ ; FCKConfig.FontSizes =‘9/最小;12/较小;16/中等;20/较大;36/最大;54/更大;‘;
// 3. 修改 "回车" 和 "shift + 回车" 的样式 FCKConfig.EnterMode = ‘br‘ ; // p | div | br FCKConfig.ShiftEnterMode = ‘p‘ ; // p | div | br
// 4. 更换表情图片 FCKConfig.SmileyPath = FCKConfig.BasePath + ‘images/smiley/wangwang/‘ ; // 表情图片所在的文件夹 // 列出表情图片的文件名 FCKConfig.SmileyImages = [‘0.gif‘,‘1.gif‘,‘2.gif‘,‘3.gif‘,‘4.gif‘,‘5.gif‘,‘6.gif‘,‘7.gif‘,‘8.gif‘,‘9.gif‘,‘10.gif‘,‘11.gif‘,‘12.gif‘,‘13.gif‘,‘14.gif‘,‘15.gif‘,‘16.gif‘,‘17.gif‘,‘18.gif‘,‘19.gif‘,‘20.gif‘,‘21.gif‘,‘22.gif‘,‘23.gif‘,‘24.gif‘,‘25.gif‘,‘26.gif‘,‘27.gif‘,‘28.gif‘,‘29.gif‘,‘30.gif‘,‘31.gif‘,‘32.gif‘,‘33.gif‘,‘34.gif‘,‘35.gif‘,‘36.gif‘,‘37.gif‘,‘38.gif‘,‘39.gif‘,‘40.gif‘,‘41.gif‘,‘42.gif‘,‘43.gif‘,‘44.gif‘,‘45.gif‘,‘46.gif‘,‘47.gif‘,‘48.gif‘,‘49.gif‘,‘50.gif‘,‘51.gif‘,‘52.gif‘,‘53.gif‘,‘54.gif‘,‘55.gif‘,‘56.gif‘,‘57.gif‘,‘58.gif‘,‘59.gif‘,‘60.gif‘,‘61.gif‘,‘62.gif‘,‘63.gif‘,‘64.gif‘,‘65.gif‘,‘66.gif‘,‘67.gif‘,‘68.gif‘,‘69.gif‘,‘70.gif‘,‘71.gif‘,‘72.gif‘,‘73.gif‘,‘74.gif‘,‘75.gif‘,‘76.gif‘,‘77.gif‘,‘78.gif‘,‘79.gif‘,‘80.gif‘,‘81.gif‘,‘82.gif‘,‘83.gif‘,‘84.gif‘,‘85.gif‘,‘86.gif‘,‘87.gif‘,‘88.gif‘,‘89.gif‘,‘90.gif‘,‘91.gif‘,‘92.gif‘,‘93.gif‘,‘94.gif‘,‘95.gif‘,‘96.gif‘,‘97.gif‘,‘98.gif‘,‘test.gif‘] ; FCKConfig.SmileyColumns = 8 ; FCKConfig.SmileyWindowWidth = 668 ; FCKConfig.SmileyWindowHeight = 480 ;
// 5. 设置允许上传的图片类型的扩展名列表 FCKConfig.ImageUploadAllowedExtensions = ".(jpg|gif|jpeg|png|bmp)$" ; // empty for all
// 其它需要修改的配置 ... FCKConfig.LinkDlgHideTarget = true; // false ; FCKConfig.LinkDlgHideAdvanced = true; // false ;
FCKConfig.ImageDlgHideLink = true; // false ; FCKConfig.ImageDlgHideAdvanced = true; // false
FCKConfig.LinkUpload = false; |
这是老师写好的myconfig.js,可以用现成的,还想要修改字体,图片等的话可以在里面自行修改.
- 实际操作: (也就两步,都是复制粘贴的工作)
将整个fckedit文件拷贝到WebContent根目录下:
然后打开topicAction对应的addUI.jsp(发帖页面) ,show.jsp(主题展示页面的底部有快速回复功能) 和replyAction对应的addUI.jsp(发表回复页面),在这三个页面的顶部加入下面这些代码:
<%-- 使用FCKeditor --%> <script type="text/javascript" src="${pageContext.request.contextPath}/fckeditor/fckeditor.js"></script> <script type="text/javascript"> $(function() { var oFCKeditor = new FCKeditor(‘content‘); // 参数就是提交表单时的参数名 // 必须要指定的属性。用于指定fckeditor的核心文件的存放路径(editor文件夹所在的路径)。要求一定要以‘/‘结尾。 oFCKeditor.BasePath = "${pageContext.request.contextPath}/fckeditor/"; oFCKeditor.Width = "90%";//指定宽度 oFCKeditor.ToolbarSet = "bbs"; // 提定工具栏的功能项配置,默认是Default oFCKeditor.ReplaceTextarea(); // 替换指定的textarea为FCKeditor }); </script> |
Tips:这三个页面的文本域的name值都为content;
这段代码本来是一定要出现在文本域之后的,不然会提示找不到name值为content的文本域.之所以能这么做,是因为加了$(function(){});表明等页面加载完成之后再执行这些代码.
在eclipse自带的浏览器中显示不出来--.
效果如下:
五.导入演示数据(为实现和展示分页效果为准备)
他没给演示数据….自己手动添加,,,,,-------…