likes
comments
collection
share

uniapp+node.js前后端平台的购物车列表(单选,全选,增加,减少,模糊搜索,删除)(社区管理平台的小程序)

作者站长头像
站长
· 阅读数 33

@TOC


👍 点赞,你的认可是我创作的动力!

⭐️ 收藏,你的青睐是我努力的方向!

✏️ 评论,你的意见是我进步的财富!


0前提

温馨提示:我做的思路可能是复杂化了或者说代码写的不规范,如果你觉得可以更加简便的话欢迎分享到评论区或者自己改写一下我的代码,我的后端是写的很简单的没有什么路由分发是直接写的,你可以自由优化,以及在需要验证用户是否登录和验证用户token是否正确的我没有进行验证,你们可以自行添加 小程序的其他部分你可以看看我往期的文章

1.一些准备

1.1表

购物车表shopCart

字段名称类型(长度)允许空主键外键自增唯一说明
idint购物车id
countint商品数量
commodityIdint商品id
userIdint用户id
creatTimetimestamp创建时间
updateTimetimestamp更新时间

1.2总体思路

描述:用户在点击购买东西或者是点击购物车时到购物车页面,购物车页面上面显示搜索框用于模糊搜索购物车里面的商品,中间内容显示用户已有的购物车商品列表,下方固定显示总价以及去结算和删除的按钮。

实现:当用户进入购物车页面时,先获取用户对应的购物车商品列表,然后显示出来的商品列表由计算属性里面的方法控制,输入框没有东西就显示全部如果有内容就显示相关的商品列表,计算属性还有一个方法就是计算已勾选的商品的总价。当用户点击全选时,全选状态随之变化,勾选单个按钮时checked状态也随之变化是否勾选,还有勾选删除、勾选结算、加加减数量都是根据那一行的商品id进行控制的

2.前端

data:全选状态,输入框内容,商品列表 计算属性:计算总价,计算返回显示的商品列表 methods里面的方法:获取商品列表,删除特定商品,全选/全不选,勾选/取消勾选,更新商品数量,增加商品数量,减少商品数量

代码实现:

     
export default {
  data() {
    return {
      allChecked: false,
      inputs: '',
      list: []
    };
  },
  computed: {
    totalPrice() {
      //总计金额
      var str = 0;
      for (var i = 0; i < this.searchData.length; i++) {
        if (this.searchData[i].checked) {
          str += this.searchData[i].count * this.searchData[i].price;
        }
      }
      return str;
    },
    searchData: function () {
      //模糊查询
      if (!this.inputs) {
        return this.list;
      }
      return this.list.filter((item) => {
        return item.name.includes(this.inputs);
      });
    }
  },
  onLoad() {
    this.myshopcar();
  },
  methods: {
    async deleteItems() {
      // 找到被选中的商品的商品ID
      const selectedItemsIds = this.list.filter((item) => item.checked).map((item) => item.id);
      if (selectedItemsIds.length === 0) {
        uni.showToast({
          title: '请选择要删除的商品',
          icon: 'none'
        });
        return;
      }
      // 发送请求删除选中的商品
      try {
        const res = await this.$myRequest({
          method: 'post',
          url: '/deleteItems',
          data: {
            itemIds: selectedItemsIds // 传入被选中商品的商品ID
          }
        });

        if (res.error) {
          this.$message.error(res.error);
        } else {
          // 删除成功后更新列表
          this.list = this.list.filter((item) => !item.checked);
        }
      } catch (error) {
        console.error('请求发生错误:', error);
        uni.showToast({
          title: '删除失败,请稍后再试',
          icon: 'none'
        });
      }
    },
    // 跳转到订单结算页
    goorder(id) {
      uni.navigateTo({
        url: '../userOder/userOder?orderId=' + id
      });
    },
    // 结算方法
    async settleAccounts(totalPrice) {
      // 获取选中的商品
      const selectedItems = this.list.filter((item) => item.checked);
      // 如果没有选中的商品,给出提示
      if (selectedItems.length === 0) {
        uni.showToast({
          title: '请先选择商品',
          icon: 'none'
        });
        return;
      }
      try {
        // 构建包含选中商品信息的数据对象
        const orderData = {
          userId: this.$store.state.user.id,
          totalPrice: totalPrice,
          items: selectedItems.map((item) => ({
            commodityId: item.commodityId, // 商品ID,根据实际情况修改
            count: item.count, // 商品数量
            price: item.price // 商品数量
          }))
        };
        console.log(orderData);
        // 发送请求到后端进行下单
        const res = await this.$myRequest({
          method: 'post',
          url: '/placeOrder',
          data: orderData
        });

        // 假设后端返回错误时包含一个名为 'error' 的字段
        if (res.error) {
          this.$message.error(res.error);
        } else {
          console.log(res.data.orderId);
          // 下单成功,清空购物车
          this.deleteItems(); //删除掉所选的商品
          // 清空购物车
          this.list = this.list.filter((item) => !item.checked);
          this.goorder(res.data.orderId); //跳转的下单页
        }
      } catch (error) {
        // 捕获异常,显示通用错误消息或者其他处理
        console.error('请求发生错误:', error);
      }
    },
    // 获取购物车数据
    async myshopcar() {
      try {
        const res = await this.$myRequest({
          method: 'get',
          url: '/myshopcar?userId=' + this.$store.state.user.id
        });
        console.log(res.data.data);
        // 假设后端返回错误时包含一个名为 'error' 的字段
        if (res.error) {
          this.$message.error(res.error);
        } else {
          this.list = res.data.data;
        }
      } catch (error) {
        // 捕获异常,显示通用错误消息或者其他处理
        console.error('请求发生错误:', error);
      }
    },
    //更新商品数量
    async update(item, count) {
      // 发送请求更新商品数量
      try {
        const res = await this.$myRequest({
          method: 'post',
          url: '/updateCartcount',
          data: {
            commodityId: item.id, // 假设后端要求发送商品的ID
            count: item.count // 告知后端增加的数量
          }
        });
        if (res.error) {
          this.$message.error(res.error);
          // 如果请求失败,需要回滚数量的增加操作
          item.count = count;
        } else {
        }
      } catch (error) {
        console.error('请求发生错误:', error);
        // 如果请求失败,需要回滚数量的增加操作
        item.count = count;
        uni.showToast({
          title: '增加失败,请稍后再试',
          icon: 'none'
        });
      }
    },
    // 增加商品数量
    async add(item) {
      // 加加
      let count = item.count;
      //  item 中包含了库存信息,item.stock
      if (item.stock && count >= item.stock) {
        uni.showToast({
          title: '已达到库存上限',
          icon: 'none'
        });
        return;
      }
      item.count = count + 1;
      this.update(item, count);
    },
    // 减少商品数量
    async reduce(item) {
      // 减减
      let count = item.count;
      if (count > 1) {
        count -= 1;
      } else {
        uni.showToast({
          title: '该宝贝不能减少了哟~'
        });
        return;
      }
      item.count = count;
      this.update(item, count + 1);
    },
    // 单个商品的选择
    checkClick(item) {
      item.checked = !item.checked;
      if (!item.checked) {
        this.allChecked = false;
      } else {
        // 判断每一个商品是否是被选择的状态
        const goods = this.list.every((item) => {
          return item.checked === true;
        });
        if (goods) {
          this.allChecked = true;
        } else {
          this.allChecked = false;
        }
      }
    },
    //全选、全不选
    checkAll() {
      this.allChecked = !this.allChecked;
      if (this.allChecked) {
        this.list.map((item) => {
          item.checked = true;
        });
      } else {
        this.list.map((item) => {
          item.checked = false;
        });
      }
    }
  }
};

3.后端

(接口编写逻辑:接口名字-接收前端传值-sql语句-sql操作-返回信息) 接口分别是:获取我的购物车列表,更新特定商品数量到购物车表,购物车删除商品 ,跳转下单页

代码实现:

// 获取我的购物车列表
app.get('/myshopcar', (req, res) => {
  const userId = req.query.userId; // 假设从请求中获取用户ID
  const query = `
    SELECT 
      shopcart.id ,
      shopcart.count,
      commodity.id as commodityId,
      commodity.stock,
      commodity.name,
      commodity.price,
      commodity.coverImage
    FROM shopcart
    INNER JOIN commodity ON shopcart.commodityId = commodity.id
    WHERE shopcart.userId = ?`; // 使用内连接进行并联查询

  connection.query(query, [userId], (err, results) => {
    if (err) {
      console.log(err);
      return res.json({
        error: '获取购物车列表失败,请稍后重试',
      });
    }
    // 为每个商品对象添加selected属性,并设置为false
    const modifiedResults = results.map(item => ({
      ...item,
      checked: false
    }));
    // 返回购物车列表
    res.json({
      data: modifiedResults,
    });
  });
});
// 更新特定商品数量到购物车表
app.post('/updateCartcount', (req, res) => {
  const {
    commodityId,
    count
  } = req.body; // 假设从请求体中获取用户ID、商品ID和数量

  const query = `SELECT * FROM shopcart WHERE commodityId=?`;
  connection.query(query, [commodityId], (err, results) => {
    if (err) {
      console.log(err);
      return res.status(500).json({
        error: '查询购物车失败,请稍后重试'
      });
    }

    if (results.length > 0) {
      // 如果购物车中已存在该商品,则更新数量
      const updateQuery = `UPDATE shopcart SET count=? WHERE commodityId=?`;
      connection.query(updateQuery, [count, commodityId], (err, updateResult) => {
        if (err) {
          console.log(err);
          return res.status(500).json({
            error: '更新购物车失败,请稍后重试'
          });
        }
        res.json({
          message: '成功更新购物车'
        });
      });
    } else {
      // 如果购物车中不存在该商品,则返回错误
      res.status(404).json({
        error: '购物车中不存在该商品'
      });
    }
  });
});

// 购物车删除商品  
app.post('/deleteItems', (req, res) => {
  const {
    itemIds
  } = req.body;
  console.log(itemIds)
  if (!Array.isArray(itemIds) || itemIds.length === 0) {
    return res.status(400).json({
      error: '传入的商品ID不正确或未提供'
    });
  }

  // 生成占位符字符串,例如 '?,?,?'  
  const placeholders = itemIds.map(() => '?').join(',');

  // 构建SQL查询语句  
  const query = `DELETE FROM shopcart WHERE id IN (${placeholders})`;

  // 执行删除操作  
  connection.query(query, itemIds, (err, results) => {
    if (err) {
      console.error('删除商品失败:', err);
      return res.status(500).json({
        error: '删除商品失败,请稍后重试'
      });
    }

    // 返回成功响应  
    res.json({
      message: '删除成功'
    });
  });
});
// 跳转下单页
app.post('/placeOrder', (req, res) => {
  try {
    const {
      userId,
      totalPrice,
      items
    } = req.body;

    // 生成订单号,假设这里简单地使用时间戳  
    const orderNumber = generateOrderNumber();

    // 构建插入订单数据的 SQL 查询语句  
    const orderQuery = 'INSERT INTO `order` (num, userId, totalPrice) VALUES (?, ?, ?)';
    connection.query(orderQuery, [orderNumber, userId, totalPrice], (orderErr, orderResult) => {
      if (orderErr) {
        console.error('存储订单失败:', orderErr);
        res.status(500).json({
          error: '订单提交失败,请稍后重试'
        });
        return;
      }

      const orderId = orderResult.insertId; // 获取订单ID  

      // 构建插入订单商品数据的 SQL 查询语句  
      const insertItemPromises = items.map((item) => {
        const {
          commodityId,
          count,
          price
        } = item;
        // 使用参数化查询避免 SQL 注入  
        const itemQuery =
          'INSERT INTO orderdetail (orderId, commodityId, count, price) VALUES (?, ?, ?, ?)';
        connection.query(itemQuery, [orderId, commodityId, count, price], (itemErr) => {
          if (itemErr) {
            console.error('存储订单商品失败:', itemErr);
            res.status(500).json({
              error: '订单商品提交失败,请稍后重试'
            });
            return;
          }
        });
      });

      // 返回订单ID,而不是订单号  
      res.json({
        message: '订单已成功下单',
        orderId
      });
    });

  } catch (error) {
    console.error('存储订单失败:', error);
    res.status(500).json({
      error: '订单提交失败,请稍后重试'
    });
  }
});

4.验证结果

uniapp+node.js前后端平台的购物车列表(单选,全选,增加,减少,模糊搜索,删除)(社区管理平台的小程序) uniapp+node.js前后端平台的购物车列表(单选,全选,增加,减少,模糊搜索,删除)(社区管理平台的小程序) uniapp+node.js前后端平台的购物车列表(单选,全选,增加,减少,模糊搜索,删除)(社区管理平台的小程序)

5.每日一题:

v-if和v-show的区别

v-if 和 v-show 都是在 Vue.js 中用于控制元素是否显示或隐藏的指令,但它们之间有一些关键的区别: 渲染方式: v-if:是“真实”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。当条件为假时,什么都不会做,条件块之内的东西不会渲染到 DOM 中。 v-show:只是简单地基于 CSS 进行切换。无论初始条件是什么,元素始终都会被渲染,并且只是简单地基于 CSS 进行显示与隐藏。

性能考虑: 当条件频繁改变时,v-show 比 v-if 有更好的性能,因为它只是简单地切换 CSS 的 display 属性。 如果你的元素可能永远不会被显示出来,或者条件不经常改变,那么使用 v-if 会更好,因为它会确保不必要的 DOM 操作被避免。

CSS 与 JavaScript 交互: 如果你需要让元素即使在隐藏时也能保留在 DOM 中,那么 v-show 是有用的,因为元素仍然渲染到 DOM 中,并且你可以使用 JavaScript 访问它(例如,通过 ref)。 另一方面,如果你想要通过 JavaScript 来改变一个条件,并且这个条件会触发 DOM 结构的变化,那么 v-if 可能是更好的选择。

初始渲染: 当条件为假时,v-if 控制的元素在初始渲染时不会出现在 DOM 中。 而 v-show 则会渲染元素,但使用 CSS 将其隐藏。

与 v-else、v-else-if 的组合: v-if 可以与 v-else 和 v-else-if 一起使用,以提供多个条件渲染选项。 v-show 则不支持这种组合。

转载自:https://juejin.cn/post/7369884289711931443
评论
请登录