13. 带你实现购物车功能
SQL
CREATE TABLE `cart` (
`id` int NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`goods_id` int DEFAULT NULL COMMENT '商品ID',
`num` int DEFAULT NULL COMMENT '数量',
`user_id` int DEFAULT NULL COMMENT '用户ID',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='购物车信息';
开发后端接口
Cart.java
package com.example.entity;
import java.math.BigDecimal;
public class Cart {
/**主键ID */
private Integer id;
/**商品ID */
private Integer goodsId;
private String goodsName;
private String goodsImg;
private BigDecimal goodsPrice;
/**数量 */
private Integer num;
/**用户ID */
private Integer userId;
public BigDecimal getGoodsPrice() {
return goodsPrice;
}
public void setGoodsPrice(BigDecimal goodsPrice) {
this.goodsPrice = goodsPrice;
}
public String getGoodsImg() {
return goodsImg;
}
public void setGoodsImg(String goodsImg) {
this.goodsImg = goodsImg;
}
public String getGoodsName() {
return goodsName;
}
public void setGoodsName(String goodsName) {
this.goodsName = goodsName;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getGoodsId() {
return goodsId;
}
public void setGoodsId(Integer goodsId) {
this.goodsId = goodsId;
}
public Integer getNum() {
return num;
}
public void setNum(Integer num) {
this.num = num;
}
public Integer getUserId() {
return userId;
}
public void setUserId(Integer userId) {
this.userId = userId;
}
}
CartMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.CartMapper">
<select id="selectAll" resultType="com.example.entity.Cart">
select cart.*, goods.name as goodsName, goods.img as goodsImg, goods.price as goodsPrice from `cart`
left join goods on cart.goods_id = goods.id
<where>
<if test="userId != null"> and cart.user_id = #{userId}</if>
</where>
order by cart.id desc
</select>
<select id="selectById" resultType="com.example.entity.Cart">
select * from `cart` where id = #{id}
</select>
<delete id="deleteById">
delete from `cart` where id = #{id}
</delete>
<insert id="insert" parameterType="com.example.entity.Cart" useGeneratedKeys="true">
insert into `cart`
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">id,</if>
<if test="goodsId != null">goods_id,</if>
<if test="num != null">num,</if>
<if test="userId != null">user_id,</if>
</trim>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="id != null">#{id},</if>
<if test="goodsId != null">#{goodsId},</if>
<if test="num != null">#{num},</if>
<if test="userId != null">#{userId},</if>
</trim>
</insert>
<update id="updateById" parameterType="com.example.entity.Cart">
update `cart`
<set>
<if test="id != null">
id = #{id},
</if>
<if test="goodsId != null">
goods_id = #{goodsId},
</if>
<if test="num != null">
num = #{num},
</if>
<if test="userId != null">
user_id = #{userId},
</if>
</set>
where id = #{id}
</update>
</mapper>
详情页 addCart
const addCart = () => {
request.post('/cart/add', { goodsId: data.id, num: data.num, userId: data.user.id }).then(res => {
if (res.code === '200') {
ElMessage.success('加入购物车成功')
} else {
ElMessage.error(res.msg)
}
})
}
删除或者下架商品需要删除对应购物车的商品
/**
* 删除
*/
@Transactional
public void deleteById(Integer id) {
goodsMapper.deleteById(id);
// 删除购物车的对应的商品
cartMapper.deleteByGoodsId(id);
}
/**
* 修改
*/
public void updateById(Goods goods) {
goodsMapper.updateById(goods);
if (goods.getStatus().equals("下架")) {
// 删除购物车的对应的商品
cartMapper.deleteByGoodsId(goods.getId());
}
}
前端页面
Cart.vue
<template>
<div class="front-container">
<div class="card" style="padding: 20px">
<div>
<el-table :data="data.tableData" stripe @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" />
<el-table-column label="商品图片">
<template #default="scope">
<img style="width: 50px; height: 50px; display: block" :src="scope.row.goodsImg" alt="">
</template>
</el-table-column>
<el-table-column label="商品名称" prop="goodsName"></el-table-column>
<el-table-column label="商品单价">
<template #default="scope">
<b style="color: red">¥{{ scope.row.goodsPrice }}</b>
</template>
</el-table-column>
<el-table-column label="商品数量">
<template #default="scope">
<el-input-number @change="calTotal" v-model="scope.row.num" :min="1" style="width: 150px"></el-input-number>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="100">
<template #default="scope">
<el-button type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div style="text-align: right; margin-top: 20px; font-size: 20px">总价格:
<b style="color: red; display: inline-block; min-width: 60px; text-align: left">{{ data.total }} 元</b>
<div style="margin-top: 10px"><el-button @click="addOrder" type="danger">立即下单</el-button></div>
</div>
</div>
<el-dialog title="下单信息" width="30%" v-model="data.formVisible" :close-on-click-modal="false" destroy-on-close>
<el-form ref="formRef" :model="data.form" :rules="data.rules" label-width="80px" style="padding-right: 30px; padding-top: 20px">
<el-form-item label="名称" prop="name">
<el-input v-model="data.form.name" autocomplete="off" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="data.formVisible = false">取 消</el-button>
<el-button type="primary" @click="save">保 存</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import request from "@/utils/request";
import {reactive, ref} from "vue";
import {ElMessageBox, ElMessage} from "element-plus";
const formRef = ref()
const data = reactive({
user: JSON.parse(localStorage.getItem('system-user') || '{}'),
total: 0,
formVisible: false,
form: {},
tableData: [],
selectedRows: [],
rules: {
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
]
}
})
const calTotal = () => {
data.total = 0
// rows是选中行
data.selectedRows.forEach(item => {
data.total += item.goodsPrice * item.num
})
if (data.total > 0) {
data.total = data.total.toFixed(2)
}
}
const handleSelectionChange = (rows) => {
data.selectedRows = rows
calTotal()
}
// 分页查询
const load = () => {
request.get('/cart/selectAll', {
params: {
userId: data.user.id
}
}).then(res => {
data.tableData = res.data
})
}
load()
// 新增
const handleAdd = () => {
data.form = {}
data.formVisible = true
}
// 编辑
const handleEdit = (row) => {
data.form = JSON.parse(JSON.stringify(row))
data.formVisible = true
}
// 新增保存
const add = () => {
request.post('/cart/add', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 编辑保存
const update = () => {
request.put('/cart/update', data.form).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
data.formVisible = false
} else {
ElMessage.error(res.msg)
}
})
}
// 弹窗保存
const save = () => {
formRef.value.validate(valid => {
if (valid) {
// data.form有id就是更新,没有就是新增
data.form.id ? update() : add()
}
})
}
// 删除
const handleDelete = (id) => {
ElMessageBox.confirm('您确定删除吗?', '删除确认', { type: 'warning' }).then(res => {
request.delete('/cart/delete/' + id).then(res => {
if (res.code === '200') {
load()
ElMessage.success('操作成功')
} else {
ElMessage.error(res.msg)
}
})
}).catch(err => {})
}
// 重置
const reset = () => {
data.name = null
load()
}
</script>
我们只查询上架的商品
