Express+Jquery实现购物车的前后端
2021-03-03 本文已影响0人
alicemum
需求分析:
列表页list.html
html结构
<p class="login-txt">登录</p>
<p> <a href="register.html">注册</a></p>
<p><a href="./shopCart.html">购物车</a></p>
<div id="box">
<ul>
</ul>
</div>
<div class="mask">
<div class="login">
<p><input type="text" class="user"/></p>
<p><input type="text" class="pw"/></p>
<p><button>登录</button>></p>
</div>
</div>
<script src="js/axios.js"></script>
<script src="js/jquery.js"></script>
<script src="js/jquery.cookie.js"></script>
<script src="js/list.js"></script>
<script src="js/addCart.js"></script>
<script src="js/login.js"></script>
列表页功能一: 初始化展示商品列表
前端代码
let page = 1; //页码
let mask = document.querySelector(".mask") //获取dom
// 渲染数据
function render(data) {
if (data.code === 0) {
let html = ''
data.list.forEach((item) => {
html += `
<li>
<a href="./detail.html?id=${item.Id}">
<div class="pro_img"><img src="https:${item.imgUrl}" width="150" height="150"></div>
<h3 class="pro_name"><a href="#"> ${item.title} </a></h3>
<p class="pro_price">${item.price}元</p>
<p></p>
<div class="add_btn" data-id="${item.Id}">加入购物车</div>
</a>
</li>
`
})
$("#box ul").append(html)
}
}
// 初始化加载数据
function initData() {
axios.get("/product",{
params: {
page
}
})
.then((res) => {
render(res.data)
})
}
initData()
后端代码
// 查询所有商品(分页显示)
router.get("/product", (req, res) => {
let { page } = req.query;
const pageSize = 10;
let start = (page - 1) * pageSize
let sql = `select * from midata order by Id asc limit ?,?`
conn.query(sql, [start, pageSize], function (err, result) {
if (err) {
console.log('查询数据库失败');
} else {
let data;
if (result.length) {
data = {
code: 0,
list: result
}
} else {
data = {
code: 1,
msg: '没有结果 '
}
}
res.send(data)
}
})
})
列表页功能二: 登录入口,可在本页面显示登录窗口,实现登录功能
// 每个页面中的登录
function login() {
$(".mask button").on("click", function () {
let data = {
user: $(".user").val(),
pw: $(".pw").val()
}
axios.post('/login', data)
.then((res) => {
console.log(res.data);
mask.classList.remove("active")
$.cookie("user",data.user,{ expires: 100})
})
})
}
//登录弹窗
function loginBox() {
let loginTxt = document.querySelector(".login-txt")
var login = document.querySelector(".login")
//绑定事件,单击登录时,显示弹窗
loginTxt.onclick = function () {
mask.classList.add("active")
}
//单击遮罩层,让遮罩层消失
mask.onclick = function () {
this.classList.remove("active")
}
//阻止冒泡
login.onclick = function (e) {
e.stopPropagation()
}
}
login()
loginBox()
列表页功能三: 每个商品实现加入购物车功能
先验证是否登录,如何没有登录,进行登录,如果已登录,则把商品id和用户id传向后端接口
前端代码
function cart() {
// 加入购物车
function addCart() {
$("#box ul").on("click", ".add_btn", function () {
//验证用户是否登录
let userId = $.cookie("user");
let proId = $(this).data("id") //获取商品Id
console.log(proId);
if (!userId) {
//当cookie不存在,即没有登录
mask.classList.add("active")
return;
}
let data = {
userId,
proId
}
axios.post("/cart",data)
.then((res)=>{
console.log(res.data);
})
})
}
addCart();
}
cart()
后端代码
// 添加商品到购物车
router.post("/cart", (req, res) => {
//接收参数
let { userId, proId } = req.body;
let sql = "select * from cart where userId = ? and proId = ?"
conn.query(sql, [userId, proId], function (err, result) {
if (err) {
console.log('数据库访问失败');
} else {
let data;
if (result.length) {
console.log('11');
//当前用户存在该商品
let cartId = result[0].cartId
let num = result[0].num + 1
// 更新商品数量
let sql = "update cart set num = ? where cartId = ?"
conn.query(sql, [num, cartId], function (err, result) {
if (result.affectedRows === 1) {
res.send({
code: 0,
msg: '添加购物车成功,数量更改'
})
}
})
} else {
console.log(22);
//当前用户不存在该商品
let sql = "insert into cart (userId,proId,num) values (?,?,?)"
conn.query(sql, [userId, proId, 1], function (err, result) {
if (result.affectedRows === 1) {
res.send({
code: 0,
msg: '添加购物车成功,数量初始为1'
})
}
})
}
}
})
})
列表页功能四: 加入购物车
后端接口接收商品id和用户id,先验证该用户的当前商品是否存在,如果不存在,则新增,如果存在,则修改数量
前端代码
function cart() {
// 加入购物车
function addCart() {
$("#box ul").on("click", ".add_btn", function () {
//验证用户是否登录
let userId = $.cookie("user");
let proId = $(this).data("id") //获取商品Id
console.log(proId);
if (!userId) {
//当cookie不存在,即没有登录
mask.classList.add("active")
return;
}
let data = {
userId,
proId
}
axios.post("/cart",data)
.then((res)=>{
console.log(res.data);
})
})
}
addCart();
}
cart()
后端代码
// 添加商品到购物车
router.post("/cart", (req, res) => {
//接收参数
let { userId, proId } = req.body;
let sql = "select * from cart where userId = ? and proId = ?"
conn.query(sql, [userId, proId], function (err, result) {
if (err) {
console.log('数据库访问失败');
} else {
let data;
if (result.length) {
console.log('11');
//当前用户存在该商品
let cartId = result[0].cartId
let num = result[0].num + 1
// 更新商品数量
let sql = "update cart set num = ? where cartId = ?"
conn.query(sql, [num, cartId], function (err, result) {
if (result.affectedRows === 1) {
res.send({
code: 0,
msg: '添加购物车成功,数量更改'
})
}
})
} else {
console.log(22);
//当前用户不存在该商品
let sql = "insert into cart (userId,proId,num) values (?,?,?)"
conn.query(sql, [userId, proId, 1], function (err, result) {
if (result.affectedRows === 1) {
res.send({
code: 0,
msg: '添加购物车成功,数量初始为1'
})
}
})
}
}
})
})
列表页功能五: 跳转至详情页
单击商品,跳转到详情页,并传递当前商品id
前端代码
<a href="./detail.html?id=${item.Id}">商品</a>
详情页: detail.html
详情页功能一: 初始化获取商品id, 请求后端接口,获取商品信息并渲染
前端代码
// 获取Id
function getId(){
let search = location.search; // "?id=100"
let result = search.match(/^\?id=(\d+)$/)
let id = result[1];
return id
}
//渲染数据
function render(res){
if (res.data.code === 0) {
let item = res.data.list[0]
let html = ''
html += `
<li>
<a href="./detail.html?id=${item.Id}">
<div class="pro_img"><img src="https:${item.imgUrl}" width="150" height="150"></div>
<h3 class="pro_name"><a href="#"> ${item.title} </a></h3>
<p class="pro_price">${item.hot}元</p>
<div class="add_btn" data-id="${item.Id}">加入购物车</div>
</a>
</li>
`
$("#box ul").append(html)
}
}
//获取指定id对应的商品信息
function getProduct() {
let id = getId()
axios.get(`/product/${id}`)
.then((res) => {
render(res)
})
}
getProduct()
后端代码
router.get("/product/:id", (req, res) => {
let { id } = req.params;
let sql = `select * from midata where id = ?`
conn.query(sql, [id], function (err, result) {
if (err) {
console.log('查询数据库失败');
} else {
let data;
if (result.length) {
data = {
code: 0,
list: result
}
} else {
data = {
code: 1,
msg: '没有结果 '
}
}
res.send(data)
}
})
})
详情页功能二: 加入购物车功能,逻辑参考列表页功能
购物车 shopCart.html
购物车功能一: 初始化请求后台接口,并传递当前用户id, 请求该用户的所有购物信息并展示
前端代码
// 查看用户是否登录
function checkIsLogin(userId) {
if (!userId) return;
//登录过
$("h1").addClass("hide")
$(".car").removeClass("hide")
}
//渲染数据
function render(res) {
if (res.data.code == 0) {
let html = ""
res.data.list.forEach((item) => {
html += `
<div class="row hid">
<div class="check left"> <input type="checkbox" class="select"></div>
<div class="img left"><img src="img/03-car-02.png" width="80" height="80"></div>
<div class="name left"><span> ${item.title} </span></div>
<div class="price left"><span>${item.price}元</span></div>
<div class="item_count_i">
<div class="num_count">
<div class="count_d" data-id="${item.cartId}">-</div>
<div class="c_num">${item.num}</div>
<div class="count_i" data-id="${item.cartId}">+</div>
</div>
</div>
<div class="subtotal left"><span>${item.price * item.num}元</span></div>
<div class="ctrl left"><a href="javascript:;" data-id="${item.cartId}">×</a></div>
</div>
`
})
$(".list").html(html)
}
}
// 请求购物车列表数据
function loadData() {
let userId = $.cookie("user")
checkIsLogin(userId)
// 请求数据
axios.get("/cart", {
params: {
userId
}
})
.then((res) => {
render(res)
})
}
后端代码
//查询购物车信息
router.get("/cart", (req, res) => {
let { userId } = req.query;
let sql = `select * from cart,midata where cart.userId = ? and cart.proId = midata.Id order by cart.cartId desc`
conn.query(sql, [userId], function (err, result) {
if (err) {
console.log('查询数据库失败');
} else {
let data;
if (result.length) {
data = {
code: 0,
list: result
}
} else {
data = {
code: 1,
msg: '没有结果 '
}
}
res.send(data)
}
})
})
购物车功能二: 复选框全选的单击事件: 选中时,所有商品也要选中,取消选择时,所有商品也取消选择. 同时计算总价
计算总价
let total = 0; //总价
//计算总价
function getTotal() {
total = 0;
[...$(".list .select")].forEach((item) => {
if ($(item).prop("checked")) {
let subTotal = parseFloat($(item).parent().parent().find(".subtotal span").text())
total += subTotal
}
})
$("#sum_area #price_num").text(total)
}
前端代码
// 全选功能
function checkAll() {
$(".check-all").on("click", function () {
let selected = $(this).prop("checked")
$(".list .select").prop("checked", selected)
getTotal()
})
}
购物车功能三. 每个商品复选框的单击事件: 单击时,要遍历所有商品的选中状态,都被选中,则选中“全选”复选框,都取消选中,则取消“全选”复选框。 同时计算总价
前端代码
//切换某一个商品的选择状态
function toggleOne() {
$(".list").on("click", ".select", function () {
let selected = $(this).prop("checked")
if (selected) {
let flag = [...$(".list .select")].every((item) => {
return $(item).prop("checked")
})
if (flag) {
$(".check-all").prop("checked", true)
}
} else {
$(".check-all").prop("checked", false)
}
getTotal()
})
}
购物车功能四. 加减数量: 获取原有数量,加或减后,调用后端接口,传递数量,CartId, 后端则把对应购物车信息的数量进行更新。 前端接收到更新成功后,计算小计价格,并同时计算总价
前端代码
//改变数量
function changeNum(el, type) {
$(".list").on("click", el, function () {
let num = $(this).siblings(".c_num").text()
type === 'add' ? num++ : num--
//后端接口
let data = {
cartId: $(this).data("id"),
num
}
axios.put("/cart", data)
.then((res) => {
//前端
$(this).parent().parent().siblings(".check").children(".select").prop("checked", true);
let price = parseFloat($(this).parent().parent().siblings(".price").children("span").text())
$(this).siblings(".c_num").text(num)
let subTotal = num * price
$(this).parent().parent().siblings(".subtotal").children("span").text(subTotal)
getTotal()
})
})
}
后端代码
//修改商品数量
router.put("/cart", (req, res) => {
//接收参数
let { cartId, num } = req.body;
let sql = "update cart set num = ? where cartId = ?"
conn.query(sql, [num, cartId], function (err, result) {
if (err) {
console.log('数据库访问失败');
} else {
if (result.affectedRows === 1) {
res.send({
code: 0,
msg: '添加购物车成功,数量更改'
})
}
}
})
})
购物车功能五: 删除: 调用后端接口,传递CartId, 在后端删除指定CartId的信息,前端接收到成功删除后,再次到后端请求当前用户所有购物车信息并渲染
前端代码
// 删除购物车信息
function delCart() {
$(".list").on("click", ".ctrl a", function () {
let cartId = $(this).data("id")
axios.delete(`/cart/${cartId}`)
.then((res) => {
loadData()
})
})
}
后端代码
//删除购物车信息
router.delete("/cart/:cartId", (req, res) => {
let { cartId } = req.params;
let sql = `delete from cart where cartId = ?`
conn.query(sql, [cartId], function (err, result) {
if (err) {
console.log('查询数据库失败');
} else {
let data;
if (result.affectedRows === 1) {
data = {
code: 0,
msg: '删除成功'
}
}
res.send(data)
}
})
})