第11章 基于三层架构的XML数据分页浏览设计

11.1 设计需求

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?> <table border="1" id="books"> <tr style="background-color:greenyellow"> <th id="ISBN">书号</th> <th id="title">书名</th> <th id="price">价格</th> </tr> <tr> <td>5744885</td> <td>Java programming</td> <td>80</td> </tr> <tr> <td>2323885</td> <td>C# programming</td> <td>87</td> </tr> <tr> <td>8888</td> <td>Python programming</td> <td>68</td> </tr> <tr> <td>8767885</td> <td>NodeJS</td> <td>90</td> </tr> <tr> <td>r64547565647689</td> <td>C++ programming</td> <td>68</td> </tr> <tr> <td>45634748758478</td> <td>MongoDB</td> <td>90</td> </tr> <tr> <td>675665647689</td> <td>operation system</td> <td>68</td> </tr> <tr> <td>2342353758478</td> <td>XML技术</td> <td>90</td> </tr> </table>

现提出如下设计需求:

  1. 设计合适的用户接口,可自定义浏览页面大小(即可自定义每页可展示图书条目的数量)。
  2. 基于选定的页面大小,用户可选择性地浏览"最前页"、"最后页"、"前一页"、"后一页"。
  3. 基于三层架构读取XML文档。

11.2 设计分析

11.2.1 功能分析

测试用户期望功能如下:

  1. 定义页面大小(单页浏览条目数)。
  2. 浏览最前一页。
  3. 浏览最后一页。
  4. 浏览当前页前一页
  5. 浏览当前页后一页

用户测试功能用例建模如图11.1所示。

用户测试功能用例建模
图11.1 用户测试功能用例建模

11.2.2 核心业务数据流分析

XML文档分页浏览的核心业务描述如下:

  1. 页面加载时,读取XML文档并XML文本形式返回到前端;
  2. XML文本转化为XML文档对象并保存为全局变量。
  3. 用户选择浏览页面大小(单页信息条目数)。
  4. XML文档对象、页面大小、页面总数、当前页面号、数据输出容器div作为参数,经分页处理程序处理后生成可视分页结果。

核心业务数据流如图11.2所示。

核心业务数据流
图11.2 核心业务数据流

11.2.3 文件包及文件设计分析

基于三层架构视角的文件包及文件设计如表11.1所述。

序号 包设计 文件设计
1 设计表示层包(包名:UI),抽象表示层文件容器。
  1. 设计界面主页文件main.js。
  2. 设计自定义XML_page.js(主页引入,用于分页浏览)。
  3. 主页引入jquery框架(jquery.js),辅助表示层处理。
2 设计业务逻辑层包(包名:server),抽象业务层文件容器。
  1. 设计通用业务逻辑服务文件server.js。
  2. 设计当前模块专用业务逻辑服务文件server_11.js.
3 设计数据层包(包名:data),抽象数据层文件容器。
  1. 设计XML数据存储文件books.xml。

文件包设计及文件关系如图11.3所示。

文件包设计及文件关系
图11.3 文件包设计及文件关系

11.3 文件架构

文件架构如图11.4所示。

说明:为让通用逻辑服务除了本项目以外的项目,文件server.js位于所有文件夹外围。

文件架构
图11.4 文件架构

文件功能说明见表11.2所述。

序号 文件(夹)名 作用
1 books.xml 存放在后台用于测试的XML基础数据文件
2 main.html 实现人机交互的用户界面主页
3 Server.js NodeJS后台通用逻辑服务程序,动态引入Server_11.js模块。
4 XML_page.js 包含XML文档数据分页函数。
5 jquery.js 加载JQuery函数库,辅助表示层数据可视化。
6 UI 存放表示层文件的文件夹
7 Server 存放业务逻辑层文件的文件夹
8 Data 存放XML数据文件
9 Server_11.js 本案例后台服务专用逻辑

11.4 代码实现

11.4.1 主页实现代码

对应文件:main.html

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title></title>
</head>
<body>
    <h3>XML数据浏览测试</h3>
    <hr />
    【<span style="background-color:black;color:white">页面大小(每页显示记录</span>
    <select id="p_size" disabled="disabled" onchange='pagesize_change(xmldom, "p_size", "p_count","p_current", "result" )'>
        <option>3</option>
        <option>6</option>
        <option>9</option>
    </select><span style="background-color:black;color:white">条);</span>
    共<input readonly="readonly" id="p_count" type="text" style="width:60px" />页;
    当前是第<input readonly="readonly" id="p_current" type="text" value="1" style="width:60px"/>页】
    <hr />
    <input class="op_but" disabled="disabled" type="button" value="最前页" onclick='first_page(xmldom, "p_size", "p_count","p_current", "result" )' />
    <input class="op_but" disabled="disabled" type="button" value="上一页" onclick='prior_page(xmldom, "p_size", "p_count","p_current", "result" )' />
    <input class="op_but" disabled="disabled" type="button" value="下一页" onclick='next_page(xmldom, "p_size", "p_count","p_current", "result" )' />
    <input class="op_but" disabled="disabled" type="button" value="最后页" onclick='last_page(xmldom, "p_size", "p_count","p_current", "result" )' />
    <hr />
    <div id="result">
    </div>
</body>
</html>
<script type="text/javascript" src="/jquery.js"></script>
<script type="text/javascript" src="XML_page.js"></script>
<script type="text/javascript" >
//初始化,按默认参数显示图书信息
var xmldom;
$(document).ready(function () {
    var xhr = new XMLHttpRequest();
    xhr.open("post", "http://localhost:1017/viewAllbooks_11", true);
    xhr.onreadystatechange = function () {
        if (xhr.status == 200 && xhr.readyState == 4) {
            xmldom = $.parseXML(xhr.responseText);
            var book_count = xmldom.getElementsByTagName("tr").length - 1;
            var page_size = parseInt($("#p_size option:selected").text());
            var page_count = Math.ceil(book_count / page_size);
            $("#p_count").val(page_count);
            $("#p_size").attr("disabled", false);
            $(".op_but").attr("disabled", false);
            display_cur_page(xmldom, "p_size", "p_count", "p_current", "result");
        }
    }
    xhr.send();
});
</script>

11.4.2 自定义JS文件实现代码

文件XML_page.js实现代码:

// 重要说明
//xmldom:XML文档对象
// page_size_id:调整页面大小的下拉列表
//page_count_id:显示页面总数
//page_current_id:当前正在浏览的页面号
// result_div_id:输出结果的div

//显示当前页数据
function display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    var page_size = parseInt($("#" + page_size_id).val());
    var cur_page = parseInt($("#" + page_current_id).val());
    var begin_pos = (cur_page - 1) * page_size + 1;
    var end_pos = cur_page * page_size;
    var max_page = parseInt($("#" + page_count_id).val());
    var trs = xmldom.getElementsByTagName("tr");
    if (cur_page == max_page) { end_pos = trs.length - 1; }
    var table_str = "<table border='1'>";
    table_str += trs[0].outerHTML;
    for (var k = begin_pos; k <= end_pos; k++) {
        table_str += trs[k].outerHTML;
    }
    table_str += "</table>";
    $("#" + result_div_id).html(table_str);
}

//页面大小变化时
function pagesize_change(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    var book_count = xmldom.getElementsByTagName("tr").length - 1;
    var page_size = parseInt($("#" + page_size_id + " option:selected").text());
    var page_count = Math.ceil(book_count / page_size);
    $("#" + page_count_id).val(page_count);
    $("#" + page_current_id).val("1");
    display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id);
}

//下一页
function next_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    var page_count = parseInt($("#" + page_count_id).val());
    var cu_page = parseInt($("#" + page_current_id).val());
    cu_page++;
    if (cu_page > page_count) { alert("已到最后一页"); return; }
    else {
        $("#" + page_current_id).val(cu_page);
        display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id);
    }
}

//上一页
function prior_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    var cu_page = parseInt($("#" + page_current_id).val());
    cu_page--;
    if (cu_page == 0) { alert("已到最前一页"); return; }
    else {
        $("#" + page_current_id).val(cu_page);
        display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id);
    }
}

//最前一页
function first_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    $("#" + page_current_id).val("1");
    display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id);
}

//最后一页
function last_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id) {
    var page_count = parseInt($("#" + page_count_id).val());
    $("#" + page_current_id).val(page_count);
    display_cur_page(xmldom, page_size_id, page_count_id, page_current_id, result_div_id);
}

11.4.3 后台服务实现代码

主服务server.js实现代码:

// JavaScript source code
//---------------请将这个文件保存为Unicode格式,否则麻烦大大的----------------

const http_obj = require("http");//用于产生服务对象
const fs_obj = require("fs");//读写后台文件
const url_tran_obj = require("url");//解释URL

const server = http_obj.createServer(function (request, response) {
    //请求路径处理
    var req_str = decodeURI(request.url);
    var req_head;
    var POS = req_str.indexOf("?");
    if (POS == -1) { req_head = req_str; }
    else { req_head = req_str.substring(0, POS); }
    
    //设置查询对象
    var url_obj = url_tran_obj.parse(req_str, true);
    var Q_obj = url_obj.query;
    
    //响应头设置
    response.setHeader("Content-Type", "text/plain;charset=utf-8");
    response.setHeader("Access-Control-Allow-Origin", "*");//实现跨域访问
    response.writeHead(200);
    
    //%%%%%%%%%%%%%----------------begin your code
    require("./11/server/server_11.js")(request, response, req_head, Q_obj, fs_obj);
    //%%%%%%%%%%%%%------------------end your code
});

server.listen(1007);
console.log("Server is running at port 1007...");

模块服务Server_11.js实现代码:

function server_11(request, response, req_head, Q_obj, fs_obj) {
    if (req_head == "/viewAllbooks_11") {
        //代码1
        fs_obj.readFile("./11/data/books.xml", "utf-8", function (err, data) {
            if (err) { response.end(err); }
            else { response.end(data); }
        });
    }
}

module.exports = server_11

11.4.4 实现效果

实现效果如图11.4所示。

实现效果
图11.4 实现效果

11.5 仿真实训

给定如下XML文档:

<?xml version="1.0" encoding="utf-8"?> <table border="1" id="books"> <tr style="background-color:greenyellow"> <th id="s_id">学号</th> <th id="name">姓名</th> <th id="age">年龄</th> </tr> <tr> <td>5744885</td> <td>Max</td> <td>18</td> </tr> <tr> <td>2323885</td> <td>Jack</td> <td>17</td> </tr> <tr> <td>8888</td> <td>Roseg</td> <td>18</td> </tr> <tr> <td>8767885</td> <td>Json</td> <td>19</td> </tr> <tr> <td>64547565647689</td> <td>张三</td> <td>16</td> </tr> <tr> <td>45634748758478</td> <td>李四</td> <td>19</td> </tr> <tr> <td>675665647689</td> <td>王五</td> <td>18</td> </tr> <tr> <td>2342353758478</td> <td>刘六</td> <td>20</td> </tr> </table>

现提出如下设计需求:

  1. 设计合适的用户接口,可自定义浏览页面大小(每页可展示图书条目的数量)。
  2. 基于选定的页面大小,用户可选择性地浏览"最前页"、"最后页"、"前一页"、"后一页"。
  3. 基于三层架构读取XML文档。
  4. 仿照本章案例完成相关设计及实现相关功能。