SchoolDash Alpha冲刺随笔4 - Day 7
课程与作业信息
所属课程:软件工程实践
作业要求来源:第五次作业——Alpha冲刺
- 项目燃尽图(Burn-up Chart)
当前冲刺总Story Point:50 SP(已完成46 SP,剩余4 SP)

- 本日冲刺整体进展
完成购物车管理与订单创建。
前端添加/更新/删除购物车项,后端同步订单表。
- 项目最新运行效果
视频演示
SchoolDash商品添加购物车及结算演示视频
购物车页面图片

- 成员今日工作成果
(后端开发)
购物车
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>
(测试)
任务:订单流程测试
成果:端到端测试通过,缓存问题已解决
- 本日小结与明日计划
今日总结:下单流程通畅
明日计划:骑手与管理端功能
快接近尾声了!
2776

被折叠的 条评论
为什么被折叠?



