主頁 > 後端開發 > 京東投票專案開發筆記

京東投票專案開發筆記

2020-09-14 19:23:08 後端開發

京東投票專案開發筆記

  1. 打開專案
    $yarn install / $ npm install: 跑環境(把專案依賴的插件進行安裝)
    $node admin.js: 啟服務(把自己的計算機作為服務器,創建一個指定埠的服務,來管理后臺程式->后臺程式會根據客戶端請求的需求,把對應的資料和業務邏輯實作)

  2. API.TXT: API介面檔案
    真實專案中,后臺開發人員會給前端開發人員提供一個技術檔案(介面檔案),檔案中描述了前端需要調取后臺的某些介面實作的某些功能,并且標注了請求的地址、請求方式、傳遞給服務器的內容、以及服務器回傳的結果等資訊

    這就是前后端分離: 前端開發者不需要考慮后臺是基于什么技術怎么實作的,我們只需要按照API檔案中提供的資訊,去發送請求傳遞內容即可,這樣就可以獲取我們需要的資料(API檔案就是約束前端和后臺的規范檔案)

面試題:有一萬條資料,想讓其系結到頁面中,怎么做好一些?

  1. 檔案碎片: 遍歷資料,把對應的資料和結構都添加到檔案碎片中,在把檔案碎片扎入到頁面中(優勢∶減少了DOM的回流=>基于字串拼接也可以)

  2. 虛擬DOM:類似于REACT框架,基于虛擬DOM以及DIFF演算法,也可以優化資料系結

  3. 其實本質來講怎么做都不是最好的,我們不應該出現1萬條這種大資料量的系結
    ->從服務器獲取1萬條消耗很多時間
    ->頁面渲染1萬條也會消耗很多時間

異步資料加載(分頁加載)

「需要服務器端做支持]

  1. 客戶端向服務器端發送一個GET請求,傳遞給服務器:每頁展示的條數,當前要展示的頁數等資訊,例如傳遞的是?limit=20&page=1(每頁展示20條,當前展示第一頁)
  2. 服務器端接受到請求后,在所有的資料中把第一頁的20條資料回傳給客戶端
  3. 當用戶下拉加載更多或者點擊第二頁等頁碼按鈕等時候,重復第一步,把對應要展示的頁碼傳遞給服務器,服務器回傳對應頁碼中的資料

最終實作效果圖

通過AJAX請求來獲取JSON檔案中的資料,只有在登錄情況下才能投票和參賽,并且每個人只能投一票,如果沒有資料會顯示當前沒有資料,登錄的密碼需要用MD5加密等等,

一、首頁后端

目錄結構

|-- node_modules       第三方包
|-- public             公共的靜態資源
|-- app.js        	   子應用檔案
|-- package.json       包描述檔案
|-- package-lock.json  第三方包版本鎖定檔案(npm 5 以后才有)
|-- router.js          路由介面檔案

路由設計

路徑方法get引數post引數是否需要登錄備注
/indexGET渲染首頁
/registerGET渲染注冊頁
/registerPOSTusername、phone、password、passwordtrue、slogan、sex處理注冊頁
/loginGET渲染登錄頁
/loginPOSTphone、password處理登錄頁
/aboutGET渲染個人主頁
/logoutGET退出登錄
/searchGETusername搜索功能
/voteGETusername投票功能
/matchGET渲染參賽頁

專案構建

app.js 檔案的基本配置

  • 需要引入第三方模塊

    npm i express

    npm i ejs

    npm i cookie-parser

    npm i body-parser

    npm i mongoose

    npm i multer

  • 路由檔案(router.js)

    // 引入第三方服務器模塊
    const express = require("express");
    // 路由物件
    const router = express.Router();
    
    router.get("/", (req, res) => {
      res.send("Hello World");
    });
    
    module.exports = router;
    
  • app.js

    // 引入寫服務器的第三方模塊
    const express = require("express");
    // 引入用來決議的插件
    const bodyParser = require("body-parser");
    // 引入mongoose插件
    const mongoose = require("mongoose");
    // 引入router.js
    const router = require("./router");
    
    // 創建服務器
    var app = express();
    
    // 模板引擎的設定
    app.set("view engine", "html");
    app.set("views", `${__dirname}/views`);
    app.engine("html", require("ejs").renderFile); // 用ejs模板渲染html
    // 加載到沒有掛載路徑的中間件
    app.use(bodyParser.urlencoded({ extended: false }));
    
    // 靜態資源配置
    app.use(express.static(__dirname + "/public"));
    
    // 使用router, router.js檔案中的所有路由都可以使用
    app.use(router);
    
    // 連接資料庫和開啟服務器
    /*
     * localhost - 本地地址
     * jdvotes - mongodb資料庫
     */
    mongoose.connect(
      "mongodb://localhost/jdvotes",
      {
        useNewUrlParser: true,
        useUnifiedTopology: true,
      },
      function (err) {
        if (err) {
          console.log("資料庫連接失敗");
        } else {
          console.log("資料庫連接成功");
          app.listen(8888, function () {
            console.log("服務器開啟成功 -- 8888");
          });
        }
      }
    );
    
  • 需要用到MondoDB,所以需要配置以下表結構檔案

    /* MongoDB資料庫模型檔案 */
    
    // 引入mongoose插件
    const mongogose = require("mongoose");
    // 通過mongoose定義介面 Schema
    var Schema = mongogose.Schema;
    
    // 生成表結構
    // 操作users表(集合) 定義一個Schema  Schema里面的物件和資料庫表里面的欄位需要一一對應
    var mySchema = new Schema({
      // name: { type: String, default: "zhangsan" },
      name: String,
    });
    
    // 把Schema匯出
    module.exports = mySchema;
    
  • 然后再訪問資料庫模型

    // 引入mongoose插件
    const mongoose = require("mongoose");
    // 引入 schema.js 檔案(MongoDB資料庫表結構檔案)
    const mySchema = require("./schema");
    // 定義資料庫模型
    /*
        model里面的第一個引數 要注意:
          1.首字母大寫
            2.要和資料庫表(集合)名稱對應
        這個模型會和模型名稱相同的復數的資料庫表建立連接 
    */
    
    // 把這個資料庫模型匯出
    module.exports = mongoose.model("User", mySchema);
    
  • 最后在寫路由,在對應路由上查詢對應的資料回傳給頁面即可

    /* 路由檔案 */
    
    // 引入第三方服務器模塊
    const express = require("express");
    // 引入model.js檔案(資料庫模型檔案)
    const USER = require("./model");
    // 路由物件
    const router = express.Router();
    
    // 首頁
    router.get("/", (req, res) => {
      USER.find({}, (err, docs) => {
        // docs.forEach
        // 如果查詢成功
        if (err === null) {
          console.log(docs);
        }
      });
      res.send("Hello");
    });
    
    module.exports = router;
    

    瀏覽器訪問 127.0.0.1:8888 ,得到結果 [ { _id: 5f5ad635994e3f8f4923d1cd, name: 'zhangsan' } ]

  • 創建資料

    for(var i=1;i<=150;i++){
    db.users.insert({"id":i,"name":"張"+i,"picture":"https://cdn.jsdelivr.net/gh/extheor/images/Ajax%E5%9B%BE%E7%89%87/man.png","phone":"10377771223","sex":0,"password":"123456","bio":"Live beautifully, dream passionately, love completely","time":1506090072369,"isMatch":1,"matchId":i<10?"00"+i:i<100?"0"+i:i,"slogan":"你是唯一的,你是非常獨特的,你就是你生命中的第一名","voteNum":1})
    };
    

二、首頁前端

首先把首頁寫出來(views/index.html)

完全手擼的

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>首頁</title>
    <link rel="stylesheet" href="css/index.css" />
  </head>
  <body>
    <div class="app">
      <header>
        <a href="/index">首頁</a>
        <a href="/login" class="login">登錄</a>
        <a href="/register">注冊</a>
      </header>
      <div class="header">
        <img class="img" src="img/title.png" alt="" />
        <div class="myToMacth">
          <a href="/match">我要參賽</a>
        </div>
        <div class="search">
          <input type="text" class="searchInput" placeholder="輸入用戶名查找" />
          <input type="button" class="searchButton" value="搜索" />
        </div>
      </div>
      <div class="content">
        <ul class="userul">
          <% datas.forEach(data => { %>
          <li class="<%= data.phone %>">
            <div class="left">
              <img src="<%= data.picture %> " alt="" />
            </div>
            <div class="center">
              <div><%= data.username %> | 編號# <%= data.matchId %></div>
              <div><%= data.slogan %></div>
            </div>
            <div class="right">
              <div class="voteNum"><%= data.voteNum %></div>
              <div><button>投他一票</button></div>
            </div>
          </li>
          <% }) %>
        </ul>
      </div>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="js/index.js"></script>
  </body>
</html>

CSS樣式在這里

* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

body {
  background: yellowgreen;
}

.app {
  width: 450px;
  height: 800px;
  background: red;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

a {
  text-decoration: none;
}

/* 首頁、登錄、注冊 */
header {
  position: fixed;
  right: 10px;
  top: 10px;
  font-weight: 800;
}

header > a {
  color: #000;
  margin-left: 20px;
}

.header {
  width: 100%;
  height: 50%;
  background: #31a5de;
}

/* 投起來 */
.header > .img {
  position: relative;
  left: 50%;
  top: 40%;
  transform: translate(-50%, -50%);
}

/* 我要參賽 */
.myToMacth {
  position: absolute;
  left: 50%;
  top: 38%;
  transform: translate(-50%, -50%);

  padding: 10px 20px;
  background: #eb713b;
  box-shadow: 5px 2px 15px rgb(90, 38, 38);
  border-radius: 20px;
}
.myToMacth > a {
  color: #fff;
}

/* 搜索 */
.search {
  position: absolute;
  left: 52%;
  top: 45%;
  transform: translate(-50%, -50%);

  width: 250px;
  height: 30px;
  /* border: 1px solid #000; */

  /* border-radius: ; */
}

.searchInput,
.searchButton {
  height: 30px;
  border-radius: 30px;
  border: none;
  outline: none;
}

.searchInput {
  border-top-right-radius: 0;
  border-bottom-right-radius: 0;
  padding-left: 15px;
}
.searchButton {
  width: 50px;
  background: #007ec3;
  color: #fff;
  position: relative;
  right: 5px;
  top: 1px;
  border-top-left-radius: 0;
  border-bottom-left-radius: 0;
}

/* 串列 */
.content {
  height: 50%;
  background: #ccc;
}

.content > ul {
  list-style: none;
  margin: 0;
  padding: 0;
  width: 450px;
}

.content > ul > li {
  height: 100px;
  background: #fff;
}

.content > ul > li > .left {
  float: left;
  width: 100px;
  height: 100px;
}
.content > ul > li > .left > img {
  width: 80%;
  border-radius: 50%;
  margin-left: 10px;
  margin-top: 10px;
}

.content > ul > li > .center {
  float: left;
  height: 100px;
  width: 250px;
  margin-top: 20px;
}
.content > ul > li > .center > div:nth-child(1) {
  margin-bottom: 10px;
}

.content > ul > li > .right {
  float: right;
  width: 100px;
  height: 100px;
  margin-top: 20px;
}
.content > ul > li > .right > div:nth-child(1) {
  margin-bottom: 10px;
  text-align: center;
  color: rgb(156, 92, 92);
}
.content > ul > li > .right > div:nth-child(2) {
  margin-left: 10px;
}
.content > ul > li > .right > div:nth-child(2) > button {
  /* width: 60px;
  height: 30px; */
  padding: 5px 10px;
  border: none;
  background: #0081cd;
  color: #fff;
  border-radius: 15px;
}

頁面樣式如下:

  • 接下來需要把從資料庫獲取到的資料渲染到HTML頁面當中去

    • 從路由檔案中獲取到資料

      /* 路由檔案 */
      
      // 引入第三方服務器模塊
      const express = require("express");
      // 引入model.js檔案(資料庫模型檔案)
      const USER = require("./model");
      // 路由物件
      const router = express.Router();
      
      // 首頁
      router.get("/index", (req, res) => {
        USER.find({}, (err, datas) => {
          // docs.forEach
          // 如果查詢成功
          if (err === null) {
            // console.log(datas);
            res.render("index", {
              // es6語法:物件解構賦值,
              // 相當于datas:datas
              datas,
            });
          }
        });
      });
      
      module.exports = router;
      
    • 渲染到HTML頁面

      <!DOCTYPE html>
      <html lang="en">
        <head>
          <meta charset="UTF-8" />
          <meta name="viewport" content="width=device-width, initial-scale=1.0" />
          <title>首頁</title>
          <link rel="stylesheet" type="text/css" href="css/index.css" />
        </head>
        <body>
          <div class="app">
            <header>
              <a href="#">首頁</a>
              <a href="/login">登錄</a>
              <a href="/register">注冊</a>
            </header>
            <div class="header">
              <img class="img" src="img/title.png" alt="" />
              <div class="myToMacth">
                <a href="/match">我要參賽</a>
              </div>
              <div class="search">
                <input type="text" class="searchInput" placeholder="輸入用戶名查找" />
                <input type="button" class="searchButton" value="搜索" />
              </div>
            </div>
            <div class="content">
              <ul>
                <% datas.forEach(data => { %>
                <li class="<%= data.id %>">
                  <div class="left">
                    <img src="<%= data.picture %> " alt="" />
                  </div>
                  <div class="center">
                    <div>張三 | 編號# <%= data.matchId %></div>
                    <div><%= data.slogan %></div>
                  </div>
                  <div class="right">
                    <div class="voteNum"><%= data.voteNum %></div>
                    <div><button>投他一票</button></div>
                  </div>
                </li>
                <% }) %>
              </ul>
            </div>
          </div>
        </body>
      </html>
      

      效果如下:

三、注冊頁前端

首先把注冊頁面寫出來

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>注冊頁</title>
    <link rel="stylesheet" href="css/index.css" />
    <link rel="stylesheet" href="css/register.css" />
  </head>
  <body>
    <div class="app">
      <header>
        <a href="#">首頁</a>
        <a href="/login">登錄</a>
        <a href="/register">注冊</a>
        <a href="/logout">退出</a>
      </header>

      <div class="header">
        <img class="img" src="img/title.png" alt="" />
      </div>
      <form action="#">
        用戶名:<input
          type="text"
          name="username"
          id="username"
          placeholder="請輸入您的真實姓名"
          required
        />
        手機號碼:<input
          type="text"
          name="phone"
          id="phone"
          placeholder="請填寫您常用的手機號"
          required
        />
        密碼:<input
          type="password"
          name="password"
          id="password"
          placeholder="由6~12位數字和字母組成"
          required
        />
        確認密碼:
        <input type="password" name="passwordtrue" id="password" required />
        <div class="introduce">自我描述:</div>
        <textarea
          cols="54"
          rows="6"
          placeholder="限制在10~100字之間"
          minlength="10"
          maxlength="100"
          id="descript"
          name="descript"
        ></textarea>
        上傳頭像:
        <input type="file" id="avatar" name="avatar" />
        <div class="sex">性別:</div>
        <input type="radio" name="sex" id="man" required /><span></span>
        <input type="radio" name="sex" id="woman" required /><span></span>
        <input type="submit" value="提交注冊資訊" class="submit" />
      </form>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="js/register.js"></script>
  </body>
</html>
.app {
  background: #fff;
}
.header {
  height: 40%;
}
/* 投起來 */
.header > .img {
  position: relative;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

form {
  color: #656e73;
  background: #fff;
  padding: 20px;
}

form > input {
  display: block;

  margin-top: 5px;
  margin-bottom: 20px;
  width: 396px;
  height: 30px;
  padding: 10px;
}
form > textarea {
  padding: 10px;
  outline: none;
  resize: none;
}

form > #man,
form > #woman {
  float: left;
  position: absolute;
  bottom: -135px;
  width: 30px;
}
.sex {
  margin-bottom: 10px;
}
form > #man ~ span {
  margin-right: 20px;
  margin-left: 30px;
}

form > #man {
  left: 17px;
}
form > #woman {
  left: 89px;
}

form > .submit {
  margin-top: 20px;
  background: #54abe8;
  color: #fff;
  font-size: 20px;
  height: 60px;
  border: none;
  outline: none;
  cursor: pointer;
  border-radius: 3px;
}

前端需要把注冊資訊發送給后端,所以需要用到AJAX

// 當提交表單時觸發
$("form").on("submit", (event) => {
  // 阻止默認行為
  event.preventDefault();
  //   console.log($("form")[0]);
  /*
    serialize()  FormData  serializeArray()都是序列化表單,實作表單的異步提交
    但是serialize()和serializeArray()都是只能序列化表單中的資料,比如input  select等的資料,
    但是對于檔案上傳就只能用 FormData,
  */
  var data = new FormData($("form")[0]);
  $.ajax("/register", {
    type: "post",
    data,
    dataType: "json",
    // (默認: "application/x-www-form-urlencoded") 發送資訊至服務器時內容編碼型別,默認值適合大多數情況,如果你明確地傳遞了一個content-type給 $.ajax() 那么他必定會發送給服務器(即使沒有資料要發送)
    contentType: false,
    // processData 默認為true,當設定為true的時候,jquery ajax 提交的時候不會序列化 data,而是直接使用data
    processData: false,
    success: function (response) {
      if (response.code == "200") {
        window.location = "/";
      }
    },
  });
});

四、注冊頁后端

// 注冊
router.get("/register", (req, res) => {
  res.render("register");
});
// 必須寫上傳檔案
router.post("/register", upload.single("picture"), (req, res) => {
  // 獲取前端發送過來的注冊資訊
  var { username, phone, password, passwordtrue, slogan, sex } = req.body;
  // console.log(username);
  var picture;
  try {
    picture = req.file.filename;
  } catch (e) {
    picture = undefined;
  }
  // console.log(picture);
  if (password !== passwordtrue) {
    res.send({
      code: "304",
      message: "兩次密碼不一致",
    });
    return;
  }
  USER.findOne({ username })
    .then((result) => {
      // 如果查詢到username存在,則說明該用戶已注冊過
      if (result) {
        // console.log(result); // 查詢結果
        res.send({
          code: 304,
          message: "該用戶已存在",
        });
      }
      // 否則該用戶不存在,則添加到表(集合)中去
      else {
        console.log("用戶注冊~~~");
        // 如果用戶上傳了圖片
        // 創建一個資料集合(物件)
        var myUser = new USER({
          username,
          phone,
          password,
          picture,
          slogan,
          // sex,
        });
        // console.log(myUser);
        // 把這個資料集合插入到表(集合)中
        USER.insertMany(myUser)
          .then((result) => {
            // console.log(result);
            // 插入成功
            res.send({
              code: 200,
              message: "注冊成功",
            });
          })
          .catch((err) => {
            // console.log(err);
            res.send({
              code: 304,
              message: "注冊失敗",
            });
          });
      }
    })
    .catch((err) => {
      res.send({
        code: 500,
        message: "資料庫內部錯誤",
      });
    });
});

頁面效果如下

注冊即可,其中用戶名為必填項,手機號碼為必填項,密碼為必填項,確認密碼為必填項,性別為必填項

點擊“提交注冊資訊”即可注冊成功,我們會發現在資料庫里就多了一條資料,相對而言,首頁也就會多出這條資料,如下:

五、登錄頁前端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
    <link rel="stylesheet" href="css/index.css" />
    <link rel="stylesheet" href="css/register.css" />
  </head>
  <body>
    <div class="app">
      <header>
        <a href="/index">首頁</a>
        <a href="/register">注冊</a>
      </header>

      <div class="header">
        <img class="img" src="img/title.png" alt="" />
      </div>

      <form action="#">
        手機號碼:<input
          type="text"
          name="phone"
          id="phone"
          placeholder="請填寫您常用的手機號"
          required
        />
        密碼:<input
          type="password"
          name="password"
          id="password"
          placeholder="由6~12位數字和字母組成"
          required
        />
        <input type="submit" value="提交登錄資訊" class="submit" />
      </form>
    </div>

    <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
    <script src="js/login.js"></script>
  </body>
</html>

六、登錄頁后端

// 登錄
router.get("/login", (req, res) => {
  res.render("login");
});
router.post("/login", (req, res) => {
  var { username, phone, password } = req.body;

  // 去資料庫查找phone和password
  USER.findOne({ phone, password })
    .then((result) => {
      if (phone === result.phone && password === result.password) {
        // 如果手機號和密碼都正確,則登錄成功
        var id = result._id;
        // console.log(id);

        res.cookie("phone", phone);
        res.cookie("id", id);

        res.send({
          code: 200,
          message: "登錄成功",
          data: {
            phone,
            id,
          },
        });
      } else {
        res.send({
          code: 304,
          message: "手機號或密碼錯誤",
        });
      }
    })
    .catch((err) => {
      res.send({
        code: 500,
        message: "資料庫內部錯誤",
      });
    });
});

登錄用到了cookie,可以通過用后端發送cookie給客戶端,并在客戶端存盤,然后前端可以通過判斷cookie中的phone是否存在,來判斷用戶是否登錄

  • 登錄頁ajax請求部分

    // 獲取后臺傳送的cookie
    if (document.cookie.indexOf("phone") >= 0) {
      // 如果cookie里的phone存在,則把index頁面中的登錄換成用戶名
      // 想要獲取當前手機號,可以使用cookie中的phone
      // console.log(document.cookie); // phone=17692414892; id=j%3A%225f5c9c1f203b5c3b2ca5c27c%22
      // 通過 ; 切割字串
      // console.log(document.cookie.split(";")); // ["phone=17692414892", " id=j%3A%225f5c9c1f203b5c3b2ca5c27c%22"]
      // 取第零個 -- 手機號
      // console.log(document.cookie.split(";")[0]); // phone=17692414892
      // 利用 = 切割字串
      // console.log(document.cookie.split(";")[0].split("=")); // ["phone", "17692414892"]
      // 然后再取第一個就可以了
      var phone = document.cookie.split(";")[0].split("=")[1];
      // console.log(phone);
      // 對登錄a標簽進行文本替換,herf地址也換一下,可以換成個人主頁
      $(".login").text(phone);
      $(".login").attr("href", "/about");
    
    
      // about頁邏輯
      $(".about").text(phone + "~ 歡迎你");
    }
    

順便就把個人主頁寫出來了

七、個人主頁前端

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>關于</title>
    <link rel="stylesheet" href="css/index.css" />
    <link rel="stylesheet" href="css/about.css" />
  </head>
  <body>
    <div class="app">
      <header>
        <a href="/index">首頁</a>
        <a href="/login" class="login">登錄</a>
        <a href="/logout">退出</a>
      </header>
      <div class="about"></div>
      <img
        class="seekfocus"
        src="https://cdn.jsdelivr.net/gh/extheor/images/%E6%B1%82%E5%85%B3%E6%B3%A8.jpg"
        alt=""
      />
    </div>
  </body>

  <script src="https://cdn.bootcdn.net/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
  <script src="js/index.js"></script>
</html>

CSS樣式:

body {
  background: #31a5de;
}
.app {
  background: #edf1f8;
}
.about {
  background: #edf1f8;
  width: 80%;
  height: 20%;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  border-radius: 8px;
  box-shadow: 0 4px 8px 6px rgba(7, 17, 27, 0.06);

  text-align: center;
  line-height: 100px;
  font-size: 18px;
}

八、個人主頁后端

// 個人主頁
router.get("/about", (req, res) => {
  // 在資料庫中查找到所有的資料進行回傳
  USER.find({}, (err, datas) => {
    if (err === null) {
      res.render("about", {
        datas,
      });
    } else {
      res.send({
        code: 500,
        message: "資料庫內部錯誤",
      });
    }
  });
});

點擊 手機號

即可出現以下頁面

九、退出后端(沒有前端頁面)

// 退出
router.get("/logout", (req, res) => {
  // 清除cookie
  res.clearCookie("phone");
  res.clearCookie("id");

  // 重定向
  res.writeHead(302, {
    Location: "/index",
  });
  // 結束回應程序
  // 用于快速結束沒有任何資料的回應
  res.end("");
});

十、搜索后端

// 搜索
router.get("/search", (req, res) => {
  // 根據id搜索資訊
  var { username } = req.query;
  // 把前端傳遞過來的username在資料庫進行查找,然后回傳結果給前端
  if (username !== "") {
    // 如果用戶名不為空,則查詢對應的用戶
    USER.find({ username }, (err, datas) => {
      if (err === null) {
        res.send({
          datas,
        });
      } else {
        res.send({
          code: 500,
          message: "資料庫內部錯誤",
        });
      }
    });
  }
  // 如果用戶名不為空,則查詢所有的用戶
  else {
    USER.find({}, (err, datas) => {
      if (err === null) {
        res.send({
          datas,
        });
      } else {
        res.send({
          code: 500,
          message: "資料庫內部錯誤",
        });
      }
    });
  }
});
  • 然后前端發送資料給后端,讓后端在資料庫里查詢想要的結果,然后后端在把結果回傳給前端,前端只需渲染到頁面上即可

    // 搜索邏輯
    // 按用戶名 -- 搜索
    $(".searchButton").on("click", (event) => {
      if (document.cookie.indexOf("phone") >= 0) {
        // 如果進入到這里說明用戶已登錄
    
        // 獲取到用戶在搜索框輸入的用戶名
        var username = {
          username: $(".searchInput").val(),
        };
        // 發送ajax請求,后端會傳給前端資料,讓前端進行頁面處理
        $.ajax("/search", {
          type: "get",
          data: username,
          success: function (response) {
            // console.log(response.datas);
            var result = response.datas;
            // console.log(result[0].username);
    
            if (result[0]) {
              // 如果查詢到的結果不為空
              // 先把ul串列清空
              $(".userul").empty();
    
              // 回圈遍歷result
              $.each(result, (index, data) => {
                console.log(data);
                var html = `<li class="${data.username}">
                <div class="left">
                  <img src="${data.picture}" alt="" />
                </div>
                <div class="center">
                  <div>${data.username} | 編號# ${data.matchId}</div>
                  <div>${data.slogan}</div>
                </div>
                <div class="right">
                  <div class="voteNum">${data.voteNum}</div>
                  <div><button>投他一票</button></div>
                </div>
              </li>`;
    
                // 然后再把查詢到的所有資料都添加到ul串列中
                $(".userul").append(html);
              });
            } else {
              // 如果查詢到的結果為空
              alert("當前沒有該用戶");
            }
        },
        });
      //   var username = datas
      } else {
      // 如果進入到這里說明用戶未登錄
        alert("請先登錄!!!");
      }
    });
    

    注意:該操作必須登錄

    效果如下:

十一、投票后端

// 投票
router.get("/vote", (req, res) => {
  // 當前端點擊投票按鈕時獲取到前端發送過來的投票數,然后自加
  var { username } = req.query;
  USER.findOne({ username }, (err, datas) => {
    if (err === null) {
      // 修改資料庫中vaotNum的值 -- update
      // console.log(datas); // 從資料庫查到的資料集合(物件)
      // 從資料庫中修改用戶點擊的用戶的投票數
      USER.updateOne(
        { username },
        { voteNum: ++datas.voteNum },
        (err, voteNum) => {
          if (err === null) {
            // console.log("點贊成功");
            res.send({
              code: 200,
              message: "點贊成功",
              datas,
            });
          } else {
            console.log("點贊失敗");
          }
        }
      );
    }
  });
});
  • 首先是前端點擊投票a標簽,然后通過Ajax發送給后端,后端接收到資料后,從資料庫進行查找,每點擊一次投票數(voteNum)加一,然后 后端會把這個結果回傳給前端,前端可以使用這個資料,來重新繪制HTML頁面

  • 前端ajax請求如下

    // 投票
    // 因為li標簽是動態創建的,所以需要通過事件委托來獲取投票按鈕
    $(".userul").delegate(".vote", "click", function () {
      if (document.cookie.indexOf("phone") >= 0) {
        var $this = $(this);
        var username = $this.parents("li").attr("class");
        var data = {
          username,
        };
        $.ajax("/vote", {
          type: "get",
          data,
          success: function (response) {
            // console.log(response.datas);
            if (response.code === 200) {
              // 先獲取到資料庫中該用戶的票數
              var voteNum = response.datas.voteNum;
              console.log(voteNum);
              // 通過 全域$this 找到voteNum元素
              var $voteNum = $this.parents(".right").children(".voteNum");
              // 然后把voteNum重新繪制到HTML頁面即可
              $voteNum.text(voteNum);
            }
          },
        });
      } else {
        alert("請先登錄!!!");
      }
    });
    

    到這里,基本的投票功能也已經能實作了,如下效果

: 200,
message: “點贊成功”,
datas,
});
} else {
console.log(“點贊失敗”);
}
}
);
}
});
});


- 首先是前端點擊投票a標簽,然后通過Ajax發送給后端,后端接收到資料后,從資料庫進行查找,每點擊一次投票數(voteNum)加一,然后 后端會把這個結果回傳給前端,前端可以使用這個資料,來重新繪制HTML頁面

- 前端ajax請求如下

  ```javascript
  // 投票
  // 因為li標簽是動態創建的,所以需要通過事件委托來獲取投票按鈕
  $(".userul").delegate(".vote", "click", function () {
    if (document.cookie.indexOf("phone") >= 0) {
      var $this = $(this);
      var username = $this.parents("li").attr("class");
      var data = {
        username,
      };
      $.ajax("/vote", {
        type: "get",
        data,
        success: function (response) {
          // console.log(response.datas);
          if (response.code === 200) {
            // 先獲取到資料庫中該用戶的票數
            var voteNum = response.datas.voteNum;
            console.log(voteNum);
            // 通過 全域$this 找到voteNum元素
            var $voteNum = $this.parents(".right").children(".voteNum");
            // 然后把voteNum重新繪制到HTML頁面即可
            $voteNum.text(voteNum);
          }
        },
      });
    } else {
      alert("請先登錄!!!");
    }
  });

到這里,基本的投票功能也已經能實作了,如下效果

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/38126.html

標籤:python

上一篇:創建 WinForm 應用程式

下一篇:vuepress-theme-vdoing使用體驗

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more