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

news2025/1/11 8:01:22

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

  • 定义服务器返回的 json 数据
  • 前端绘制动态表格
  • 后端返回列表数据
  • 验证
    • 执行添加
    • 查看数据库中的数据是否与页面一致
    • 使用浏览器debug表格绘制过程
  • 项目地址

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

定义服务器返回的 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/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/21470.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springmvc-day03

springmvc-day03 第一章 拦截器 1.概念 1.1 使用场景 1.1.1 生活中坐地铁的场景 为了提高乘车效率&#xff0c;在乘客进入站台前统一检票&#xff1a; 1.1.2 程序中的校验登录场景 在程序中&#xff0c;使用拦截器在请求到达具体 handler 方法前&#xff0c;统一执行检…

基于stm32单片机的智能恒温自动加氧换水鱼缸

资料编号&#xff1a;105 下面是相关功能视频演示&#xff1a; 105-基于stm32单片机的智能恒温自动加氧换水鱼缸Proteus仿真&#xff08;源码仿真全套资料&#xff09;功能讲解&#xff1a;采用stm32单片机&#xff0c;ds18b20测量温度&#xff0c;LCD1602显示温度&#xff0c…

C语言第十一课(上):编写扫雷游戏(综合练习2)

目录 前言&#xff1a; 一、文件建立&#xff1a; 1.头文件game.h&#xff1a; 2.函数定义文件game.c&#xff1a; 3.工程测试文件test.c&#xff1a; 二、编写井字棋游戏&#xff1a; 1.程序整体执行思路&#xff1a; 2.menu菜单&#xff1a; 3.game游戏函数逻辑&#xff…

【Detectron2】代码库学习-5.标注格式- 矩形框, 旋转框,关键点, mask, 实例标注,IOU计算, 旋转框IOU计算,

文章目录Detectron2 内置的标注格式BoxMode 表示方式实用APIRotatedBoxesInstances 实例标注KeypointsMasks结语Detectron2 内置的标注格式 BoxesRotatedBoxesBitMasksPolygonMasksROIMasksKeypointsInstancesImageList BoxMode 表示方式 XYXY_ABSXYWH_ABSXYXY_REL # 相对模…

Windows安装mysql并且配置odbc

文章目录 mysql下载ODBC驱动下载安装mysql使用测试安装ODBC驱动添加ODBC数据源配置完成了用户不能远程访问的问题mysql下载 https://dev.mysql.com/downloads/installer/ ODBC驱动下载 https://dev.mysql.com/downloads/connector/odbc/ 安装mysql 点击mysql安装包,选择…

【25-业务开发-基础业务-品牌管理-图片管理-图片上传方式的三种实现方式-第三方公共服务模块集成到项目中-服务端生成签名实战】

一.知识回顾 【0.三高商城系统的专题专栏都帮你整理好了&#xff0c;请点击这里&#xff01;】 【1-系统架构演进过程】 【2-微服务系统架构需求】 【3-高性能、高并发、高可用的三高商城系统项目介绍】 【4-Linux云服务器上安装Docker】 【5-Docker安装部署MySQL和Redis服务】…

【第三部分 | 移动端开发】1:移动端基础概要

目录 | 概述 | 手机端调试 | 视口 ViewPort 三种视口 meta标签 设置视口 代码适配PE端的要点 | 二倍图 物理像素和物理像素比 利用二倍图解决图片在PE端默认放大失真 背景缩放 background-size | 移动端的开发选择 | 移动端的相关开发注意点 | 概述 | 手机端调试 打…

【操作系统习题】假定某多道程序设计系统供用户使用的主存空间为100 KB ,磁带机2台,打印机1台

4&#xff0e;假定某多道程序设计系统供用户使用的主存空间为100 KB &#xff0c;磁带机2台&#xff0c;打印机1台。采用可变分区方式管理主存&#xff0c;采用静态分配方式分配磁带机和打印机&#xff0c;忽略用户作业I/O时间。现有如下作业序列&#xff0c;见表2-8。 采用先来…

Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理

文章目录一 基础定义二 创建逻辑卷2-1 准备物理设备2-2 创建物理卷2-3 创建卷组2-4 创建逻辑卷2-5 创建文件系统并挂载文件三 扩展卷组和缩减卷组3-1 准备物理设备3-2 创建物理卷3-3 扩展卷组3-4 查看卷组的详细信息以验证3-5 缩减卷组四 扩展逻辑卷4-1 检查卷组是否有可用的空…

Python实现全自动输入文本

文章目录1. 效果图2. 示例代码3. 代码解释1. 效果图 该Python脚本可以实现自动用Notepad打开文本文件&#xff0c;然后自动输入文本&#xff0c;最后保存并关闭文件&#xff0c;从而实现全面自动化处理文本。 2. 示例代码 Python脚本源码如下&#xff0c;主要使用了win32gui、…

Modern Radar for Automotive Applications(用于汽车应用的现代雷达)

目录 1 引言 2 汽车雷达系统的工作原理 2.1 基本雷达功能 2.2 汽车雷达架构 2.2.1 发射机 2.2.2 接收机 2.2.3 天线和天线阵 2.3 信号模型 2.3.1 振幅模型 2.3.2 噪声模型 2.4 雷达波形和信号处理 2.4.1 距离处理 2.4.2 多普勒处理 2.4.3 FMCW汽车雷达应用的典型波形参数…

[Unity好插件之PlayMaker]PlayMaker如何扩展额外创建更多的脚本

学习目标&#xff1a; 如果你正在学习使用PlayMaker的话&#xff0c;那么本篇文章将非常的适用。关于如何连线则是你自己的想法。本篇侧重于扩展适用更多的PlayMaker行为Action&#xff0c;那么什么是PlayMaker行为Action呢&#xff1f; 就是这个列表。当我们要给PlayMaker行为…

CSS的元素显示模式和CSS的背景

&#x1f353;个人主页&#xff1a;bit.. &#x1f352;系列专栏&#xff1a;Linux(Ubuntu)入门必看 C语言刷题 数据结构与算法 HTML和CSS3 目录 一.CSS的元素显示模式 1.1什么是元素的显示模式 1.2块元素 1.3行内元素 1.4 行内块元素 1.5元素显示模式总结 1.6…

JavaEE——HttpServletRequest

HttpServletRequest 核心方法 方法功能String getProtocol()返回请求协议的名称和版本。String getMethod()返回请求的 HTTP 方法的名称String getRequestURI()从协议名称直到 HTTP 请求的第一行的查询字符串中&#xff0c;返回该请求的 URL 的一部分。String getContextPath…

DolphinScheduler3.1简介、部署、功能介绍以及架构设计

DolphinScheduler3.1简介、部署、功能介绍以及架构设计1.DolphinScheduler简介 1-1.关于DolphinScheduler Apache DolphinScheduler 是一个分布式易扩展的可视化DAG工作流任务调度开源系统。适用于企业级场景&#xff0c;提供了一个可视化操作任务、工作流和全生命周期数据处…

Day08--自定义组件的数据监听器案例

1.案例效果&#xff1a; 我的操作&#xff1a; 1》新建一个组件test4 2》在app.json里面将test4设置为全局组件 3》在home.wxml里面是用这个test4组件。 4》在test4.js中编写代码&#xff1a;【需要的配置项都弄一下呗。】 *********************************************…

anaconda3报错Can‘t find libdevice directory解决方案

anaconda3报错Cant find libdevice directory解决方案1. 问题描述2. 解决方案3. 原理分析4. 其他解决方案1. 问题描述 使用anaconda3运行tensorflow进行单机多GPU运算时报错&#xff1a; error: Cant find libdevice directory ${CUDA_DIR}/nvvm/libdevice较的全报错如下&…

【Wayland】QtWayland框架分析

QtWayland框架分析 QtWayland概念介绍 QtWayland是Qt官方基于Wayland开发的一款Toolbox&#xff0c;根据其官网介绍 The QtWayland module consists of two parts: Wayland platform plugin: Enables Qt applications to be run as Wayland clients. QtWaylandCompositor API…

地理空间技术改变世界的未来

摘要: 地理空间技术是一项重大的科学发现&#xff0c;它将人类的可能性推向了一个全新的水平。那么什么是地理空间技术呢&#xff1f;事实上&#xff0c;它与普通的空间数据不同&#xff0c;地理空间技术的创新使我们能够确定物体或人在地球上的确切位置。人们将地理空间技术应…

网络安全系列-四十: suricata 的运行模型Running mode讲解

1. 什么是Running mode 1.1. 基本概念 Sruciata由线程、线程模块、队列组成。 数据包在线程间传递通过队列实现,线程由多个线程模块组成,每个线程模块实现一种功能一个数据包可以由多个线程处理,数据包将通过队列传递到下一个线程。包每次由一个线程处理,但是引擎可以同时…