【斗医】【16】Web应用开发50天

    在本文完成下挑战书的功能,其中里面也涉及到富文本编辑器的使用

1、生成challenge数据表

  在D:\medical\war\etc\db.txt文本中增加数据表challenge脚本,然后通过navicat工具把数据表在mysql中生成    

/*创建挑战书记录表*/
CREATE TABLE CHALLENGE(challengeId int PRIMARY KEY NOT NULL, userId VARCHAR(20), title VARCHAR(128), depId int, prescript TEXT, challengers VARCHAR(256))
ENGINE=InnoDB DEFAULT CHARSET=UTF8

2、challenge数据表hibernate配制

  在D:\medical\war\etc\mapping目录下生成challenge.hbm.xml文件,里面填写如下内容

<?xml version="1.0" encoding="utf-8"?>  
<!DOCTYPE hibernate-mapping PUBLIC  
       "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
       "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 
<hibernate-mapping package="com.medical.server.dao">
   <class name="ChallengeDAO" table="CHALLENGE">
    <id name="challengeId" column="challengeId" type="int">
    <generator class="increment"></generator>
    </id>
       <property name="userId" column="userId" />
       <property name="depId" column="depId" />
       <property name="title" column="title" />
       <property name="prescript" column="prescript" />
       <property name="challengers" column="challengers" />
   </class>
</hibernate-mapping>

3、定义challenge.hbm.xml对应的POJO类

package com.medical.server.dao;

/**
* 斗医系统发布挑战书处理类
*
* @author qingkechina 2014-08-18
*/
public class ChallengeDAO
{
   /**
    * 挑战ID
    */
   private int challengeId = 0;
   
   /**
    * 挑战人
    */
   private String userId = null;
   
   /**
    * 科室ID
    */
   private int depId = 0;
   
   /**
    * 挑战标题
    */
   private String title = null;
   
   /**
    * 挑战内容
    */
   private String prescript = null;
   
   /**
    * 被挑战人
    */
   private String challengers = null;
   
   public int getChallengeId()
   {
       return challengeId;
   }
   
   public void setChallengeId(int challengeId)
   {
       this.challengeId = challengeId;
   }
   
   public String getUserId()
   {
       return userId;
   }
   
   public void setUserId(String userId)
   {
       this.userId = userId;
   }
   
   public int getDepId()
   {
       return depId;
   }
   
   public void setDepId(int depId)
   {
       this.depId = depId;
   }
   
   public String getTitle()
   {
       return title;
   }
   
   public void setTitle(String title)
   {
       this.title = title;
   }
   
   public String getPrescript()
   {
       return prescript;
   }
   
   public void setPrescript(String prescript)
   {
       this.prescript = prescript;
   }
   
   public String getChallengers()
   {
       return challengers;
   }
   
   public void setChallengers(String challengers)
   {
       this.challengers = challengers;
   }
}

4、由于涉及到对数据表challenge数据的读取与写入操作,所以定义一个ChallengeUtil类对POJO进行操作

package com.medical.server.util;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.medical.frame.util.FrameDBUtil;
import com.medical.server.dao.ChallengeDAO;

/**
* 斗医系统服务端挑战书工具类
*
* @author qingkechina 2014-08-18
*/
public class ChallengeUtil
{
   /**
    * 把挑战书记录入库
    */
   public static void insertChallenge(String userId, String title, int depId, String prescript, String challengers)
   {
       ChallengeDAO challengeDao = new ChallengeDAO();
       challengeDao.setChallengers(challengers);
       challengeDao.setDepId(depId);
       challengeDao.setPrescript(prescript);
       challengeDao.setTitle(title);
       challengeDao.setUserId(userId);
       
       Session session = FrameDBUtil.openSession();
       Transaction transaction = session.beginTransaction();
       session.save(challengeDao);
       transaction.commit();
       FrameDBUtil.closeSession();
   }
   
}

5、当用户登录系统在浏览器中发布挑战书时,需要调用到业务逻辑处理,所以定义PublishChallengeAction业务Java类,里面涉及对数据的校验

package com.medical.server.data;

import com.google.gson.Gson;
import com.medical.frame.FrameCache;
import com.medical.frame.FrameDefaultAction;
import com.medical.frame.FrameException;
import com.medical.frame.bean.FramePathBean;
import com.medical.frame.bean.FrameResultBean;
import com.medical.frame.constant.FrameErrorCode;
import com.medical.frame.util.FrameUtil;
import com.medical.server.dao.UserDAO;
import com.medical.server.util.ChallengeUtil;

/**
* 斗医系统发布挑战书处理类
*
* @author qingkechina 2014-08-18
*/
public class PublishChallengeAction extends FrameDefaultAction
{
   /**
    * 全局Gson对象
    */
   private final static Gson gson = new Gson();
   
   @Override
   public String execute()
       throws FrameException
   {
       // 用户尚未登录系统
       UserDAO loginUser = FrameCache.getInstance().getUserBySession(session);
       if (loginUser == null)
       {
           FrameResultBean resultBean = new FrameResultBean();
           resultBean.setErrorCode(FrameErrorCode.USER_NOT_LOGIN_ERROR);
           resultBean.setErrorDesc(FrameUtil.getErrorDescByCode(resultBean.getErrorCode()));
           return gson.toJson(resultBean);
       }
       
       // 获取挑战标题
       String title = this.getParameter("title");
       if (FrameUtil.isEmpty(title))
       {
           FrameResultBean resultBean = new FrameResultBean();
           resultBean.setErrorCode(FrameErrorCode.CHANLLENGE_TITLE_EMPTY);
           resultBean.setErrorDesc(FrameUtil.getErrorDescByCode(resultBean.getErrorCode()));
           return gson.toJson(resultBean);
       }
       
       // 获取科室ID
       int depId = -1;
       String departValue = getParameter("departId");
       if (FrameUtil.isEmpty(departValue) == false)
       {
           depId = Integer.valueOf(departValue);
       }
       if (depId == -1)
       {
           FrameResultBean resultBean = new FrameResultBean();
           resultBean.setErrorCode(FrameErrorCode.CHANLLENGE_DEP_EMPTY);
           resultBean.setErrorDesc(FrameUtil.getErrorDescByCode(resultBean.getErrorCode()));
           return gson.toJson(resultBean);
       }
       
       // 获取挑战药方内容、挑战人
       String prescript = getParameter("prescript");
       String challengers = getParameter("challengers");
       // 写入数据库
       ChallengeUtil.insertChallenge(loginUser.getUserId(), title, depId, prescript, challengers);
       
       // 设置返回路径
       FramePathBean pathBean = new FramePathBean();
       pathBean.setErrorCode(200);
       pathBean.setForwardPath("index.act?timestamp=" + System.currentTimeMillis());
       return gson.toJson(pathBean);
   }
}

6、如何调用到PublishChallengeAction业务处理类呢?我们在D:\medical\war\WEB-INF\config\challenge\challenge-data.xm定义,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<business-config>
   <!--发布挑战书-->
   <business name="publishChallenge" business-class="com.medical.server.data.PublishChallengeAction" />
</business-config>

从上面的配置可以看出,当界面上触发按钮时,调用到publishChallenge的JS方法,从而触发业务逻辑。那么界面是什么样的呢?

7、在上面的这个界面原型中,药方使用纯文本描述,理论上讲只要使用textarea标签就可以了,考虑到有时还可能会上传一些中药材的图片或病情图片,所以有一个上传图片的功能,这里面就需要使用HTML的富文本编辑器了。

    关于HTML富文本编辑器有很多,像百度的ueditor、Twitter的Bootstrap、Tower的Simditor等等,我们这里选择Kindeditor,为什么选择它呢?主要原因是我没有使用过,呵呵。

    下面就随着我进入Kindeditor世界吧!

(1)进入http://kindeditor.net/down.php下载

(2)下载后把kindeditor解压medical应用的war\js目录下

(3)由于我们使用的是Tomcat容器,所以只保留JSP的功能即可(即删除kindeditor下的asp、asp.net和php目录)

(4)由于medical应用并非发布版本,所以不需要压缩后的js(即删除kindeditor-min.js、kindeditor-all-min.js文件)

(5)有一个examples文件夹,从名称上看应该是一个使用示例,我们可以通过这学习这个例子快速入门

    完成了上述准备工作,下面就可以步入kindeditor之旅了!

8、定义challenge.html来绘制出上述界面(在D:\medical\war\module\challenge下增加challenge.html文件)

<!DOCTYPE HTML>
<html>
   <head>
       <title>斗医</title>
       <!--利于搜索引擎查询-->
       <meta name="description" content="斗医是一个医学交流平台" />
       <meta name="keywords" content="医学,交流,讨论" />
       <!--设置字符集-->
       <meta http-equiv="content-type" content="text/html;charset=utf-8" />
       <!--页面不缓存-->
       <meta http-equiv="pragma" content="no-cache" />
       <meta http-equiv="cache-control" content="no-cache,must-revalidate" />
       <meta http-equiv="expires" content="Wed, 26 Feb 1997 08:21:57 GMT" />
       <!--引入外部文件-->
       <link rel="stylesheet" type="text/css" href="theme/navigation/navigation.css">
       <link rel="stylesheet" type="text/css" href="theme/challenge/challenge.css">
       <script src="js/common/jquery.js"></script>
       <script src="js/kindeditor/kindeditor-all.js"></script>
       <script src="js/common/common.js"></script>
       <script src="js/challenge/challenge.js"></script>
   </head>

   <body>
       <!--系统导航菜单-->
       <div id="system_navigation_menu"></div>

       <!--系统内容部分-->
       <div class="system_content">
           <div class="challenge_content_wrapper">
               <div class="challenge_textarea_wrapper">
                   <textarea id="challenge_title_id" placeholder="写下您的问题"></textarea>
               </div>
               <div class="challenge_hint_info">
                   已超出<label class="challenge_hint_warn" id="challenge_title_hint_id"></label>字
               </div>

               <div class="challenge_text_desc">选择所属科室(必填):</div>
               <div class="challenge_depart_wrapper" id="challenge_depart_id"></div>

               <div class="challenge_text_desc">
                   药方说明(可选):
                   <span class="challenge_editor_switch" id="challenge_editor_switcher" display="default"></span>
               </div>
               <div class="challenge_textarea_wrapper">
                   <textarea id="challenge_prescript_id" placeholder="写下病人神、色、形、态、舌象等症状......"></textarea>
               </div>
               <div class="challenge_hint_info">
                   已超出<label class="challenge_hint_warn" id="challenge_prescript_hint_id"></label>字
               </div>

               <div class="challenge_text_desc">挑战人(可选):</div>
               <div class="challenge_textarea_wrapper">
                   <textarea id="challenge_challenger_id" placeholder="选择您的挑战人"></textarea>
               </div>

               <div class="challenge_text_desc">
                   <a href="javascript:publishChallenge()" class="challenge_confirm_publish">发布</a>
               </div>
           </div>
       </div>
   </body>
</html>

9、定义上面HTML对应的CSS样式文件(在D:\medical\war\theme\challenge下增加challenge.css文件)

/*********************************************************************/
/*                           系统下战书样式                          */
/*********************************************************************/
.challenge_content_wrapper{
   width: 710px;
   margin: 20px auto;
   font-size: 13px;
   border: 1px solid #CCC;
   overflow: hidden;
}

.challenge_textarea_wrapper{
   margin: 5px;
   padding: 8px 5px;
   line-height: 15px;
   box-shadow: 0 1px 1px rgba(0,0,0,.1) inset;
   border-radius: 3px;
   background: #FFF;
   border: 1px solid #ccc;
   color: #222;    
}

.challenge_richtext_wrapper{
   margin: 5px;
   padding: 0;
   line-height: 15px;
   background: #FFF;
   color: #222;    
}
/*********************************************************************/
/*                       下战书TextArea公共样式                      */
/*********************************************************************/
.challenge_textarea_wrapper textarea{
   height: 20px;
   line-height: 20px;
   width: 100%;
   vertical-align: bottom;
   /*移除滚动条*/
   overflow: hidden;
   /*自动换行*/
   word-wrap: break-word;
   /*移除边框*/
   border: 0;
   background-color: #FFF;
   /*去除改变大小拖拽柄*/
   resize: none;
   font-size: 13px;
   border-radius: 5px;
}

.challenge_textarea_wrapper textarea:focus{
   outline: 0;
   outline-offset: -2px;
}

/*********************************************************************/
/*                            药方说明样式                           */
/*********************************************************************/
.challenge_text_desc{
   margin: 25px 5px 0 6px;
}

.challenge_depart_wrapper{
   margin: 5px;
   line-height: 15px;
   background-color: #FFF;
   color: #222;
   overflow: hidden;
}

.challenge_depart_item, .challenge_depart_item_selected{
   float: left;
   min-width: 55px;
   width: auto;
   height: 22px;
   line-height: 22px;
   font-size: 13px;
   text-align: center;
   margin-right: 8px;
   background-color: #E1EAF2;
   border-radius: 6px;
   cursor: pointer;
}

.challenge_depart_item:hover, .challenge_depart_item_selected{
   color: #FFF;
   background-color: #225599;
}

#challenge_prescript_id{
   height: 80px;
   min-height: 80px;
}

.challenge_editor_switch{
   float: right;
   width: 16px;
   height: 16px;
   /*若无属性背景图片无法显示*/
   display: inline-block;
   background-image: url(../navigation/navigation.png);
   background-repeat: no-repeat;
   background-position: -80px -127px;
   cursor: pointer;
}

/*********************************************************************/
/*                            文本提示信息                           */
/*********************************************************************/
.challenge_hint_info{
   text-align: right;
   margin-right: 5px;
   font-size: 13px;
   color: #999;
   display: none;
}

.challenge_hint_warn{
   color: #C33;
}

/*********************************************************************/
/*                             下战书按钮                            */
/*********************************************************************/
.challenge_confirm_publish{
   float: right;
   margin: 0 3px 10px 0;
   color: #FFF;
   font-size: 14px;
   line-height: 1.7;
   padding: 4px 10px;
   display: inline-block;
   text-align: center;
   background-color: #1575D5;
   border: 1px solid #0D6EB8;
   border-radius: 3px;
   text-shadow: 0 -1px 0 rgba(0,0,0,.5);
   box-shadow: 0 1px 0 rgba(255,255,255,.2) inset,0 1px 0 rgba(0,0,0,.2);
}

10、为了响应界面上的动作,所以需要在D:\medical\war\js\challenge下增加challenge.js文件,该文件中有三个方法值得读者粗略地读一下,它们分别是bindEvent2Switcher、initInputComponent和publishChallenge,分别对应富文本与纯textarea切换按钮、自适应textarea高度和发布挑战书。其内容如下:

(function( window){
   $(document).ready(function(){
       // 生成系统菜单
       generateSystemMenu();
       // 选择下战书系统菜单
       selectSystemMenu("system_challenge_menu");
       // 获取用户简要信息
       getBreifUserInfo();
       // 初始化文本框
       initInputComponent();
       // 初始化科室类别数据
       initDepartData();
       // 切换开关按钮绑定事件
       bindEvent2Switcher();
   });

   // 当前选中的科室ID
   var CURRENT_SELECTED_ITEM = -1;

   // 当前编辑器ID
   var CURRENT_HTML_EDITOR = null;

   /**
    * 初始化文本框
    */
   function initInputComponent(){        
       var textareaArray = new Array("challenge_title_id", "challenge_prescript_id", "challenge_challenger_id");
       // 进入页面"标题textarea"获取焦点
       $("#" + textareaArray[0]).focus();
       
       $.each(textareaArray, function(i, item){
           var dynamicItem = $("#" + item);
           // 绑定PlaceHolder
           bindPlaceHolder(dynamicItem);
           
           dynamicItem.bind("keyup", function(event){
               // 设置textArea高度自适应
               autoAdaptHeight(this);
               // 长度超长时给出提示信息
               setLengthHint(this);
           });
       });
   }

   /**
    * textarea高度自适应
    */
   function autoAdaptHeight(component){
       var paddingTop = parseInt($(component).css("padding-top"));
       var paddingBtm = parseInt($(component).css("padding-bottom"));
       var scrollHeight = component.scrollHeight;
       var height = $(component).height();

       // 判断是否为chrome浏览器
       if(window.navigator.userAgent.indexOf("Chrome") > 0){
           if(scrollHeight - paddingTop - paddingBtm > height){
               $(component).css("height", scrollHeight);
           }
           return;              
       }
       $(component).css("height", scrollHeight);
   }

   /**
    * textarea长度超出时提示
    */
   function setLengthHint(component){
       if(component.id == "challenge_title_id"){
           if(!component.value){
               return;
           }

           var titleId = $("#challenge_title_hint_id");
           if(component.value && component.value.length > 96){
               titleId.parent().show();
           } else {
               titleId.parent().hide();
           }

           titleId.text(component.value.length - 96);
       }
   }

   /**
    * 初始化科室类别数据
    */
   function initDepartData(){
       asyncRequest("gainDepart.data", null, function(result){
           var resultJson = eval(result);
           if(!resultJson){
               return;
           }

           $("#challenge_depart_id").empty();
           $.each(resultJson, function(i, item){
               var departItem = $("<div />").attr("class", "challenge_depart_item").attr("id", "challenge_depart_id_" + item.depId).text(item.depName);
               departItem.click(function(){
                   if(CURRENT_SELECTED_ITEM != -1 && CURRENT_SELECTED_ITEM != item.depId){
                       $("#challenge_depart_id_" + CURRENT_SELECTED_ITEM).attr("class", "challenge_depart_item");
                   }
                   CURRENT_SELECTED_ITEM = item.depId;
                   $("#challenge_depart_id_" + item.depId).attr("class", "challenge_depart_item_selected");
               });
               $("#challenge_depart_id").append(departItem);
           });
       });
   }
   
   /**
    * 切换开关按钮绑定事件
    */
   function bindEvent2Switcher(){
       var switcher = $("#challenge_editor_switcher");
       var display = switcher.attr("display");

       switcher.click(function(){
           var parent = $("#challenge_prescript_id").parent();
           if(display === "default"){
               // 设置父div样式
               parent.removeClass("challenge_textarea_wrapper").addClass("challenge_richtext_wrapper");
               // 设置编辑框的当前样式
               display = "custom";
               switcher.attr("display", "custom");
               var options = {resizeType: 0, items: ["bold", "italic", "underline", "strikethrough", "|", "insertorderedlist", "insertunorderedlist", "|", "image"]};
               CURRENT_HTML_EDITOR = KindEditor.create("#challenge_prescript_id", options);
           }
           else
           {
               // 设置父div样式
               parent.removeClass("challenge_richtext_wrapper").addClass("challenge_textarea_wrapper");
               // 设置编辑框的当前样式
               display = "default";
               switcher.attr("display", "default");
               CURRENT_HTML_EDITOR = null;
               KindEditor.remove("#challenge_prescript_id");
           }
       });
   }

   /**
    * 发布挑战书
    */
   function publishChallenge(){
       // 判断挑战话题
       var challengeTitle = $.trim($("#challenge_title_id").val());
       if(!challengeTitle){
           showSystemGlobalInfo("亲,您还没有写下问题呢~~~");
           return;
       }
       // 判断挑战科室
       if(CURRENT_SELECTED_ITEM === -1){
           showSystemGlobalInfo("亲,您还没有选择科室呢~~~");
           return;
       }
       // 挑战医方内容        
       var challengePrescript = $.trim($("#challenge_prescript_id").val());
       if(CURRENT_HTML_EDITOR){
           challengePrescript = CURRENT_HTML_EDITOR.html();
       }        
       // 挑战人
       var challengers =  $.trim($("#challenge_challenger_id").val());

       var data = {"title": challengeTitle, "departId": CURRENT_SELECTED_ITEM, "prescript": challengePrescript, "challengers": challengers};
       asyncRequest("publishChallenge.data", data, function(result)
       {
           // 跳转到相应页面
           var resultJson = eval(result);            
           top.location = resultJson.forwardPath;
       });
   }

   /**
    * 对外公开接口
    */
   window.publishChallenge = publishChallenge;
})( window );

11、下面验证一下效果:在浏览器中输入http://localhost:8080/medical,在菜单上选择“下战书”,在下战书页面输入相应的信息后点击“发布”,如下图:

12、点击发布成功后进入系统主页面,若看到内容变化不用着急,因为这个功能尚未实现,我们查看一下数据库是否把此记录追加成功?

OK,富文本的简单应用就这样实现了,后面有机会再详细研读原码!





本文出自 “青客” 博客,请务必保留此出处http://qingkechina.blog.51cto.com/5552198/1541813

【斗医】【16】Web应用开发50天,古老的榕树,5-wow.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。