文章目录
1. 案例分析
- 该案例主要采用后端渲染数据
- 前端部分:html、css、js、bootstrap
- 后端部分:node.js
- 框架:express
- 模板引擎:art-template、express-art-template
- 数据库:mysql
2. 案例效果
2.1 主页(唯一页面)
2.2 添加模态框
2.3 修改模态框
点击修改按钮时,会包含姓名和台词
2.3 删除模态框
3. 代码片段
3.1 目录结构
3.2 首页、异常页
<!-- 首页 -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<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/bootstrap.min.css">
</head>
<body>
<div class="container">
<div class="row">
<div class="col-1"></div>
<div class="col-10 border box">
<div class="row header">
<span class="text-center">王者荣耀英雄信息</span>
</div>
<div class="row">
<form action="searchInfo" method="post" class="row g-3">
<div class="col-auto username">
<label for="inputUsername">名称</label>
</div>
<div class="col-auto">
<input type="text" class="form-control" id="inputUsername" placeholder="名称" name="name">
</div>
<div class="col-auto">
<button type="submit" class="btn btn-primary mb-3">搜索</button>
</div>
<div class="col-auto">
<!-- 添加按钮 -->
<button type="button" class="btn btn-success mb-3" data-bs-toggle="modal"
data-bs-target="#addModal">添加</button>
</div>
</form>
</div>
<div class="row">
<table class="table" text-align="center">
<thead style="background-color: #dfefd7;">
<tr>
<th scope="col">#</th>
<th scope="col">英雄名称</th>
<th scope="col">英雄台词</th>
<th scope="col" width="80">修改</th>
<th scope="col" width="80">删除</th>
</tr>
</thead>
<tbody style="background-color: #dbeff7;">
{{each res}}
<tr>
<th scope="row" class="userID">{{$index + 1}}</th>
<td>{{$value.name}}</td>
<td>{{$value.words}}</td>
<td>
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#updateModal"
data-name="{{$value.name}}" data-words="{{$value.words}}" data-id="{{$value.id}}">修改</button>
</td>
<td>
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#deleteModal"
data-id="{{$value.id}}">删除</button>
</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
</div>
<div class="col-1"></div>
</div>
</div>
<!-- 添加模块 -->
<div class="modal fade" id="addModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form action="/addInfo" method="post">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">添加英雄</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">姓名:</label>
<input type="text" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp" name="name">
</div>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">台词:</label>
<input type="text" class="form-control" id="exampleInputPassword1" name="words">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</form>
</div>
<!-- 修改模块 -->
<div class="modal fade" id="updateModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form action="/updateInfo" method="get">
<input name="id" class="okUpdate">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">修改英雄</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label for="updateName" class="form-label">姓名:</label>
<input type="text" class="form-control" id="updateName" aria-describedby="emailHelp" name="name">
</div>
<div class="mb-3">
<label for="updateWords" class="form-label">台词:</label>
<input type="text" class="form-control" id="updateWords" name="words">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</form>
</div>
<!-- 删除模块 -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
<form action="/delInfo" method="post">
<input name="id" class="okDel">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">删除英雄</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
确定要删除吗??
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">取消</button>
<button type="submit" class="btn btn-primary">确定</button>
</div>
</div>
</div>
</form>
</div>
<script src="./js/index.js"></script>
<script src="./js/bootstrap.min.js"></script>
</body>
</html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>{{err}}</h1>
<script>
setTimeout(() => {
window.location.href = "http://localhost:9527/"
}, 3000);
</script>
</body>
</html>
3.3 入口文件
// 入口
const express = require("express")
const path = require("path")
const app = express()
// 使用路由
const { router } = require("./router/router")
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
// 后端渲染
app.engine('art', require('express-art-template'));
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'art');
// 读取静态文件中的数据
app.use(express.static("public"))
app.use(router)
app.listen(9527, () => {
console.log("服务也开启: http://localhost:9527");
})
3.4 mysql文件
// 数据库连接
const mysql = require("mysql")
module.exports.database = function (sql, parameter = null) {
// 数据库配置
var connection = mysql.createConnection({
host: 主机,
port: 端口号,
user: 用户名,
password: 密码,
database: 数据库
});
// 连接数据库
connection.connect((err) => {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log('连接次数:' + connection.threadId);
});
return new Promise((reslove, reject) => {
// 访问数据库
connection.query(sql, parameter, function (error, results, fields) {
if (error) {
reject(error)
return
}
reslove(results)
});
// 关闭数据库
connection.end();
})
}
3.5 路由文件
// 路由
const express = require("express")
// 数据处理模块
const { renderPage, addInfo, delInfo, updateInfo, searchInfo } = require("../mysql/renderData")
const router = express.Router()
// 主页面渲染数据
router.get("/", renderPage)
router.get("/home", renderPage)
// 添加数据
router.post("/addInfo", addInfo)
// 删除数据
router.post("/delInfo", delInfo)
// 更新数据
router.get("/updateInfo", updateInfo)
// 搜索数据
router.post("/searchInfo", searchInfo)
exports.router = router
3.6 渲染数据文件
// 渲染数据
const { database } = require("./database")
function renderPage(request, response) {
database('select * from info_list where isDel = 0').then(res => {
response.render("index.art", {
res
})
}).catch(err => {
renderErr(response, "主页数据出错了")
})
}
function addInfo(request, response) {
if (!request.body.name.trim()) {
renderErr(response, "添加数据名字不能为空")
return
}
database('insert into info_list set ?', request.body).then(res => {
response.redirect("/home")
response.send()
}).catch(err => {
renderErr(response, "添加数据出错了")
})
}
function delInfo(request, response) {
database('update info_list set isDel = 1 where id = ?', request.body.id).then(res => {
response.redirect("/home")
response.send()
}).catch(err => {
response.send(err)
})
}
function updateInfo(request, response) {
if (!request.query.name.trim()) {
renderErr(response, "修改数据名字不能为空")
return
}
// set 要设置的
// where 过滤条件
// ? 对应数组 索引
database('update info_list set name = ?, words = ? where id = ?',
[request.query.name, request.query.words, request.query.id]).then(res => {
response.redirect("/home")
response.send()
}).catch(err => {
renderErr(response, "修改数据出错了")
})
}
function searchInfo(request, response) {
// 如果搜索为空,直接跳到首页
if (!request.body.name.trim()) {
response.redirect("/home")
response.send()
return
}
// like 模糊查询,% 表示零个或多个
database('select * from info_list where name like ? and isDel = 0', `%${request.body.name}%`).then(res => {
response.render("index.art", {
res
})
}).catch(err => {
response.send(err)
})
}
function renderErr(response, err) {
response.render("404.art", {
err: err
})
}
module.exports = {
renderPage,
addInfo,
delInfo,
updateInfo,
searchInfo
}
3.7 修改删除数据关联文件
// 修改
document.querySelector("#updateModal").addEventListener("show.bs.modal", function (e) {
document.querySelector("#updateName").value = e.relatedTarget.dataset.name
document.querySelector("#updateWords").value = e.relatedTarget.dataset.words
this.children[0].children[0].value = e.relatedTarget.dataset.id
})
// 删除
document.querySelector("#deleteModal").addEventListener("show.bs.modal", function (e) {
this.children[0].children[0].value = e.relatedTarget.dataset.id
})
3.8 css样式文件
body {
padding-top: 20px;
}
.container .box {
border-radius: 5px;
overflow: hidden;
}
.container .header {
border-bottom: 1px solid #e8e8e8;
background-color: #f7f7f7;
font-size: 20px;
padding: 5px 0;
}
.container .username label {
font-weight: bold;
padding-top: 6px;
}
.table {
margin-bottom: 0px !important;
border-top: 1px solid #dee2e6;
}
.table tr {
line-height: 2.5;
}
.table tbody tr td:nth-child(4) {
background-color: #fbf7e3;
}
.table tbody tr td:nth-child(5) {
background-color: #f3dfdf;
}
button {
opacity: 0.8;
}
.okDel {
display: none;
}
.okUpdate {
display: none;
}
4. 案例总结
后端渲染每次请求数据都会刷新整个页面,所以后端渲染不太好