需求分析:英雄列表
- 后端项目运行地址:http://127.0.0.1:5000
- 前端项目运行地址:http://127.0.0.1:3000
- 前后端分离开发模式的注意点:
-
跨域问题
-
如果不考虑 表单的 Post 提交,则 可以使用 JSONP 的形式来请求接口
-
但是,我们的项目中,涉及到了 英雄表单的 提交,表单提交一般都是Post
-
经过分析,由于JSONP,不支持Post,所以,我们的后端接口,无法设计成JSONP的接口;
-
前端项目 Jquery + 模板引擎 + Semantic UI
Semantic 官网:https://semantic-ui.com/introduction/getting-started.html
前端web项目
-
初始化项目:
npm init -y
-
安装Semantic UI
npm install -g gulp
npm install semantic-ui --save
cd semantic/
gulp build
package.json
{
"name": "web",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"art-template": "^4.12.2",
"express": "^4.16.3",
"jquery": "^3.3.1",
"semantic-ui": "^2.3.2"
}
}
views
index.js
$(function () {
// 获取英雄列表的方法
function getHeroList () {
$.ajax({
url: 'http://127.0.0.1:5001/getallhero',
type: 'get',
success: function (result) {
console.log(result)
var str = template('row', result)
$('#tbd').html(str)
}
})
}
getHeroList()
$('#add').on('click', function () {
$('.ui.modal').modal('show')
})
// 初始化下拉框的样式
$('.ui.dropdown').dropdown()
$('#btnAdd').on('click', function () {
$.ajax({
url: 'http://127.0.0.1:5001/addhero',
data: $('form').serialize(),
type: 'post',
dataType: 'json',
success: function (result) {
if (result.status === 200) {
getHeroList()
}
}
})
})
})
index.css
body{
padding: 20px;
}
h1 {
padding: 20px 0;
text-align: center;
}
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="/semantic/dist/semantic.min.css">
<script src="/node_modules/jquery/dist/jquery.min.js"></script>
<!-- 注意:semantic.min.js 依赖于 Jquery -->
<script src="/semantic/dist/semantic.min.js"></script>
<script src="/node_modules/art-template/lib/template-web.js"></script>
<link rel="stylesheet" href="./index.css">
<script src="./index.js"></script>
</head>
<body>
<h1>英雄列表</h1>
<button class="ui primary button" id="add">添加英雄</button>
<!-- 表格区域 -->
<table class="ui celled striped blue table">
<thead>
<tr>
<th>编号</th>
<th>英雄名称</th>
<th>性别</th>
<th>创建时间</th>
<th>操作</th>
</tr>
</thead>
<tbody id="tbd">
</tbody>
</table>
<!-- 对话框 -->
<div class="ui tiny modal">
<div class="header">
添加新英雄
</div>
<div class="content">
<form class="ui form">
<div class="field">
<label>名称:</label>
<input type="text" name="name" placeholder="请输入英雄名称">
</div>
<div class="field">
<label>性别:</label>
<select name="gender" class="ui fluid dropdown">
<option value="男">男</option>
<option value="女">女</option>
</select>
</div>
</form>
</div>
<div class="actions">
<div class="ui black deny button">
取消
</div>
<div class="ui positive right button" id="btnAdd">
添加
</div>
</div>
</div>
<script type="text/template" id="row">
{{each data}}
<tr>
<td>
<div class="ui ribbon label {{$value.isdel === '000' ? 'blue' : 'red'}}">{{$value.isdel === '000' ? '正常' : '删除'}}</div>
{{$value.id}}
</td>
<td>{{$value.name}}</td>
<td>{{$value.gender}}</td>
<td>{{$value.ctime}}</td>
<td>
<a href="javascript:;">编辑</a>
<a href="javascript:;">删除</a>
</td>
</tr>
{{/each}}
</script>
</body>
</html>
web.js
创建express服务器,实现静态资源托管
// 导入 express 模块
const express = require('express')
// 创建 express 的服务器实例
const app = express()
// 静态资源托管
app.use(express.static('./views'))
app.use('/semantic', express.static('./semantic'))
app.use('/node_modules', express.static('./node_modules'))
// 调用 app.listen 方法,指定端口号并启动web服务器
app.listen(3001, function () {
console.log('Express server running at http://127.0.0.1:3001')
})
数据库设计 - heros
字段名 | 字段类型 | 字段描述 |
---|---|---|
id | int | 主键Id(自增) |
name | varchar | 英雄名称 |
gender | varchar | 性别 |
ctime | varchar | 创建时间 |
isdel | tinyint(布尔值) | 是否被删除 0 表示未删除;1 表示已经被删除 |
在 mysql 中的 tinyint 等同于 bool 值
api_server项目
- 初始化项目:
npm init -y
- 安装express服务器:
npm install express -S
- 安装mysql数据库:
npm install mysql -S
- 开启CORS跨域:
npm install cors -S
- 安装body-parser解析表单数据:
npm install body-parser -S
db.js 数据操作模块
// 导入 mysql 模块
const mysql = require('mysql')
// 创建数据库连接,获取数据库连接对象
const conn = mysql.createConnection({
host: '127.0.0.1',
user: 'root',
password: 'root',
database: 'mysql_001'
})
module.exports = conn
router.js 路由模块
路由模块 本质:就是 URL 地址到 处理函数之间的对应关系
const express = require('express')
const router = express.Router()
// 导入自己的 业务逻辑处理模块
const ctrl = require('./controller.js')
// 只要有人请求 后台的 / 根路径地址,就提示他,请求API服务器成功了!
router.get('/', ctrl.testAPI)
// 对外暴露 getAllHero 接口
router.get('/getallhero', ctrl.getAllHero)
// 对外暴露添加英雄的API接口
router.post('/addhero', ctrl.addHero)
// 对外暴露 获取英雄信息的 API 接口
router.get('/gethero/:id', ctrl.getHeroById)
// 对外暴露 根据Id更新英雄数据的API接口
router.post('/updatehero/:id', ctrl.updateHeroById)
// 对外暴露 根据Id软删除英雄数据的API接口
router.get('/deletehero/:id', ctrl.deleteHeroById)
module.exports = router
server.js 入口
const express = require('express')
const app = express()
// 注册 body-parser 中间件
const bodyParser = require('body-parser')
app.use(bodyParser.urlencoded({ extended: false }))
// 在 API 服务器端,启用 CORS 跨域资源共享
const cors = require('cors')
app.use(cors())
// 导入自己的路由模块
const router = require('./router.js')
// 注册路由模块
app.use(router)
// 让 后端项目,运行在 5001 端口
app.listen(5001, () => {
console.log('api server running at http://127.0.0.1:5001')
})
controller.js 业务处理模块
const conn = require('./db.js')
module.exports = {
/**
* 测试 API 服务器能否正常被请求
*/
testAPI: (req, res) => {
res.send('请求后台API接口成功!')
},
/**
* 获取所有英雄列表
*/
getAllHero: (req, res) => {
const sql = 'select * from heros'
conn.query(sql, (err, result) => {
// 如果读取数据失败,则返回一个失败的结果
if (err) return res.send({ status: 500, msg: err.message, data: null })
// 如果获取数据成功,则直接返回成功的数据结果
res.send({ status: 200, msg: 'ok', data: result })
})
},
/**
* 添加英雄
* 1. 获取到客户端提交到 服务器的 表单数据: 英雄名称、性别 即可
* 2. 获取服务器的当前时间,当作 英雄的添加时间
*/
addHero: (req, res) => {
const hero = req.body
// 得到当前的时间对象
const dt = new Date()
// 字符串,有一个新方法,叫做 padStart(长度, 要填充的字符串)
const y = dt.getFullYear()
const m = (dt.getMonth() + 1).toString().padStart(2, '0')
const d = dt
.getDate()
.toString()
.padStart(2, '0')
const hh = dt
.getHours()
.toString()
.padStart(2, '0')
const mm = dt
.getMinutes()
.toString()
.padStart(2, '0')
const ss = dt
.getSeconds()
.toString()
.padStart(2, '0')
// 补全英雄的添加时间
hero.ctime = y + '-' + m + '-' + d + ' ' + hh + ':' + mm + ':' + ss
/**
* 调用 conn.query 实现 添加英雄
*/
const sql = 'insert into heros set ?'
conn.query(sql, hero, (err, result) => {
if (err) return res.send({ status: 500, msg: err.message, data: null })
res.send({ status: 200, msg: 'ok', data: null })
})
},
/**
* 根据Id获取英雄信息
* 1. 获取到英雄的Id
* 2. 根据Id查询数据,并返回给客户端查询的结果
*/
getHeroById: (req, res) => {
const id = req.params.id
const sql = 'select * from heros where id=?'
conn.query(sql, id, (err, result) => {
if (err) return res.send({ status: 500, msg: err.message, data: null })
res.send({ status: 200, msg: 'ok', data: result })
})
},
/**
* 根据 Id 更新英雄信息
*/
updateHeroById: (req, res) => {
const id = req.params.id
const newInfo = req.body
const sql = 'update heros set ? where id=?'
conn.query(sql, [newInfo, id], (err, result) => {
if (err) return res.send({ status: 500, msg: err.message, data: null })
res.send({ status: 200, msg: 'ok', data: null })
})
},
/**
* 根据Id删除英雄信息
*/
deleteHeroById: (req, res) => {
const id = req.params.id
const sql = 'update heros set isdel=1 where id=?'
conn.query(sql, id, (err, result) => {
if (err) return res.send({ status: 500, msg: err.message, data: null })
res.send({ status: 200, msg: 'ok', data: null })
})
}
}
后台接口设计
获取所有英雄列表
- 请求类型:GET
- 请求地址:
http://127.0.0.1:5001/getallhero
- 请求的参数:无
插入新的英雄数据
- 请求类型:POST
- 请求地址:
http://127.0.0.1:5001/addhero
- 请求参数:{ name, gender }
根据Id获取英雄信息
请求类型:GET
请求地址:http://127.0.0.1:5001/gethero/:id
请求参数:通过 URL 地址,把要查询的英雄Id,携带过去
根据Id更新英雄数据
请求类型: POST
请求地址:http://127.0.0.1:5001/updatehero/:id
请求参数:{ name, gender }
根据Id软删除英雄数据
软删除 只是将isdel的状态改变了 (isdel为 1,表示删除)
请求类型:GET
请求地址:http://127.0.0.1:5001/deletehero/:id
请求参数:通过 URL 地址栏传参,把 要删除的英雄Id提交给服务器