NodeJs实战-待办列表(6)-前端绘制表格显示待办事项详情

前面几节显示的列表,看不到事项创建时间,完成时间,数据显示比较少。本节增加表格显示这些结构化数据
在这里插入图片描述

定义服务器返回的 json 数据

  1. 一行数据例子
 		{
            "itemName": "1111",
            "createTime": "2022-11-19 22:06:15",
            "completeTime": "2022-11-19 22:06:29",
            "todoStatus": "已完成"
        }
  1. 多行数据放入列表
[
    {
        "itemName": "1111",
        "createTime": "2022-11-19 22:06:15",
        "completeTime": "2022-11-19 22:06:29",
        "todoStatus": "已完成"
    },
    {
        "itemName": "en1111",
        "createTime": "2022-11-19 22:06:20",
        "completeTime": "2022-11-19 22:06:33",
        "todoStatus": "已完成"
    }
]
  1. 完整的json 示例
{
    "code": 1,
    "data": [
        {
            "itemName": "1111",
            "createTime": "2022-11-19 22:06:15",
            "completeTime": "2022-11-19 22:06:29",
            "todoStatus": "已完成"
        },
        {
            "itemName": "en1111",
            "createTime": "2022-11-19 22:06:20",
            "completeTime": "2022-11-19 22:06:33",
            "todoStatus": "已完成"
        }
    ],
    "msg": "查询成功"
}

前端绘制动态表格

  1. 前端增加表格
<table id="todo_table" cellpadding="0" cellspacing="0" border="1">
        <tr id ="todo_header">
            <th>待办事项</th>
            <th>创建时间</th>
            <th>完成时间</th>
            <th>是否已完成</th>
            <th>操作</th>
        </tr>  
</table>
  1. JQuery 绘制动态表格
      function initUlData(result) {
            if (result.code == '0') {
                alert(result.msg);
            } else {
                if (result.data == null) {
                    alert('服务器返回数据为空');
                    return ;
                }
                var todoHeader = $('#todo_header');
                $('#todo_table').empty();
                $('#todo_table').append(todoHeader);
                result.data.forEach(item => {
                    // $('#todo_ul').append( $('<li></li>').html(a));
                    var tr = $('<tr></tr>');
                    tr.append($('<td></td>').html(item.itemName));
                    tr.append($('<td></td>').html(item.createTime));
                    tr.append($('<td></td>').html(item.completeTime));
                    tr.append($('<td></td>').html(item.todoStatus));
                    var delButton = $('<input type="button" value="完成" />');
                    if (item.todoStatus = '已完成') {
                        delButton[0].disabled = true;
                    } else {
                        delButton.on('click', function(){
                            var tdNodes = this.parentNode.childNodes;
                            if (tdNodes.length > 1) {
                                complete(tdNodes[0].textContent);
                            }
                        });
                    }
                    tr.append(delButton);
                    $('#todo_table').append(tr);
                });
                focusItemText();
            }
        }

2.1 创建表格行

 var tr = $('<tr></tr>');

2.2 创建单元格,填充单元格html数据,再添加至行中,最后一个单元格放入按钮

tr.append($('<td></td>').html(item.itemName));
tr.append($('<td></td>').html(item.createTime));
tr.append($('<td></td>').html(item.completeTime));
tr.append($('<td></td>').html(item.todoStatus));
var delButton = $('<input type="button" value="完成" />');
tr.append(delButton);

2.3 如果该待办事项已完成,按钮设置禁用

if (item.todoStatus = '已完成') {
   delButton[0].disabled = true;
}

2.4 表格添加行数据

$('#todo_table').append(tr);
  1. 完整的 index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>待办列表</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.1.min.js"></script>
    <script type="text/javascript">
        $(function() {
            init();
            $("#add").on('click', function(){
                add();
            });
            $("#complete").on('click', function(){
                complete();
            });
        });

        
        function init() {
            $.ajax({
                url: "/query",
                method: 'GET',
                success: function(result) {
                    initUlData(result);
                }
            });
        }

        function add() {
            var item = $("#item_text").val().trim();
            if (item.length == 0) {
                alert('输入数据不能为空');
            }
            $.ajax({
                url: "/add",
                method: 'GET',
                data: {
                    item: item
                },
                success: function(result) {
                    initUlData(result);
                }
            });
        }

        function complete(item) {
            $.ajax({
                url: "/complete",
                method: 'GET',
                data: {
                    item: item
                },
                success: function(result) {
                    initUlData(result);
                }
            });
        }

        function initUlData(result) {
            if (result.code == '0') {
                alert(result.msg);
            } else {
                if (result.data == null) {
                    alert('服务器返回数据为空');
                    return ;
                }
                var todoHeader = $('#todo_header');
                $('#todo_table').empty();
                $('#todo_table').append(todoHeader);
                result.data.forEach(item => {
                    // $('#todo_ul').append( $('<li></li>').html(a));
                    var tr = $('<tr></tr>');
                    tr.append($('<td></td>').html(item.itemName));
                    tr.append($('<td></td>').html(item.createTime));
                    tr.append($('<td></td>').html(item.completeTime));
                    tr.append($('<td></td>').html(item.todoStatus));
                    var delButton = $('<input type="button" value="完成" />');
                    if (item.todoStatus == '已完成') {
                        delButton[0].disabled = true;
                    } else {
                        delButton[0].disabled = false;
                        delButton.on('click', function(){
                            var tdNodes = this.parentNode.childNodes;
                            if (tdNodes.length > 1) {
                                complete(tdNodes[0].textContent);
                            }
                        });
                    }
                    tr.append(delButton);
                    $('#todo_table').append(tr);
                });
                focusItemText();
            }
        }

        function focusItemText() {
            $('#item_text').val('');
            $('#item_text').focus();
        }
    </script>
</head>
<body>
    <h1>待办列表</h1>
    <form method="post" >
        <p><input id="item_text" type="text" name="item" /><input id="add" type="button" value="添加" /></p>
        <!-- <p><input id="complete" type="button" value="完成" /></p> -->
    </form>
    <!-- <ul id="todo_ul"></ul> -->
    <table id="todo_table" cellpadding="0" cellspacing="0" border="1">
        <tr id ="todo_header">
            <th>待办事项</th>
            <th>创建时间</th>
            <th>完成时间</th>
            <th>是否已完成</th>
            <th>操作</th>
        </tr>
        
    </table>
</body>
</html>

后端返回列表数据

后端返回的数据都在 doQuery 接口中封装的,只需要修改该方法

function doQuery(response) {
	db.query('select item_name,create_time,complete_time,' + 
	'case todo_status when 0 then \'未完成\' when 1 then \'已完成\' end as todo_status from t_todo_list', (err, result) => {
		if (err) {
			console.log(err);
			var data = buildData(CODE_ERROR, [], '查询数据库失败');
			sendMsg(response, JSON.stringify(data));
			return;
		}
		var itemList = [];
		for (var i in result) {
			let item = {
				'itemName':'',
				'createTime':'',
				'completeTime':'',
				'todoStatus':''
			}
			item.itemName = result[i].item_name;
			item.createTime = formatDate(result[i].create_time);
			item.completeTime = formatDate(result[i].complete_time);
			item.todoStatus = result[i].todo_status;
			itemList.push(item);
		}
		var data = buildData(CODE_SUCCESS, itemList, '查询成功');
		sendMsg(response, JSON.stringify(data));
	});
}
  1. 列表需要展示的数据有创建时间、完成时间、事项状态,修改查询SQL
select item_name,create_time,complete_time, case todo_status when 0 then '未完成' when 1 then '已完成' end as todo_status from t_todo_list
  1. 一行数据的模型如下
			let item = {
				'itemName':'',
				'createTime':'',
				'completeTime':'',
				'todoStatus':''
			}
  1. 数据需要格式化处理
function formatDate(date) {
	if (date == null) {
		return "";
	}
	return silly.format(date, 'YYYY-MM-DD HH:mm:ss');
}

调试窗口 中第一行显示的是未格式化之前的 date
在这里插入图片描述

完整的 server.js

const http = require('http');
const fs = require('fs');
const parse = require('url').parse;
const mysql = require('mysql');
const silly = require('silly-datetime');
const db = mysql.createPool({
    host: "127.0.0.1",
    port: 3306,
    user: 'root',
    password: 'shootercheng',
    database: "test"
});

const hostname = '127.0.0.1';
const port = 3000;

const CODE_ERROR = 0;
const CODE_SUCCESS = 1;

function send404(response) {
    response.writeHead(404, {'Content-Type': 'text/plain'});
    response.write('Error 404: resource not found.');
    response.end();
}

function sendMsg(response, msg) {
	response.writeHead(200, {'Content-Type': 'application/json;charset=UTF-8'});
    response.write(msg);
    response.end();
}

function readFile(response, filePath) {
	fs.readFile(filePath, (err, data) => {
		if (err) {
			return send404(response);
		}
		var html = data.toString();
		// html = html.replace('%', Array.from(todoSet).join('</li><li>'));
		response.writeHead(200, {'Content-Type': 'text/html'});
		response.end(html);
	});
}

function findItemData(urlParse) {
	if (urlParse.query.length > 0) {
		var queryArray = urlParse.query.split('=');
		if (queryArray.length >= 2) {
			return decodeURI(queryArray[1]);
		}
	}
	return '';
}

function buildData(code, data, msg) {
	// 返回数据
	let retData = {
		'code':'',
		'data': [],
		'msg':''
	}
	retData.code = code;
	retData.data = data;
	retData.msg = msg;
	return retData;
}

/**
 * 获取当前时间
 * @returns
 */
function currentTime() {
    return silly.format(new Date(), 'YYYY-MM-DD HH:mm:ss');
}

/**
 * 格式日期
 * @param {*} date 
 * @returns 
 */
function formatDate(date) {
	if (date == null) {
		return "";
	}
	return silly.format(date, 'YYYY-MM-DD HH:mm:ss');
}

/**
 * 查询待办数据,并且输出到response
 * @param {*} response 
 */
function doQuery(response) {
	db.query('select item_name,create_time,complete_time,' + 
	'case todo_status when 0 then \'未完成\' when 1 then \'已完成\' end as todo_status from t_todo_list', (err, result) => {
		if (err) {
			console.log(err);
			var data = buildData(CODE_ERROR, [], '查询数据库失败');
			sendMsg(response, JSON.stringify(data));
			return;
		}
		var itemList = [];
		for (var i in result) {
			let item = {
				'itemName':'',
				'createTime':'',
				'completeTime':'',
				'todoStatus':''
			}
			item.itemName = result[i].item_name;
			item.createTime = formatDate(result[i].create_time);
			item.completeTime = formatDate(result[i].complete_time);
			item.todoStatus = result[i].todo_status;
			itemList.push(item);
		}
		var data = buildData(CODE_SUCCESS, itemList, '查询成功');
		sendMsg(response, JSON.stringify(data));
	});
}

/**
 * 添加待办事项
 * @param {*} response 
 * @param {*} itemData 
 */
function doAdd(response, itemData) {
	db.query('select count(*) as num from t_todo_list where item_name = ? and todo_status = ?', [itemData,'0'], (err, result) => {
        if (err) {
            console.log(err);
			var data = buildData(CODE_ERROR, [], '查询数据库失败');
			sendMsg(response, JSON.stringify(data));
			return;
        }
        if (result[0].num > 0) {
			var data = buildData(CODE_ERROR, [], itemData + '-待办事项已存在');
			sendMsg(response, JSON.stringify(data));
			return;
		}
		db.query('insert into t_todo_list(item_name, create_time, todo_status) values(?, ?, ?)', [itemData, currentTime(), '0'], (err, result) => {
			if (err) {
				console.log(err);
				var data = buildData(CODE_ERROR, [], itemData + '-待办事项添加到数据库失败');
				sendMsg(response, JSON.stringify(data));
				return;
			}
			doQuery(response);
		});
	});
}

/**
 * 完成待办事项
 * @param {*} response 
 * @param {*} itemData 
 */
function doCompelete(response, itemData) {
	db.query('select count(*) as num from t_todo_list where item_name = ? and todo_status = ?', [itemData, '0'], (err, result) => {
        if (err) {
            console.log(err);
			var data = buildData(CODE_ERROR, [], '查询数据库失败');
			sendMsg(response, JSON.stringify(data));
			return;
        }
        if (result[0].num == 0) {
			var data = buildData(CODE_ERROR, [], itemData + '-待办事项不存在');
			sendMsg(response, JSON.stringify(data));
			return;
		}
		db.query('update t_todo_list set complete_time = ?, todo_status = ? where item_name = ?', [currentTime(), '1', itemData], (err, result) => {
			if (err) {
				console.log(err);
				var data = buildData(CODE_ERROR, [], itemData + '-更新待办事项失败');
				sendMsg(response, JSON.stringify(data));
				return;
			}
			doQuery(response);
		});
	});
}

const server = http.createServer((request, response) => {
	var urlParse = parse(request.url);
	var urlPath = urlParse.pathname;
	var itemData;
	if (urlPath == '/add' || urlPath == '/complete') {
		itemData = findItemData(urlParse);
		if (itemData.length == 0) {
			var data = buildData(CODE_ERROR, [], '输入数据有误');
			return sendMsg(response, JSON.stringify(data));
		}
	}
	switch (urlPath) {
		case '/':
			var filePath = 'public/index.html';
    		var absPath = './' + filePath;
			readFile(response, absPath);
			break;
		case '/query':
			doQuery(response);
			break;
		case '/add':
			doAdd(response, itemData);
			break;
		case '/complete':
			doCompelete(response, itemData);
			break;
	}
});

server.listen(port, hostname, () => {
	console.log(`Server running at http://${hostname}:${port}/`)
})

验证

执行添加

  1. 1111
  2. en1111
  3. 中文1111
    在这里插入图片描述
    可以看到页面已完成的数据 按钮已禁用,未完成的可以点击完成

查看数据库中的数据是否与页面一致

在这里插入图片描述

使用浏览器debug表格绘制过程

打开浏览器开发者工具,点击source, 点击代码行数加上断点
在这里插入图片描述
刷新页面,按F10 可以看到表格一行行绘制
在这里插入图片描述
在这里插入图片描述

项目地址

Gitee代码仓库: https://gitee.com/3281328128/todo_list/tree/mysql/

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Chengdu.S

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值