SchoolDash Alpha冲刺随笔4 - Day 7

SchoolDash Alpha冲刺随笔4 - Day 7
课程与作业信息

所属课程:软件工程实践
作业要求来源:第五次作业——Alpha冲刺

  1. 项目燃尽图(Burn-up Chart)

当前冲刺总Story Point:50 SP(已完成46 SP,剩余4 SP)
在这里插入图片描述

  1. 本日冲刺整体进展

完成购物车管理与订单创建。
前端添加/更新/删除购物车项,后端同步订单表。

  1. 项目最新运行效果

视频演示

SchoolDash商品添加购物车及结算演示视频

购物车页面图片

在这里插入图片描述

  1. 成员今日工作成果

(后端开发)
购物车

const express = require('express');
const router = express.Router();
const Cart = require('../models/Cart');
const Goods = require('../models/Goods');
const Category = require('../models/Category');
const { auth } = require('../middleware/auth');

// 定义模型关联(确保关联已设置)
Category.hasMany(Goods, { foreignKey: 'categoryId' });
Goods.belongsTo(Category, { foreignKey: 'categoryId' });
Cart.belongsTo(Goods, { foreignKey: 'goodsId' });

// 获取购物车列表
router.get('/list', auth, async (req, res) => {
  try {
    console.log('获取购物车列表,用户ID:', req.user.id);
    
    // 先测试直接查询商品
    const testGoods = await Goods.findByPk(1);
    console.log('测试查询商品ID 1:', testGoods ? testGoods.name : '未找到');
    
    // 测试直接查询购物车
    const testCart = await Cart.findByPk(3);
    console.log('测试查询购物车ID 3:', testCart ? `商品ID: ${testCart.goodsId}` : '未找到');
    
    // 测试关联查询
    const cartItems = await Cart.findAll({
      where: { userId: req.user.id },
      include: [{
        model: Goods,
        as: 'Good',
        include: [{ model: Category, attributes: ['name'] }]
      }],
      order: [['createdAt', 'DESC']]
    });
    
    console.log('找到购物车商品数量:', cartItems.length);
    console.log('购物车原始数据:', JSON.stringify(cartItems, null, 2));

    // 确保商品价格为数字类型,并适配前端字段名
    const formattedCartItems = cartItems.map(item => {
      // 调试:检查原始数据中的商品信息
      console.log(`处理购物车项 ID: ${item.id}, 商品ID: ${item.goodsId}`);
      console.log('原始商品数据:', JSON.stringify(item.Goods || item.Good, null, 2));
      
      // 使用正确的属性名访问商品信息
      const goodsInfo = item.Good;
      
      return {
        ...item.toJSON(),
        // 前端期望的字段名
        goods_id: item.goodsId,
        num: item.quantity,
        goodsName: goodsInfo ? goodsInfo.name : '未知商品',
        price: goodsInfo ? parseFloat(goodsInfo.price) || 0 : 0,
        checked: item.selected !== false, // 默认选中
        Good: goodsInfo ? {
          ...goodsInfo.toJSON ? goodsInfo.toJSON() : goodsInfo,
          price: parseFloat(goodsInfo.price) || 0
        } : null
      };
    });
    
    console.log('格式化后的购物车数据:', JSON.stringify(formattedCartItems, null, 2));

    res.json({
      code: 200,
      msg: '获取购物车成功',
      data: formattedCartItems
    });
  } catch (error) {
    console.error('获取购物车失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 添加商品到购物车
router.post('/add', auth, async (req, res) => {
  try {
    console.log('添加商品到购物车,用户ID:', req.user.id);
    console.log('请求体数据:', req.body);
    
    const { goods_id, goodsId, num = 1 } = req.body;
    // 兼容前端两种参数名
    const finalGoodsId = goods_id || goodsId;
    
    console.log('最终商品ID:', finalGoodsId, '数量:', num);

    if (!finalGoodsId) {
      return res.status(400).json({ code: 400, msg: '商品ID不能为空' });
    }

    // 检查商品是否存在
    const goods = await Goods.findByPk(finalGoodsId);
    console.log('查询到的商品:', goods ? goods.name : '不存在');
    
    if (!goods) {
      return res.status(404).json({ code: 404, msg: '商品不存在' });
    }

    // 检查库存
    if (goods.stock < num) {
      return res.status(400).json({ code: 400, msg: '库存不足' });
    }

    // 查找是否已存在
    const existingCart = await Cart.findOne({
      where: { userId: req.user.id, goodsId: finalGoodsId }
    });
    
    console.log('是否已存在于购物车:', !!existingCart);

    if (existingCart) {
      // 更新数量
      const newQuantity = existingCart.quantity + num;
      if (goods.stock < newQuantity) {
        return res.status(400).json({ code: 400, msg: '库存不足' });
      }
      await existingCart.update({ quantity: newQuantity });
      console.log('更新购物车数量为:', newQuantity);
    } else {
      // 创建新记录
      await Cart.create({
        userId: req.user.id,
        goodsId: finalGoodsId,
        quantity: num
      });
      console.log('创建新购物车记录');
    }

    res.json({ code: 200, msg: '添加成功' });
  } catch (error) {
    console.error('添加购物车失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 更新购物车商品数量
router.put('/update', auth, async (req, res) => {
  try {
    const { id, quantity, num } = req.body;
    // 兼容前端两种参数名
    const finalQuantity = quantity !== undefined ? quantity : num;

    if (!id || finalQuantity === undefined) {
      return res.status(400).json({ code: 400, msg: '参数不完整' });
    }

    if (finalQuantity <= 0) {
      return res.status(400).json({ code: 400, msg: '数量必须大于0' });
    }

    const cartItem = await Cart.findOne({
      where: { id, userId: req.user.id },
      include: [{ model: Goods, as: 'Good' }]
    });

    if (!cartItem) {
      return res.status(404).json({ code: 404, msg: '购物车商品不存在' });
    }

    // 检查库存
    if (cartItem.Good.stock < finalQuantity) {
      return res.status(400).json({ code: 400, msg: '库存不足' });
    }

    await cartItem.update({ quantity: finalQuantity });
    res.json({ code: 200, msg: '更新成功' });
  } catch (error) {
    console.error('更新购物车失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 删除购物车商品
router.delete('/delete', auth, async (req, res) => {
  try {
    const { id } = req.query;

    if (!id) {
      return res.status(400).json({ code: 400, msg: '购物车ID不能为空' });
    }

    const deleted = await Cart.destroy({
      where: { id, userId: req.user.id }
    });

    if (deleted === 0) {
      return res.status(404).json({ code: 404, msg: '购物车商品不存在' });
    }

    res.json({ code: 200, msg: '删除成功' });
  } catch (error) {
    console.error('删除购物车失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 兼容前端POST请求删除购物车商品
router.post('/delete', auth, async (req, res) => {
  try {
    const { id } = req.body;

    if (!id) {
      return res.status(400).json({ code: 400, msg: '购物车ID不能为空' });
    }

    const deleted = await Cart.destroy({
      where: { id, userId: req.user.id }
    });

    if (deleted === 0) {
      return res.status(404).json({ code: 404, msg: '购物车商品不存在' });
    }

    res.json({ code: 200, msg: '删除成功' });
  } catch (error) {
    console.error('删除购物车失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

module.exports = router;

订单路由

const express = require('express');
const router = express.Router();
const Order = require('../models/Order');
const OrderItem = require('../models/OrderItem');
const Cart = require('../models/Cart');
const Address = require('../models/Address');
const Goods = require('../models/Goods');
const { auth } = require('../middleware/auth');

// 生成订单编号
function generateOrderNumber() {
  const date = new Date();
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
  return `ORD${year}${month}${day}${random}`;
}

// 获取用户订单列表
router.get('/orders', auth, async (req, res) => {
  try {
    console.log('订单列表API被调用,用户:', req.user.username);
    const { status } = req.query;
    console.log('请求参数:', { status, userId: req.user.id });
    
    let whereCondition = { userId: req.user.id };
    if (status && status !== '') {
      whereCondition.status = status;
    }
    console.log('查询条件:', whereCondition);

    const orders = await Order.findAll({
      where: whereCondition,
      include: [
        { model: Address, as: 'address' },
        { model: require('../models/User'), as: 'rider', attributes: ['username'] },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ],
      order: [['createdAt', 'DESC']]
    });

    console.log('查询到订单数量:', orders.length);

    // 格式化订单数据,确保价格是数字类型
    const formattedOrders = orders.map(order => {
      const orderData = {
        ...order.toJSON(),
        totalAmount: parseFloat(order.totalAmount) || 0,
        // 前端期望的字段名
        orderNo: order.orderNumber,
        totalPrice: parseFloat(order.totalAmount) || 0,
        goodsList: order.OrderItems.map(item => ({
          goodsId: item.goodsId,
          name: item.Good ? item.Good.name : '未知商品',
          price: parseFloat(item.price) || 0,
          count: item.quantity
        }))
      };
      // 删除不需要的字段
      delete orderData.OrderItems;
      return orderData;
    });

    console.log('返回订单数据成功');
    res.json({
      code: 200,
      msg: '获取订单列表成功',
      data: formattedOrders
    });
  } catch (error) {
    console.error('获取订单列表失败:', error);
    console.error('错误详情:', error.message);
    console.error('错误堆栈:', error.stack);
    res.status(500).json({ 
      code: 500, 
      msg: '服务器错误',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
});

// 获取选中的购物车商品
router.get('/cart/selected', auth, async (req, res) => {
  try {
    console.log('获取选中购物车商品API被调用');
    console.log('用户ID:', req.user.id);
    
    // 支持两种方式:1. 查询参数中的cartIds 2. 获取所有选中的商品
    const { cartIds } = req.query;
    let whereCondition = { userId: req.user.id };
    
    if (cartIds) {
      // 如果提供了cartIds,只获取这些ID的商品
      // 处理cartIds可能是数组或字符串的情况
      let cartIdArray;
      if (Array.isArray(cartIds)) {
        cartIdArray = cartIds;
      } else {
        cartIdArray = cartIds.split(',').map(id => parseInt(id.trim()));
      }
      whereCondition.id = cartIdArray;
      console.log('使用cartIds查询:', cartIdArray);
    } else {
      // 否则获取所有选中的商品
      whereCondition.selected = true;
      console.log('查询所有选中的商品');
    }

    console.log('查询条件:', whereCondition);

    const cartItems = await Cart.findAll({
      where: whereCondition,
      include: [{ model: Goods, as: 'Good' }]
    });

    console.log('查询到的购物车项数量:', cartItems.length);

    // 计算总金额并格式化数据
    let totalAmount = 0;
    const formattedItems = cartItems.map(item => {
      // 检查商品信息是否存在
      if (!item.Good) {
        console.error(`购物车项 ID ${item.id} 缺少关联的商品信息`);
        return null;
      }
      
      const price = parseFloat(item.Good.price) || 0;
      const quantity = item.quantity || 1;
      const itemTotal = price * quantity;
      totalAmount += itemTotal;
      
      return {
        id: item.id,
        goods_id: item.goodsId,
        name: item.Good.name,
        price: price,
        count: quantity,
        stock: item.Good.stock
      };
    }).filter(item => item !== null); // 过滤掉null项

    res.json({
      code: 200,
      msg: '获取选中商品成功',
      data: formattedItems,
      totalAmount: totalAmount.toFixed(2)
    });
  } catch (error) {
    console.error('获取选中商品失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 创建订单
router.post('/order', auth, async (req, res) => {
  const transaction = await require('../config/db').transaction();

  try {
    const { addressId, notes, cartIds } = req.body;

    // 获取选中的购物车商品
    let whereCondition = { userId: req.user.id };
    
    if (cartIds) {
      // 如果提供了cartIds,只获取这些ID的商品
      // 处理cartIds可能是数组或字符串的情况
      let cartIdArray;
      if (Array.isArray(cartIds)) {
        cartIdArray = cartIds;
      } else {
        cartIdArray = cartIds.split(',').map(id => parseInt(id.trim()));
      }
      whereCondition.id = cartIdArray;
    } else {
      // 否则获取所有选中的商品
      whereCondition.selected = true;
    }

    const cartItems = await Cart.findAll({
      where: whereCondition,
      include: [{ model: Goods, as: 'Good' }],
      transaction
    });

    if (cartItems.length === 0) {
      await transaction.rollback();
      return res.status(400).json({ code: 400, msg: '没有选中商品' });
    }

    // 检查地址是否存在
    const address = await Address.findOne({
      where: { id: addressId, userId: req.user.id },
      transaction
    });

    if (!address) {
      await transaction.rollback();
      return res.status(400).json({ code: 400, msg: '收货地址不存在' });
    }

    // 检查库存并计算总金额
    let totalAmount = 0;
    for (const item of cartItems) {
      if (item.Good.stock < item.quantity) {
        await transaction.rollback();
        return res.status(400).json({
          code: 400,
          msg: `商品 ${item.Good.name} 库存不足`
        });
      }
      totalAmount += parseFloat(item.Good.price) * item.quantity;
    }

    // 创建订单
    const order = await Order.create({
      orderNumber: generateOrderNumber(),
      userId: req.user.id,
      addressId: addressId,
      totalAmount: totalAmount.toFixed(2),
      status: 'paid', // 创建后直接设为已支付状态
      paymentStatus: 'paid',
      paymentMethod: 'online',
      notes: notes || ''
    }, { transaction });

    // 创建订单项
    for (const item of cartItems) {
      await OrderItem.create({
        orderId: order.id,
        goodsId: item.goodsId,
        quantity: item.quantity,
        price: item.Good.price,
        totalPrice: (parseFloat(item.Good.price) * item.quantity).toFixed(2)
      }, { transaction });

      // 减少库存
      await Goods.update({
        stock: item.Good.stock - item.quantity
      }, {
        where: { id: item.goodsId },
        transaction
      });
    }

    // 清空已购买的购物车商品
    await Cart.destroy({
      where: whereCondition,
      transaction
    });

    await transaction.commit();

    res.json({
      code: 200,
      msg: '订单创建成功',
      data: {
        orderId: order.id,
        orderNumber: order.orderNumber,
        totalAmount: order.totalAmount
      }
    });

  } catch (error) {
    await transaction.rollback();
    console.error('创建订单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 获取订单详情
router.get('/orders/:id', auth, async (req, res) => {
  try {
    const orderId = req.params.id;
    
    const order = await Order.findOne({
      where: { 
        id: orderId,
        userId: req.user.id // 确保用户只能查看自己的订单
      },
      include: [
        { model: Address, as: 'address' },
        { model: require('../models/User'), as: 'rider', attributes: ['username', 'phone'] },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ]
    });

    if (!order) {
      return res.status(404).json({ code: 404, msg: '订单不存在' });
    }

    // 格式化订单数据
    const formattedOrder = {
      ...order.toJSON(),
      totalAmount: parseFloat(order.totalAmount) || 0,
      OrderItems: order.OrderItems ? order.OrderItems.map(item => ({
        ...item.toJSON(),
        price: parseFloat(item.price) || 0,
        totalPrice: parseFloat(item.totalPrice) || 0,
        Good: item.Good ? {
          ...item.Good.toJSON(),
          price: parseFloat(item.Good.price) || 0
        } : null
      })) : []
    };

    res.json({
      code: 200,
      msg: '获取订单详情成功',
      data: formattedOrder
    });
  } catch (error) {
    console.error('获取订单详情失败:', error);
    console.error('错误详情:', error.message);
    console.error('错误堆栈:', error.stack);
    res.status(500).json({ 
      code: 500, 
      msg: '服务器错误',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
});

// 更新订单状态
router.put('/orders/:id/status', auth, async (req, res) => {
  try {
    const orderId = req.params.id;
    const { status } = req.body;

    // 验证订单是否存在且属于当前用户
    const order = await Order.findOne({
      where: { 
        id: orderId,
        userId: req.user.id
      }
    });

    if (!order) {
      return res.status(404).json({ code: 404, msg: '订单不存在' });
    }

    // 验证状态转换是否合法
    const validTransitions = {
      'pending': ['paid', 'cancelled'], // 待支付 -> 已支付/已取消
      'paid': ['received'], // 已支付 -> 已收货
      'received': [], // 已收货是最终状态
      'cancelled': [] // 已取消是最终状态
    };

    if (validTransitions[order.status] && !validTransitions[order.status].includes(status)) {
      return res.status(400).json({ 
        code: 400, 
        msg: `无法从 ${order.status} 状态转换到 ${status} 状态` 
      });
    }

    // 更新订单状态
    const [updated] = await Order.update(
      { status }, 
      { where: { id: orderId } }
    );

    if (updated === 0) {
      return res.status(404).json({ code: 404, msg: '订单不存在' });
    }

    res.json({ code: 200, msg: '订单状态更新成功' });
  } catch (error) {
    console.error('更新订单状态失败:', error);
    console.error('错误详情:', error.message);
    console.error('错误堆栈:', error.stack);
    res.status(500).json({ 
      code: 500, 
      msg: '服务器错误',
      error: process.env.NODE_ENV === 'development' ? error.message : undefined
    });
  }
});

// 批量删除购物车商品
router.post('/cart/batch-delete', auth, async (req, res) => {
  try {
    const { ids } = req.body;

    if (!ids || !Array.isArray(ids) || ids.length === 0) {
      return res.status(400).json({ code: 400, msg: '商品ID列表不能为空' });
    }

    const deleted = await Cart.destroy({
      where: {
        id: ids,
        userId: req.user.id
      }
    });

    res.json({
      code: 200,
      msg: `成功删除 ${deleted} 个商品`,
      data: { deletedCount: deleted }
    });
  } catch (error) {
    console.error('批量删除失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

module.exports = router;

(前端开发)

购物车页面与下单流程

<template>
  <div class="cart-page">
    <div class="cart-container">
      <!-- 标题 -->
      <div class="page-title">我的购物车</div>
      <!-- 购物车列表 -->
      <div class="cart-list" v-if="cartList.length">
        <div class="cart-item" v-for="item in cartList" :key="item.id">
          <div class="item-checkbox">
            <el-checkbox v-model="item.checked" @change="calcTotalPrice"></el-checkbox>
          </div>
          <div class="item-info">
            <div class="goods-name">{{ item.goodsName }}</div>
            <div class="goods-price">¥{{ item.price.toFixed(2) }}</div>
          </div>
          <div class="item-count">
            <button class="count-btn" @click="handleMinus(item)" :disabled="item.num <= 1">-</button>
            <span class="count-num">{{ item.num }}</span>
            <button class="count-btn" @click="handlePlus(item)">+</button>
          </div>
          <div class="item-actions">
            <span @click="handleDelete(item.id)">删除</span>
          </div>
        </div>
      </div>
      <!-- 空状态 -->
      <div class="empty-state" v-else>
        <div class="empty-icon">🛒</div>
        <div class="empty-text">购物车空空如也,快去逛逛吧~</div>
        <button class="go-shop-btn" @click="$router.push('/user')">去首页购物</button>
      </div>
      <!-- 底部结算栏 -->
      <div class="cart-footer" v-if="cartList.length">
        <div class="footer-left">
          <el-checkbox v-model="allChecked" @change="handleAllCheck">全选</el-checkbox>
        </div>
        <div class="footer-middle">
          <span class="total-text">合计:</span>
          <span class="total-price">¥{{ totalPrice.toFixed(2) }}</span>
        </div>
        <div class="footer-right">
          <button class="checkout-btn" @click="handleCheckout">结算</button>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, computed, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox, ElCheckbox } from 'element-plus';
import request from '../../utils/request';

const router = useRouter();
const cartList = ref([]);
const totalPrice = ref(0);
const token = localStorage.getItem('token');

// 全选状态
const allChecked = computed({
  get() {
    return cartList.value.every(item => item.checked) && cartList.value.length > 0;
  },
  set(val) {
    cartList.value.forEach(item => {
      item.checked = val;
    });
    calcTotalPrice();
  }
});

// 获取购物车列表(后端接口:/cart/list)
const getCartList = async () => {
  try {
    const res = await request({
      url: '/cart/list',
      method: 'GET'
    });
    if (res && res.code === 200) {
      // 后端已经格式化数据,直接使用
      cartList.value = res.data;
      calcTotalPrice();
    }
  } catch (error) {
    ElMessage.error('获取购物车失败');
  }
};

onMounted(() => {
  if (!token) {
    ElMessage.warning('请先登录');
    router.push('/user/login');
    return;
  }
  getCartList();
});

// 计算总价
const calcTotalPrice = () => {
  totalPrice.value = cartList.value
    .filter(item => item.checked)
    .reduce((sum, item) => sum + item.price * item.num, 0);
};

// 数量减(后端接口:/cart/update)
const handleMinus = async (item) => {
  try {
    const res = await request({
      url: '/cart/update',
      method: 'PUT',
      data: { id: item.id, quantity: item.num - 1 }
    });
    if (res && res.code === 200) {
      item.num -= 1;
      calcTotalPrice();
    }
  } catch (error) {
    ElMessage.error('修改数量失败');
  }
};

// 数量加(后端接口:/cart/update)
const handlePlus = async (item) => {
  try {
    const res = await request({
      url: '/cart/update',
      method: 'PUT',
      data: { id: item.id, quantity: item.num + 1 }
    });
    if (res && res.code === 200) {
      item.num += 1;
      calcTotalPrice();
    }
  } catch (error) {
    ElMessage.error('修改数量失败');
  }
};

// 删除购物车商品(后端接口:/cart/delete)
const handleDelete = async (id) => {
  try {
    await ElMessageBox.confirm('确定要删除该商品吗?', '提示', {
      type: 'warning'
    });
    const res = await request({
      url: '/cart/delete',
      method: 'POST',
      data: { id }
    });
    if (res && res.code === 200) {
      ElMessage.success('删除成功');
      getCartList(); // 刷新列表
    }
  } catch (error) {
    if (error !== 'cancel') {
      ElMessage.error('删除失败');
    }
  }
};

// 全选/取消全选
const handleAllCheck = () => {
  calcTotalPrice();
};

// 结算
const handleCheckout = () => {
  const checkedGoods = cartList.value.filter(item => item.checked);
  if (!checkedGoods.length) {
    return ElMessage.warning('请选择要结算的商品');
  }
  // 跳转到结算页,携带选中商品ID(用逗号分隔)
  router.push({
    path: '/user/confirm-order',
    query: { cartIds: checkedGoods.map(item => item.id).join(',') }
  });
};
</script>

<style scoped>
.cart-page {
  min-height: 100vh;
  background-color: #f5f5f5;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 20px;
  padding-top: 40px;
  box-sizing: border-box;
}
.cart-container {
  width: 100%;
  max-width: 800px;
  background-color: #ffffff;
  padding: 32px;
  border-radius: 12px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  min-height: 600px;
}
.page-title {
  font-size: 20px;
  font-weight: 600;
  color: #333333;
  margin-bottom: 24px;
  text-align: center;
}
.cart-list {
  flex: 1;
}
.cart-item {
  display: flex;
  align-items: center;
  padding: 16px;
  border-bottom: 1px solid #e5e7eb;
}
.item-checkbox {
  width: 50px;
  text-align: center;
}
.item-info {
  flex: 1;
}
.goods-name {
  font-size: 14px;
  color: #333;
  margin-bottom: 8px;
  line-height: 1.4;
}
.goods-price {
  font-size: 14px;
  color: #e53e3e;
  font-weight: 500;
}
.item-count {
  display: flex;
  align-items: center;
  width: 100px;
}
.count-btn {
  width: 28px;
  height: 28px;
  border: 1px solid #e5e7eb;
  background-color: #f5f5f5;
  color: #333;
  border-radius: 4px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}
.count-btn:disabled {
  background-color: #eee;
  color: #999;
  cursor: not-allowed;
}
.count-num {
  width: 40px;
  text-align: center;
  font-size: 14px;
}
.item-actions {
  width: 80px;
  text-align: center;
}
.item-actions span {
  font-size: 14px;
  color: #4299e1;
  cursor: pointer;
}
.item-actions span:hover {
  text-decoration: underline;
}
.empty-state {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: 40px 0;
}
.empty-icon {
  font-size: 64px;
  margin-bottom: 16px;
}
.empty-text {
  font-size: 16px;
  color: #999;
  margin-bottom: 24px;
}
.go-shop-btn {
  padding: 12px 24px;
  background-color: #4299e1;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.2s;
}
.go-shop-btn:hover {
  background-color: #3a86cf;
}
.cart-footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 0;
  border-top: 1px solid #e5e7eb;
  margin-top: 20px;
}
.footer-left {
  padding-left: 16px;
}
.footer-middle {
  text-align: right;
}
.total-text {
  font-size: 14px;
  color: #666;
}
.total-price {
  font-size: 18px;
  color: #e53e3e;
  font-weight: 600;
  margin-left: 8px;
}
.footer-right {
  padding-right: 16px;
}
.checkout-btn {
  padding: 10px 24px;
  background-color: #4299e1;
  color: #fff;
  border: none;
  border-radius: 6px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.2s;
}
.checkout-btn:hover {
  background-color: #3a86cf;
}
</style>

(测试)

任务:订单流程测试
成果:端到端测试通过,缓存问题已解决

  1. 本日小结与明日计划

今日总结:下单流程通畅
明日计划:骑手与管理端功能

快接近尾声了!

内容概要:本文介绍了ENVI Deep Learning V1.0的操作教程,重点讲解了如何利用ENVI软件进行深度学习模型的训练与应用,以实现遥感图像中特定目标(如集装箱)的自动提取。教程涵盖了从数据准备、标签图像创建、模型初始化与训练,到执行分类及结果优化的完整流程,并介绍了精度评价与通过ENVI Modeler实现一键化建模的方法。系统基于TensorFlow框架,采用ENVINet5(U-Net变体)架构,支持通过点、线、面ROI或分类图生成标签数据,适用于多/高光谱影像的单一类别特征提取。; 适合人群:具备遥感图像处理基础,熟悉ENVI软件操作,从事地理信息、测绘、环境监测等相关领域的技术人员或研究人员,尤其是希望将深度学习技术应用于遥感目标识别的初学者与实践者。; 使用场景及目标:①在遥感影像中自动识别和提取特定地物目标(如车辆、建筑、道路、集装箱等);②掌握ENVI环境下深度学习模型的训练流程与关键参数设置(如Patch Size、Epochs、Class Weight等);③通过模型调优与结果反馈提升分类精度,实现高效自动化信息提取。; 阅读建议:建议结合实际遥感项目边学边练,重点关注标签数据制作、模型参数配置与结果后处理环节,充分利用ENVI Modeler进行自动化建模与参数优化,同时注意软硬件环境(特别是NVIDIA GPU)的配置要求以保障训练效率。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值