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

1、生成challenge数据表

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

1
2
3
/*创建挑战书记录表*/
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文件,里面填写如下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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进行操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
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类,里面涉及对数据的校验

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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定义,内容如下:

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


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

wKioL1PyHLzho6wIAAEXjD_lwkw395.jpg


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文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!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文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/*********************************************************************/
/*                           系统下战书样式                          */
/*********************************************************************/
.challenge_content_wrapper{
    width710px;
    margin20px auto;
    font-size13px;
    border1px solid #CCC;
    overflowhidden;
}
 
.challenge_textarea_wrapper{
    margin5px;
    padding8px 5px;
    line-height15px;
    box-shadow: 0 1px 1px rgba(0,0,0,.1inset;
    border-radius: 3px;
    background#FFF;
    border1px solid #ccc;
    color#222;    
}
 
.challenge_richtext_wrapper{
    margin5px;
    padding0;
    line-height15px;
    background#FFF;
    color#222;    
}
/*********************************************************************/
/*                       下战书TextArea公共样式                      */
/*********************************************************************/
.challenge_textarea_wrapper textarea{
    height20px;
    line-height20px;
    width100%;
    vertical-alignbottom;
    /*移除滚动条*/
    overflowhidden;
    /*自动换行*/
    word-wrap: break-word;
    /*移除边框*/
    border0;
    background-color#FFF;
    /*去除改变大小拖拽柄*/
    resize: none;
    font-size13px;
    border-radius: 5px;
}
 
.challenge_textarea_wrapper textarea:focus{
    outline0;
    outline-offset: -2px;
}
 
/*********************************************************************/
/*                            药方说明样式                           */
/*********************************************************************/
.challenge_text_desc{
    margin25px 5px 0 6px;
}
 
.challenge_depart_wrapper{
    margin5px;
    line-height15px;
    background-color#FFF;
    color#222;
    overflowhidden;
}
 
.challenge_depart_item, .challenge_depart_item_selected{
    floatleft;
    min-width55px;
    widthauto;
    height22px;
    line-height22px;
    font-size13px;
    text-aligncenter;
    margin-right8px;
    background-color#E1EAF2;
    border-radius: 6px;
    cursorpointer;
}
 
.challenge_depart_item:hover, .challenge_depart_item_selected{
    color#FFF;
    background-color#225599;
}
 
#challenge_prescript_id{
    height80px;
    min-height80px;
}
 
.challenge_editor_switch{
    floatright;
    width16px;
    height16px;
    /*若无属性背景图片无法显示*/
    display: inline-block;
    background-imageurl(../navigation/navigation.png);
    background-repeatno-repeat;
    background-position-80px -127px;
    cursorpointer;
}
 
/*********************************************************************/
/*                            文本提示信息                           */
/*********************************************************************/
.challenge_hint_info{
    text-alignright;
    margin-right5px;
    font-size13px;
    color#999;
    displaynone;
}
 
.challenge_hint_warn{
    color#C33;
}
 
/*********************************************************************/
/*                             下战书按钮                            */
/*********************************************************************/
.challenge_confirm_publish{
    floatright;
    margin0 3px 10px 0;
    color#FFF;
    font-size14px;
    line-height1.7;
    padding4px 10px;
    display: inline-block;
    text-aligncenter;
    background-color#1575D5;
    border1px solid #0D6EB8;
    border-radius: 3px;
    text-shadow0 -1px 0 rgba(0,0,0,.5);
    box-shadow: 0 1px 0 rgba(255,255,255,.2inset,0 1px 0 rgba(0,0,0,.2);
}


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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
(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"nullfunction(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,在菜单上选择“下战书”,在下战书页面输入相应的信息后点击“发布”,如下图:

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

wKioL1PyIXWAi-DuAAIgQcOsn8s732.jpg

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