「实用场景教程」如何用日程控件DHTMLX Scheduler制作酒店预订日历?(三)

2023-12-04

dhtmlxScheduler >https://www.evget.com/product/3946 是一个类似于Google日历的JavaScript日程安排控件,日历事件通过Ajax动态加载,支持通过拖放功能调整事件日期和时间,事件可以按天,周,月三个种视图显示。

DHTMLX Scheduler正式版下载 >https://www.evget.com/product/3946/download

在本教程中,我们将使用两个强大的工具: DHTMLX Scheduler >https://www.evget.com/product/3946 库和Angular框架来创建一个全面的酒店客房预订应用程序。在上文中( 点击这里回顾>> >https://www.evget.com/article/2023/11/21/48142.html )我们为大家介绍了提供保存数据中的数据加载、CRUD操作实现等,本文将继续介绍服务器配置。

Step 5 – 服务器配置

现在让我们继续为应用程序设置Node.js服务器,本教程使用Express框架和MySQL作为数据存储。

添加依赖项和安装模块

您应该设置MySQL服务器,或者可以使用其他服务,例如免费MySQL托管。

添加express、mysql和date-format-lite模块:

$ npm install express mysql date-format-lite

server.js被指定为上面的输入点,现在让我们在项目的根目录下创建server文件夹,并添加server.js文件,代码如下:

const express = require('express'); // use Express
const app = express(); // create application
const port = 3000; // port for listening
const cors = require('cors');
app.use(cors()); // enable CORS for all routes

// MySQL will be used for db access and util to promisify queries
const util = require('util');
const mysql = require('mysql');

// use your own parameters for database
const mysqlConfig = {
'connectionLimit': 10,
'host': 'localhost',
'user': 'root',
'password': '',
'database': 'room_reservation_node'
};

app.use(express.json()); // Enable JSON body parsing
// return static pages from the './public' directory
app.use(express.static(__dirname + '/public'));

// start server
app.listen(port, () = {
console.log('Server is running on port ' + port + '...');
});

const router = require('./router');

// open connection to mysql
const connectionPool = mysql.createPool(mysqlConfig);
connectionPool.query = util.promisify(connectionPool.query);

// add listeners to basic CRUD requests
const DatabaseHandler = require('./databaseHandler');
const databaseHandler = new DatabaseHandler(connectionPool);
router.setRoutes(app, '/data', databaseHandler);

然后打开package.json文件夹,将start语句替换为:

"scripts": {
"ng": "ng",
"start": "concurrently \"node server/server.js\" \"ng serve\"",
…

我们将使用concurrent包来同时启动服务器和客户端应用程序,因此添加concurrent模块:

$ npm install concurrently

准备数据库

让我们将Scheduler连接到数据库,并定义在其中读取和写入项的方法。

  • 创建数据库:

首先我们需要一个数据库来工作,您可以使用自己喜欢的mysql-client或通过控制台创建数据库。

要使用mysql-client创建数据库,打开它并执行下面的代码,创建预订表:

CREATE TABLE `reservations` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`start_date` datetime NOT NULL,
`end_date` datetime NOT NULL,
`text` varchar(255) DEFAULT NULL,
`room` varchar(255) DEFAULT NULL,
`booking_status` varchar(255) DEFAULT NULL,
`is_paid` BOOLEAN DEFAULT NULL CHECK (is_paid IN (0, 1)),
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

让我们添加一些测试数据:

INSERT INTO `reservations` VALUES (2, '2023-08-01', '2023-08-11', 'RSV2023-08-01ABC124', 3, 4, true);
INSERT INTO `reservations` VALUES (3, '2023-08-07', '2023-08-17', 'RSV2023-08-07ABC126', 5, 3, true);
INSERT INTO `reservations` VALUES (4, '2023-08-04', '2023-08-16', 'RSV2023-08-04ABC125', 7, 4, false);
INSERT INTO `reservations` VALUES (13, '2023-07-28', '2023-08-14', 'RSV2023-07-28ABC123', 1, 4, true);
INSERT INTO `reservations` VALUES (14, '2023-08-14', '2023-08-27', 'RSV2023-08-14ABC129', 1, 3, false);
INSERT INTO `reservations` VALUES (15, '2023-08-19', '2023-08-29', 'new booking', 4, 1, false);
INSERT INTO `reservations` VALUES (16, '2023-08-24', '2023-08-31', 'new booking', 11, 1, false);
INSERT INTO `reservations` VALUES (17, '2023-08-17', '2023-08-26', 'RSV2023-08-17ABC135', 6, 2, false);
INSERT INTO `reservations` VALUES (18, '2023-08-18', '2023-08-31', 'RSV2023-08-18ABC139', 9, 2, false);
INSERT INTO `reservations` VALUES (19, '2023-08-02', '2023-08-12', 'RSV2023-08-02ABC127', 10, 4, true);
INSERT INTO `reservations` VALUES (20, '2023-08-12', '2023-08-21', 'RSV2023-08-12ABC130', 10, 3, false);

创建房间表:

CREATE TABLE `rooms` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`cleaning_status` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

添加一些测试数据:

INSERT INTO `rooms` VALUES ('1', '1', '101', '1', '1');
INSERT INTO `rooms` VALUES ('2', '2', '102', '1', '3');
INSERT INTO `rooms` VALUES ('3', '3', '103', '1', '2');
INSERT INTO `rooms` VALUES ('4', '4', '104', '1', '1');
INSERT INTO `rooms` VALUES ('5', '5', '105', '2', '1');
INSERT INTO `rooms` VALUES ('6', '6', '201', '2', '2');
INSERT INTO `rooms` VALUES ('7', '7', '202', '2', '1');
INSERT INTO `rooms` VALUES ('8', '8', '203', '3', '3');
INSERT INTO `rooms` VALUES ('9', '9', '204', '3', '3');
INSERT INTO `rooms` VALUES ('10', '10', '301', '4', '2');
INSERT INTO `rooms` VALUES ('11', '11', '302', '4', '2');
INSERT INTO `rooms` VALUES ('12', '12', '303', '1', '2');

创建roomTypes表:

CREATE TABLE `roomTypes` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

添加一些测试数据:

INSERT INTO `roomTypes` VALUES ('1', '1', '1 bed');
INSERT INTO `roomTypes` VALUES ('2', '2', '2 bed');
INSERT INTO `roomTypes` VALUES ('3', '3', '3 bed');
INSERT INTO `roomTypes` VALUES ('4', '4', '4 bed');

创建cleaningStatuses表:

CREATE TABLE `cleaningStatuses` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
`color` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

添加一些测试数据:

INSERT INTO `cleaningStatuses` VALUES ('1', '1', 'Ready', '#43a047');
INSERT INTO `cleaningStatuses` VALUES ('2', '2', 'Dirty', '#e53935');
INSERT INTO `cleaningStatuses` VALUES ('3', '3', 'Clean up', '#ffb300');

创建bookingStatuses表:

CREATE TABLE `bookingStatuses` (
`id` bigint(20) unsigned AUTO_INCREMENT,
`value` varchar(255) DEFAULT NULL,
`label` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) DEFAULT CHARSET=utf8;

添加一些测试数据:

INSERT INTO `bookingStatuses` VALUES ('1', '1', 'New');
INSERT INTO `bookingStatuses` VALUES ('2', '2', 'Confirmed');
INSERT INTO `bookingStatuses` VALUES ('3', '3', 'Arrived');
INSERT INTO `bookingStatuses` VALUES ('4', '4', 'Checked Out');
  • 实现数据访问:

所有的读/写逻辑都将在一个名为DatabaseHandler的单独模块中定义,它将使用mysql连接并在指定的表中执行简单的CRUD操作:读取所有项,插入新项,更新或删除现有项。为此创建databaseHandler.js文件,并将以下代码添加到其中:

require('date-format-lite'); // add date format

class DatabaseHandler {
constructor(connection, table) {
this._db = connection;
this.table = 'reservations';
}

/// ↓↓↓ reservations handler ↓↓↓
// get reservations, use dynamic loading if parameters sent
async getAllReservations(params) {
let query = 'SELECT * FROM ??';
let queryParams = [
this.table
];

let result = await this._db.query(query, queryParams);

result.forEach((entry) = {
// format date and time
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;
}

// create new reservation
async insert(data) {
let result = await this._db.query(
'INSERT INTO ?? (`start_date`, `end_date`, `text`, `room`, `booking_status`, `is_paid`) VALUES (?,?,?,?,?,?)',
[this.table, data.start_date, data.end_date, data.text, data.room, data.booking_status, data.is_paid]);

return {
action: 'inserted',
tid: result.insertId
}
}

// update reservation
async update(id, data) {
await this._db.query(
'UPDATE ?? SET `start_date` = ?, `end_date` = ?, `text` = ?, `room` = ?, `booking_status` = ?, `is_paid` = ? WHERE id = ?',
[this.table, data.start_date, data.end_date, data.text, data.room, data.booking_status, data.is_paid, id]);

return {
action: 'updated'
}
}

// delete reservation
async delete(id) {
await this._db.query(
'DELETE FROM ?? WHERE `id`=? ;',
[this.table, id]);

return {
action: 'deleted'
}
}
/// ↑↑↑ reservations handler ↑↑↑

/// ↓↓↓ room cleanup status handler ↓↓↓
// get rooms
async getAllRooms(params) {
let query = 'SELECT * FROM ??';
let queryParams = [
'rooms'
];

let result = await this._db.query(query, queryParams);

return result;
}

// update room cleanup status
async updateRoomCleaningStatus(id, data) {
await this._db.query(
'UPDATE ?? SET `value` = ?, `label` = ?, `type` = ?, `cleaning_status` = ? WHERE id = ?',
['rooms', data.key, data.label, data.type, data.cleaning_status, id]);

return {
action: 'updated'
}
}
/// ↑↑↑ room cleanup status handler ↑↑↑

/// ↓↓↓ get room types ↓↓↓
async getRoomTypes(params) {
let query = 'SELECT * FROM ??';
let queryParams = [
'roomTypes'
];

let result = await this._db.query(query, queryParams);

return result;
}
/// ↑↑↑ get room types ↑↑↑

/// ↓↓↓ get cleaning statuses ↓↓↓
async getCleaningStatuses(params) {
let query = 'SELECT * FROM ??';
let queryParams = [
'cleaningStatuses'
];

let result = await this._db.query(query, queryParams);

return result;
}
/// ↑↑↑ get cleaning statuses ↑↑↑

/// ↓↓↓ get booking statuses ↓↓↓
async getBookingStatuses(params) {
let query = 'SELECT * FROM ??';
let queryParams = [
'bookingStatuses'
];

let result = await this._db.query(query, queryParams);

return result;
}
/// ↑↑↑ get booking statuses ↑↑↑
}

module.exports = DatabaseHandler;
路由

然后需要设置路由,以便放置在页面上的调度器可以访问存储。为此创建另一个helper模块,并将其命名为router.js:

function callMethod (method) {
return async (req, res) = {
let result;

try {
result = await method(req, res);
} catch (e) {
result = {
action: 'error',
message: e.message
}
}

res.send(result);
}
};

module.exports = {
setRoutes (app, prefix, databaseHandler) {
/// ↓↓↓ reservations router ↓↓↓
app.get(`${prefix}/reservations`, callMethod((req) = {
return databaseHandler.getAllReservations(req.query);
}));

app.post(`${prefix}/reservations`, callMethod((req) = {
return databaseHandler.insert(req.body);
}));

app.put(`${prefix}/reservations/:id`, callMethod((req) = {
return databaseHandler.update(req.params.id, req.body);
}));

app.delete(`${prefix}/reservations/:id`, callMethod((req) = {
return databaseHandler.delete(req.params.id);
}));
/// ↑↑↑ reservations router ↑↑↑

/// ↓↓↓ rooms router ↓↓↓
app.get(`${prefix}/collections/rooms`, callMethod((req) = {
return databaseHandler.getAllRooms(req.query);
}));

app.put(`${prefix}/collections/rooms/:id`, callMethod((req) = {
return databaseHandler.updateRoomCleaningStatus(req.params.id, req.body);
}));
/// ↑↑↑ rooms router ↑↑↑

/// ↓↓↓ room types router ↓↓↓
app.get(`${prefix}/collections/roomTypes`, callMethod((req) = {
return databaseHandler.getRoomTypes(req.query);
}));
/// ↑↑↑ room types router ↑↑↑

/// ↓↓↓ cleaning statuses router ↓↓↓
app.get(`${prefix}/collections/cleaningStatuses`, callMethod((req) = {
return databaseHandler.getCleaningStatuses(req.query);
}));
/// ↑↑↑ cleaning statuses router ↑↑↑

/// ↓↓↓ booking statuses router ↓↓↓
app.get(`${prefix}/collections/bookingStatuses`, callMethod((req) = {
return databaseHandler.getBookingStatuses(req.query);
}));
/// ↑↑↑ booking statuses router ↑↑↑
}
};

它所做的就是设置应用程序来侦听调度器可以发送的请求url,并调用存储的适当方法。请注意,所有方法都包装在try-catch块中,以便能够捕获任何错误并向客户机返回适当的错误响应。

还要注意,异常消息是直接写入API响应的。这在开发过程中非常方便,但在生产环境中,对客户端隐藏这些消息可能是一个好主意,因为到达那里的原始mysql异常可能包含敏感数据。

现在如果您打开应用程序页面,可以看到一个带有预订的调度程序。可以在调度程序中创建、删除和修改项,即使重新加载页面,您所做的任何更改也将保留。

如何用日程控件DHTMLX Scheduler制作酒店预订日历

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

「实用场景教程」如何用日程控件DHTMLX Scheduler制作酒店预订日历?(三) 的相关文章

  • 关于软件测试笔试题目

    在学习和工作中 我们经常接触到试题 试题是考核某种技能水平的标准 你知道什么样的试题才是好试题吗 下面小编给大家分享关于软件测试笔试题目内容 希望能够帮助大家 关于软件测试笔试题目 一 判断题 1 软件测试的目的是尽可能多的找出软件的缺陷

随机推荐

  • 在直播间抢到好多实惠东东,全靠抖音支付

    前不久我特别喜欢的一位主播的直播间做活动 很多我放在购物车里好久的心仪好物都有秒杀惊喜价 更让我开心的是 在拼手速抢这些秒杀好物的时候 我都成功了 这主要是因为我用了抖音支付 付款环节特别丝滑顺畅 让我在抖音的购物体验直接原地提升了几个le
  • 坚守合规底线 波场TRON将联合多方不断提升合规水平

    数字化时代 区块链和加密技术因其去中心化 全球化及透明度等优势在金融科技领域备受关注 但与此同时 一些风险事件的发生也暴露了行业合规化缺乏等问题的存在 近期 有媒体报道称 波场TRON或成为少数非法组织尤其是美国认定的恐怖组织融资的工具 对
  • 三相共直流母线式光伏储能VSG仿真模型研究(Simulink仿真实现)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Simulink仿真实现
  • 数据通信——OSPF路由控制实验

    实验需求 我们采用OSPF完成路由的控制 首先连接如下拓扑 所有设备均属于area 0 网段及环回口配置如上图所示 实验目的 R4和R1的环回口通信路径为R4 R2 R1若R2出现问题 自动切换到R3路径 实验配置 1 配置好各个接口的IP
  • 孙宇晨入选The Giving Block年度加密慈善人物

    日前 全球主流加密捐赠平台The Giving Block公布了2023年度 加密慈善人物 榜单 以表彰过去一年在加密慈善领域做出卓越贡献的个人 其中 波场TRON创始人 火币HTX全球顾问委员会委员孙宇晨因其瞩目的慈善成就入选该榜单 在榜
  • C++ -- 每日选择题 -- Day2

    第一题 1 下面代码中sizeof A 结果为 pragma pack 2 class A int i union U char str 13 int i u void func typedef char cp enum red green
  • 链表高频面试题

    1 两个链表第一个公共子节点 LeetCode160 给你两个单链表的头节点 headA 和 headB 请你找出并返回两个单链表相交的起始节点 如果两个链表不存在相交节点 返回 null 图示两个链表在节点 c1 开始相交 listA 4
  • 「Qt Widget中文示例指南」如何创建一个计算器?(二)

    Qt gt https www evget com product 3579 是目前最先进 最完整的跨平台C 开发工具 它不仅完全实现了一次编写 所有平台无差别运行 更提供了几乎所有开发过程中需要用到的工具 如今 Qt已被运用于超过70个行
  • 江铃汽车热设计工程师岗面试给我留下了较好的体验,感觉很有诚意

    includeusing namespace std int main int n m k cin gt gt n gt gt m gt gt k 秋招快结束啦 不管有没有收获满意的 offer 都要准备好启程前往新的目的地了 租房是应届生
  • Microsoft 365 for Mac:提升您的办公效率的终极选择

    在现代工作环境中 高效的办公软件是提高工作效率和团队合作的关键 Microsoft 365 for Mac 前身为Office 365 是微软为Mac操作系统推出的一套全面的办公软件解决方案 为用户提供了强大的工具和功能 帮助您在任何地方都
  • 项目实战之RabbitMQ死信队列应用

    作者名称 DaenCode gt https blog csdn net 2302 79094329 作者简介 啥技术都喜欢捣鼓捣鼓 喜欢分享技术 经验 生活 人生感悟 尝尽人生百味 方知世间冷暖 文章目录 架构图 application
  • CMake 教程:常用命令及其使用方法

    CMake是一个跨平台 开源的构建工具 它可以自动生成Makefile或者Visual Studio等IDE的工程文件 它能够帮助开发者更方便地管理项目的构建过程 提高项目构建的效率 在本文中 我们将介绍CMake常用的命令以及对应的用法
  • 题解 | #找出每个学校GPA最低的同学#

    原文连接 分享一下我的秋招经验 希望可以帮到你我的秋招目基本结束了 这段秋招很充实也收获很多 当然踩坑也很多 因为之后一段时间我可能要专心准备论文和考公了 一些同学找到我说求职很难突破自己的性格 心理弱点 跟陌生人说话紧张 无法突破自我等等
  • 网络安全技术有哪些

    前言 网络安全技术是保障网络安全的重要手段 以下是一些常见的网络安全技术 防火墙技术 防火墙是一种网络安全设备 可以对网络流量进行过滤和控制 防止未经授权的访问和攻击 入侵检测系统 IDS 入侵检测系统可以监测网络流量 发现和报告网络攻击和
  • offer决赛圈,吉利成都 vs 理想上海

  • 【计算机毕业设计】校园生活服务平台

    校园生活服务平台 如今社会上各行各业 都喜欢用自己行业的专属软件工作 互联网发展到这个时候 人们已经发现离不开了互联网 新技术的产生 往往能解决一些老技术的弊端问题 因为传统校园生活服务平台信息管理难度大 容错率低 管理人员处理数据费工费时
  • C语言--每日选择题--Day31

    第一题 1 下面程序 i 的值为 int main int i 10 int j 0 if j 0 i else i return 0 A 11 B 9 答案及解析 B if语句中的条件判断为赋值语句的时候 因为赋值语句的返回值是右操作数
  • 题解 | #筛选某店铺最有价值用户中消费最多前5名#

    背景 双非本211硕 编程语言 c cuda python方向是算法部署 AI框架 算子开发目标行业 互联网 半导体已oc 深势科技 高性能计算浙 思路 首先join两张表获取所有员工对应的薪水信息 题目为获取每个部门中当前员工薪水最高 拆
  • 项目实战之RabbitMQ冗余双写架构

    作者名称 DaenCode gt https blog csdn net 2302 79094329 作者简介 啥技术都喜欢捣鼓捣鼓 喜欢分享技术 经验 生活 人生感悟 尝尽人生百味 方知世间冷暖 所属专栏 项目所感所想 gt https
  • 「实用场景教程」如何用日程控件DHTMLX Scheduler制作酒店预订日历?(三)

    dhtmlxScheduler gt https www evget com product 3946 是一个类似于Google日历的JavaScript日程安排控件 日历事件通过Ajax动态加载 支持通过拖放功能调整事件日期和时间 事件可