dhtmlxScheduler 与 Node.js 集成
本教程将指导你如何使用 Node.js 在服务端通过 REST API 构建一个 Scheduler。如果你正在使用其他技术栈,可以参考以下集成选项:
- dhtmlxScheduler와 ASP.NET Core
- dhtmlxScheduler와 ASP.NET MVC
- dhtmlxScheduler와 PHP
- dhtmlxScheduler와 PHP:Slim
- dhtmlxScheduler와 PHP:Laravel 연동하기
- dhtmlxScheduler와 SalesForce LWC 통합하기
- dhtmlxScheduler와 Ruby on Rails 연동하기
- dhtmlxScheduler와 dhtmlxConnector 연동하기
我们的 Node.js Scheduler 配置将依赖于 REST API 进行服务器通信。幸运的是,Node.js 提供了多种现成的解决方案,无需从零开始开发。
本教程将使用 Express 框架,并以 MySQL 作为数据存储。
完整源码可在 GitHub 获取。
步骤 1. 初始化项目
创建项目
首先使用 yarn 或 npm 创建一个新应用:
$ mkdir scheduler-howto-nodejs
$ cd ./scheduler-howto-nodejs
$ yarn init // 或 npm init
初始化过程中,你需要回答一些简单的问题:
$ question name (scheduler-howto-nodejs):
$ question version (1.0.0):
$ question description: My scheduler backend
$ question entry point (index.js): server.js
$ question repository url:
$ question author: Me
$ question license (MIT): MIT
$ question private:
$ success Saved package.json
此过程会生成一个 package.json 文件,内容大致如下:
{
"name": "scheduler-backend",
"version": "1.0.0",
"main": "server.js",
"author": "Me",
"license": "MIT",
}
添加依赖并安装模块
如前所述,示例项目使用 Express 和 MySQL。
请确保你已配置好 MySQL 服务器,或者可以考虑使用 Free MySQL Hosting。
通过以下命令安装 express、mysql、body-parser 和 date-format-lite 模块:
$ yarn add express mysql body-parser date-format-lite
或
$ npm install express mysql body-parser date-format-lite
由于入口文件设置为 server.js,请创建该文件并添加如下内容:
const express = require("express"); // 使用 Express
const bodyParser = require("body-parser"); // 解析 POST 请求
const app = express(); // 创建应用实例
const port = 3000; // 监听端口
// 解析 POST 请求所必需
// 以下代码用于解析 application/x-www-form-urlencoded
app.use(bodyParser.urlencoded({extended:true}));
// 启动服务器
app.listen(port, () => {
console.log("Server is running on port " + port + "...");
});
接下来,在 package.json 中添加 "scripts" 部分:
"scripts": {
"start": "node server.js"
}
此时,package.json 文件应如下所示:
{
"name": "scheduler-howto-node",
"version": "1.0.0",
"main": "server.js",
"license": "MIT",
"scripts": {
"start": "node server.js"
},
"dependencies": {
"body-parser": "^1.20.0",
"date-format-lite": "^17.7.0",
"express": "^4.18.1",
"mysql": "^2.18.1",
}
}
现在可以通过以下命令启动服务器:
$ yarn start
或
$ npm start
步骤 2. 在页面中添加 Scheduler
创建一个目录用于存放前端 HTML、CSS 和 JS 文件:
$ mkdir ./public
在 public 文件夹下,创建一个 index.html 文件,内容如下:
<!doctype html>
<html>
<head>
<title>DHTMLX Sсheduler example</title>
<meta charSet="utf-8"/>
{/* scheduler */}
<script src="https://cdn.dhtmlx.com/scheduler/edge/dhtmlxscheduler.js"
charSet="utf-8"></script>
<link href="https://cdn.dhtmlx.com/scheduler/edge/dhtmlxscheduler.css"
rel="stylesheet" type="text/css" charSet="utf-8">
<style>
html, body{
margin:0px;
padding:0px;
height:100%;
overflow:hidden;
}
</style>
</head>
<body>
<div id="scheduler_here" className="dhx_cal_container">
<div className="dhx_cal_navline">
<div className="dhx_cal_prev_button"> </div>
<div className="dhx_cal_next_button"> </div>
<div className="dhx_cal_today_button"></div>
<div className="dhx_cal_date"></div>
<div className="dhx_cal_tab" name="day_tab"></div>
<div className="dhx_cal_tab" name="week_tab"></div>
<div className="dhx_cal_tab" name="month_tab"></div>
</div>
<div className="dhx_cal_header"></div>
<div className="dhx_cal_data"></div>
</div>
<script>
scheduler.config.load_date="%Y-%m-%d %H:%i";
scheduler.init("scheduler_here", new Date(2022, 0, 20), "week");
scheduler.setLoadMode("day");
// load data from backend
scheduler.load("/events");
// connect backend to scheduler
const dp = scheduler.createDataProcessor({
url: "/events",
mode: "REST"
});
</script>
</body>
</html>
这段代码设置了基础的 HTML 布局,引入了来自 CDN 的 dhtmlxScheduler,并通过 init 方法初 始化了 scheduler。请注意,文档 body 和 scheduler 容器都设置为 100% 高度,以确保 scheduler 能够正确填充其容器。
配置路由
为了让新页面可访问,请在 server.js 文件的 "app.listen(...);" 之前添加如下代码:
// 从 "./public" 目录返回静态页面
app.use(express.static(__dirname + "/public"));
重启应用以应用更改。
现在,在浏览器中打开 http://localhost:3000/ 即可访问 index.html 页面。

步骤 3. 数据库准备
在 scheduler UI 就绪后,下一步是将其与数据库连接,并定义读取和写入事件的方法。
创建数据库
首先,创建一个数据库。你可以使用喜欢的 MySQL 客户端或通过控制台操作。
使用 MySQL 客户端,运行如下命令:
CREATE DATABASE IF NOT EXISTS `scheduler`;
USE `scheduler`;
DROP TABLE IF EXISTS `events`;
CREATE TABLE `events` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`end_date` datetime NOT NULL,
`text` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET="utf8;"
或者,将上述 SQL 保存为 dump.sql 文件,并通过 MySQL 控制台导入:
$ mysql -uuser -ppass scheduler < dump.sql
接下来,在 server.js 中以常量形式定义你的 MySQL 连接配置,便于后续使用:
// MySQL 用于数据库访问,util 用于 Promise 化查询
const util = require("util");
const mysql = require('mysql');
// 使用你自己的数据库连接参数
const mysqlConfig = {
"connectionLimit": 10,
"host": "localhost",
"user": "root",
"password": "",
"database": "scheduler"
};
配置完成后,通过如下方式在应用中连接数据库:
// 打开 mysql 连接池
const connectionPool = mysql.createPool(mysqlConfig);
connectionPool.query = util.promisify(connectionPool.query);
这里使用了 连接池 并通过 util.promisify 将查询封装为 Promise。虽然不是强制要求,但这种方式让代码更简洁易维护。
下一步,我们会将数据库访问逻辑封装到一个独立的 Storage 类中,负责连接和 CRUD 操作。
步骤 4. 实现 CRUD
实现数据访问
所有的数据读写逻辑将被组织在一个 Storage 模块中。该类接收 MySQL 连接,负责指定表的 CRUD 操作:获取所有事件、新增、更新和删除事件。
创建名为 storage.js 的文件,并添加如下代码:
require("date-format-lite"); // 添加日期格式化
class Storage {
constructor(connection, table) {
this._db = connection;
this.table = "events";
}
// 获取表中的事件,如有参数则使用动态加载
async getAll(params) {
let query = "SELECT * FROM ??";
let queryParams = [
this.table
];
let result = await this._db.query(query, queryParams);
result.forEach((entry) => {
// 格式化日期和时间
entry.start_date = entry.start_date.format("YYYY-MM-DD hh:mm");
entry.end_date = entry.end_date.format("YYYY-MM-DD hh:mm");
});
return result;
}
// 新增事件
async insert(data) {
let result = await this._db.query(
"INSERT INTO ?? (`start_date`, `end_date`, `text`) VALUES (?,?,?)",
[this.table, data.start_date, data.end_date, data.text]);
return {
action: "inserted",
tid: result.insertId
}
}
// 更新事件
async update(id, data) {
await this._db.query(
"UPDATE ?? SET `start_date` = ?, `end_date` = ?, `text` = ? WHERE id = ?",
[this.table, data.start_date, data.end_date, data.text, id]);
return {
action: "updated"
}
}
// 删除事件
async delete(id) {
await this._db.query(
"DELETE FROM ?? WHERE `id`=? ;",
[this.table, id]);
return {
action: "deleted"
}
}
}
module.exports = Storage;