NodeJs实战-待办列表6-前端绘制表格显示待办事项详情
前面几节显示的列表,看不到事项创建时间,完成时间,数据显示比较少。本节增加表格显示这些结构化数据
定义服务器返回的 json 数据
- 一行数据例子
{
"itemName": "1111",
"createTime": "2022-11-19 22:06:15",
"completeTime": "2022-11-19 22:06:29",
"todoStatus": "已完成"
}
- 多行数据放入列表
[
{
"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": "已完成"
}
]
- 完整的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": "查询成功"
}
前端绘制动态表格
- 前端增加表格
<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>
- 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);
- 完整的 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));
});
}
- 列表需要展示的数据有创建时间、完成时间、事项状态,修改查询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
- 一行数据的模型如下
let item = {
'itemName':'',
'createTime':'',
'completeTime':'',
'todoStatus':''
}
- 数据需要格式化处理
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}/`)
})
验证
执行添加
- 1111
- en1111
- 中文1111
可以看到页面已完成的数据 按钮已禁用,未完成的可以点击完成
查看数据库中的数据是否与页面一致
使用浏览器debug表格绘制过程
打开浏览器开发者工具,点击source, 点击代码行数加上断点
刷新页面,按F10 可以看到表格一行行绘制
项目地址
Gitee代码仓库: https://gitee.com/3281328128/todo_list/tree/mysql/