项目实战-选座购票

【例14.7】实现选座购票的功能。(实例位置:资源包\源码\14\07)

本节将使用 express-generator 框架模拟实现电影院选座购票的效果,如图 14.21 所示。

image 2024 04 17 20 31 35 640
Figure 1. 图14.21 选座购票

下面讲解该案例的具体实现过程。

服务器实现

(1) 在 WebStorm 中创建一个 app.js 文件,在该文件中,首先导入相关模块,然后定义 seats 变量,以使用数组来存储座位,其中,1 表示未售出的座位,0 表示不是座位,2 表示已售出的座位。app.js 文件中的代码如下:

//导入模块
var socketio = require('socket.io');
var express = require('express');
var http = require('http');
var fs = require('fs');
//声明变量
var seats = [
     [1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
     [1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1],
];

(2) 创建 Web 服务器,并使用 app.get() 设置 GET 请求的页面路由跳转规则,然后使用 static 中间件设置资源导入位置,代码如下:

//创建Web服务器
var app = express();
var server = http.createServer(app);
//创建路由
app.get('/', function (request, response, next) {
     fs.readFile('HTMLPage.html', function (error, data) {
          response.send(data.toString());
     });
});
app.use(express.static('./'));
app.get('/seats', function (request, response, next) {
     response.send(seats);
});

(3) 启动服务器,并且创建 WebSocket 服务器,监听到有客户端连接服务器时,接收自定义的 reserve 事件,该事件中将指定座位设置为已售出状态,并将相应数据回传给客户端。代码如下:

//启动服务器
server.listen(52273, function () {
     console.log('Server Running at http://127.0.0.1:52273');
});
//创建WebSocket服务器
var io = socketio(server);
//监听连接事件
io.sockets.on('connection', function (socket) {
    socket.on('reserve', function (data) {
          seats[data.y][data.x] = 2;
          //向所有客户发送座位消息
          io.sockets.emit('reserve', data);
     });
});

客户端实现

(1) 创建 HTMLPage.html 文件,作为客户端页面,在该页面中添加电影名称和日期,并设置页面样式,代码如下:

<!DOCTYPE html>
<html>
<head>
     <title>选座购票</title>
     <style>
          .line {  overflow: hidden;  }
          .seat {  margin: 2px;  float: left;  width: 40px;  height: 35px; }
          .seat img{
               width: 40px;  height: 35px;
          }
     </style>
     <script src="js/jQuery-v3.4.0.js"></script>
     <script src="/socket.io/socket.io.js"></script>
</head>
<body>
     <h1>惊奇队长</h1>
     <p>今天3月14日 16:00 英语3D</p>
</body>
</html>

(2) 使用 io.connect() 方法向服务器端发起连接请求,然后监听服务器端发送的 reserve 事件,获取服务器端传送的座位信息,修改接收到的指定座位对应的 div 类名以及 data-xdata-y 属性值,并更改指定座位的状态显示。代码如下:

<script>
     //向服务器端发送连接请求
     var socket = io.connect();
     //监听reserve事件
     socket.on('reserve', function (data) {
          var $target = $('div[data-x = ' + data.x + '][data-y = ' + data.y + ']');
          $target.removeClass('enable');
          $target.addClass('disable');
     });
</script>

(3) 通过遍历服务器端传回的数据生成要显示的座位,显示在相应的标签中,并根据显示状态确定是否为其添加单击事件;然后在用户单击座位时,记录单击的座位坐标,并判断是否单击了弹出框中的 “确定” 按钮,如果是,向服务器端发送用户选中的座位坐标,同时移除该座位的单击事件。代码如下:

<script>
     //是否选择座位
     $(document).ready(function () {
          var onClickSeat = function () {
               var x = $(this).attr('data-x');
               var y = $(this).attr('data-y');
               if (confirm('确定吗?')) {
                     $(this).off('click');
                     socket.emit('reserve', {
                          x: x,
                          y: y
                     });
               } else {
                     alert('已取消!');
               }
          };
          //执行Ajax
          $.getJSON('/seats', { dummy: new Date().getTime() }, function (data) {
               //生成座位
               $.each(data, function (indexY, line) {
                     //生成HTML
                     var $line = $('<div></div>').addClass('line');
                     $.each(line, function (indexX, seat) {
                          var $output1 = $('<div></div>', {
                               'class': 'seat',
                               'data-x': indexX,
                               'data-y': indexY
                          }).appendTo($line);
                          var $output=$("<img src='image/yes.png' alt=''>")
                          if (seat == 1) {
                               $output1.addClass('enable').on('click', onClickSeat);
                               $output.appendTo($output1)
                          } else if (seat == 2) {
                               $output1.addClass('disable');
                               $output.appendTo($output1)
                               $output.attr("src","image/no.png")
                          }
                     });
                     $line.appendTo('body');
               });
          });
     });
</script>

运行项目

运行 app.js 服务器端文件,打开浏览器,输入地址 http://127.0.0.1:52273 ,页面初始效果如图 14.22 所示。

image 2024 04 17 20 39 30 571
Figure 2. 图14.22 选座页面初始效果

当有用户选择座位后,刷新当前页面,可以看到座位信息的变化,如图 14.23 所示。

image 2024 04 17 20 40 10 199
Figure 3. 图14.23 座位信息的变化