SchoolDash Alpha冲刺随笔5 - Day 9

SchoolDash Alpha冲刺随笔5 - Day 9

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

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

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

  1. 本日冲刺整体进展

完成骑手订单接收、管理端订单/用户管理。
系统三大端基本功能就绪。

  1. 项目最新运行效果

骑手端演示视频

SchoolDash骑手端演示视频

管理员端演示视频

SchoolDash管理员端演示视频

骑手端页面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

管理员端页面

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 今日工作成果

(后端开发)骑手与管理员路由

骑手路由

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

// 骑手仪表板统计
router.get('/dashboard/stats', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const assigned = await Order.count({ where: { status: 'assigned', riderId: riderId } });
    const delivering = await Order.count({ where: { riderId, status: 'delivering' } });
    const completed = await Order.count({ where: { riderId, status: 'completed' } });
    res.json({
      code: 200,
      msg: '获取骑手统计成功',
      data: {
        assignedOrders: assigned,
        deliveringOrders: delivering,
        completedOrders: completed
      }
    });
  } catch (error) {
    console.error('获取骑手统计失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 获取待接单订单(status = 'paid',尚未分配骑手)
router.get('/orders/pending', auth, checkRole(['rider']), async (req, res) => {
  try {
    const orders = await Order.findAll({
      where: { status: 'paid', riderId: null },
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ],
      order: [['createdAt', 'ASC']]
    });

    // 格式化订单数据,确保价格是数字类型
    const formattedOrders = orders.map(order => ({
      ...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: formattedOrders });
  } catch (error) {
    console.error('获取待接单订单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手接单(将订单状态从 'paid' 改为 'assigned',并设置骑手ID)
router.post('/orders/:id/accept', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orderId = req.params.id;

    // 验证订单是否存在且状态为'paid'且未分配骑手
    const order = await Order.findOne({
      where: {
        id: orderId,
        status: 'paid',
        riderId: null
      }
    });

    if (!order) {
      return res.status(400).json({ code: 400, msg: '订单不存在或已被接单' });
    }

    // 更新订单状态为已分配并设置骑手ID
    await order.update({ status: 'assigned', riderId: riderId });

    // 获取更新后的订单详情
    const updatedOrder = await Order.findByPk(orderId, {
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ]
    });

    res.json({ code: 200, msg: '接单成功', data: updatedOrder });
  } catch (error) {
    console.error('接单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 获取已分配订单(status = 'assigned',已分配给当前骑手)
router.get('/orders/assigned', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orders = await Order.findAll({
      where: { status: 'assigned', riderId: riderId },
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ],
      order: [['createdAt', 'ASC']]
    });

    // 格式化订单数据,确保价格是数字类型
    const formattedOrders = orders.map(order => ({
      ...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: formattedOrders });
  } catch (error) {
    console.error('获取已分配订单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手确认配送(将订单状态从 'assigned' 改为 'delivering')
router.post('/orders/:id/confirm', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orderId = req.params.id;

    // 验证订单是否分配给当前骑手
    const order = await Order.findOne({
      where: {
        id: orderId,
        riderId: riderId,
        status: 'assigned'
      }
    });

    if (!order) {
      return res.status(400).json({ code: 400, msg: '订单不存在或状态不正确' });
    }

    // 更新订单状态为配送中
    await order.update({ status: 'delivering' });

    // 获取更新后的订单详情
    const updatedOrder = await Order.findByPk(orderId, {
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ]
    });

    res.json({ code: 200, msg: '确认配送成功', data: updatedOrder });
  } catch (error) {
    console.error('确认配送失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手查看自己的配送中订单
router.get('/orders/delivering', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orders = await Order.findAll({
      where: { riderId, status: 'delivering' },
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ],
      order: [['updatedAt', 'ASC']]
    });

    // 格式化订单数据,确保价格是数字类型
    const formattedOrders = orders.map(order => ({
      ...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: formattedOrders });
  } catch (error) {
    console.error('获取配送中订单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手完成订单
router.post('/orders/:id/complete', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orderId = req.params.id;

    const [updated] = await Order.update({
      status: 'completed'
    }, {
      where: {
        id: orderId,
        riderId: riderId,
        status: 'delivering'
      }
    });

    if (updated === 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: '服务器错误' });
  }
});

// 骑手查看已完成订单
router.get('/orders/completed', auth, checkRole(['rider']), async (req, res) => {
  try {
    console.log('骑手已完成订单API被调用,用户:', req.user ? req.user.username : '未认证');
    console.log('请求参数:', req.query);
    
    const riderId = req.user.id;
    const { dateFilter, orderNo } = req.query;
    
    // 构建查询条件
    let whereCondition = { riderId, status: 'completed' };
    
    // 如果有订单号筛选条件
    if (orderNo) {
      whereCondition.orderNumber = { [require('sequelize').Op.like]: `%${orderNo}%` };
    }
    
    // 如果有时间筛选条件
    if (dateFilter && dateFilter !== 'all') {
      const today = new Date();
      today.setHours(0, 0, 0, 0);
      const tomorrow = new Date(today);
      tomorrow.setDate(tomorrow.getDate() + 1);
      
      let startDate, endDate;
      
      switch (dateFilter) {
        case 'today':
          startDate = today;
          endDate = tomorrow;
          break;
        case 'yesterday':
          startDate = new Date(today);
          startDate.setDate(startDate.getDate() - 1);
          endDate = today;
          break;
        case '7days':
          startDate = new Date(today);
          startDate.setDate(startDate.getDate() - 7);
          endDate = tomorrow;
          break;
        case '30days':
          startDate = new Date(today);
          startDate.setDate(startDate.getDate() - 30);
          endDate = tomorrow;
          break;
      }
      
      if (startDate && endDate) {
        whereCondition.updatedAt = {
          [require('sequelize').Op.between]: [startDate, endDate]
        };
      }
    }
    
    console.log('查询条件:', whereCondition);
    
    const orders = await Order.findAll({
      where: whereCondition,
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          model: OrderItem, 
          include: [{ 
            model: Goods, 
            as: 'Good',
            attributes: ['id', 'name', 'price'] 
          }] 
        }
      ],
      order: [['updatedAt', 'DESC']]
    });

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

    // 格式化订单数据,确保价格是数字类型
    const formattedOrders = orders.map(order => ({
      ...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
      })) : []
    }));

    console.log('返回骑手已完成订单数据成功');
    res.json({ code: 200, msg: '获取已完成订单成功', data: formattedOrders });
  } catch (error) {
    console.error('获取已完成订单失败:', error);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手获取订单详情(包括待接单订单和自己的订单)
router.get('/order-detail/:id', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orderId = req.params.id;

    // 骑手可以查看所有待接单订单和已分配给自己的订单
    const order = await Order.findOne({
      where: {
        id: orderId,
        [require('sequelize').Op.or]: [
          { status: 'paid', riderId: null }, // 待接单订单
          { riderId: riderId } // 已分配给自己的订单
        ]
      },
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          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);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

// 骑手获取订单详情
router.get('/orders/:id', auth, checkRole(['rider']), async (req, res) => {
  try {
    const riderId = req.user.id;
    const orderId = req.params.id;

    const order = await Order.findOne({
      where: {
        id: orderId,
        riderId: riderId
      },
      include: [
        { model: require('../models/User'), as: 'user', attributes: ['username', 'phone'] },
        { model: require('../models/Address'), as: 'address' },
        { 
          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);
    res.status(500).json({ code: 500, msg: '服务器错误' });
  }
});

module.exports = router;

管理员路由

const express = require('express');
const router = express.Router();
const { auth, checkRole } = require('../../middleware/auth');

// 挂载子路由(商品/分类/用户/订单)
router.use('/goods', require('./goodsRoutes'));
router.use('/category', require('./categoryRoutes'));
router.use('/users', require('./userRoutes'));
router.use('/order', require('./orderRoutes')); // Changed from /orders to /order to match frontend

// 管理员仪表板统计
router.get('/dashboard/stats', auth, checkRole(['admin']), async (req, res) => {
  try {
    const Sequelize = require('sequelize');
    const { Op } = require('sequelize');
    const User = require('../../models/User');
    const Goods = require('../../models/Goods');
    const Category = require('../../models/Category');
    const Order = require('../../models/Order');

    // 获取商品、订单等统计数据
    const [goodsCount] = await Goods.findAll({
      attributes: [[Sequelize.fn('COUNT', Sequelize.col('id')), 'count']]
    });

    // 获取订单总数
    const [orderCount] = await Order.findAll({
      attributes: [[Sequelize.fn('COUNT', Sequelize.col('id')), 'count']]
    });

    // 获取今日订单数
    const today = new Date();
    today.setHours(0, 0, 0, 0);
    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);

    const [todayOrderCount] = await Order.findAll({
      where: {
        createdAt: {
          [Op.between]: [today, tomorrow]
        }
      },
      attributes: [[Sequelize.fn('COUNT', Sequelize.col('id')), 'count']]
    });

    // 获取总销售额(已支付订单)
    const [totalSalesResult] = await Order.findAll({
      where: {
        status: 'paid'
      },
      attributes: [[Sequelize.fn('SUM', Sequelize.col('totalAmount')), 'total']]
    });

    const totalSales = totalSalesResult.dataValues.total || 0;

    // 获取各状态订单数量
    const statusCounts = await Order.findAll({
      attributes: [
        'status',
        [Sequelize.fn('COUNT', Sequelize.col('id')), 'count']
      ],
      group: ['status'],
      raw: true
    });

    // 构建订单状态分布数据
    const statusMap = {
      'pending': { label: '待支付', count: 0 },
      'paid': { label: '已支付', count: 0 },
      'assigned': { label: '已派单', count: 0 },
      'delivering': { label: '配送中', count: 0 },
      'completed': { label: '已完成', count: 0 },
      'cancelled': { label: '已取消', count: 0 }
    };

    statusCounts.forEach(item => {
      if (statusMap[item.status]) {
        statusMap[item.status].count = parseInt(item.count);
      }
    });

    // 计算百分比
    const total = Object.values(statusMap).reduce((sum, item) => sum + item.count, 0);
    const orderStatusData = Object.entries(statusMap).map(([status, data]) => ({
      status,
      label: data.label,
      count: data.count,
      rate: total > 0 ? Math.round((data.count / total) * 100) : 0
    }));

    res.json({
      code: 200,
      msg: '获取统计数据成功',
      data: {
        stats: {
          goodsCount: goodsCount.dataValues.count,
          orderCount: orderCount.dataValues.count,
          todayOrderCount: todayOrderCount.dataValues.count,
          totalSales: parseFloat(totalSales)
        },
        orderStatus: orderStatusData
      }
    });

(前端开发)骑手与管理端页面布局

骑手端页面布局

<template>
  <div class="rider-layout">
    <!-- 侧边栏 -->
    <div class="sidebar">
      <div class="sidebar-header">
        <div class="logo-text">School Dash 骑手端</div>
      </div>
      <div class="sidebar-menu">
        <div 
          class="menu-item" 
          :class="{ active: $route.path === '/rider/dashboard' }"
          @click="$router.push('/rider/dashboard')"
        >
          📊 订单概览
        </div>
        <div 
          class="menu-item" 
          :class="{ active: $route.path === '/rider/order-list' }"
          @click="$router.push('/rider/order-list')"
        >
          📦 待接单
        </div>
        <div
          class="menu-item"
          :class="{ active: $route.path === '/rider/accepted-orders' }"
          @click="$router.push('/rider/accepted-orders')"
        >
          🚴 配送中
        </div>
        <div
          class="menu-item"
          :class="{ active: $route.path === '/rider/completed-orders' }"
          @click="$router.push('/rider/completed-orders')"
        >
          ✅ 已完成
        </div>
        <div class="menu-item" @click="handleLogout">
          🚪 退出登录
        </div>
      </div>
    </div>

    <!-- 主内容区 -->
    <div class="main-content">
      <!-- 顶部导航 -->
      <div class="content-header">
        <div class="header-title">{{ pageTitle }}</div>
        <div class="rider-info">
          骑手:{{ riderName }}
        </div>
      </div>
      <!-- 页面内容 -->
      <div class="content-body">
        <router-view />
      </div>
    </div>
  </div>
</template>

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

const router = useRouter();
const route = useRoute();
const riderName = ref(localStorage.getItem('riderName') || '骑手');
const pageTitle = ref('');

// 页面标题映射
const titleMap = {
  '/rider/dashboard': '订单概览',
  '/rider/order-list': '待接单',
  '/rider/accepted-orders': '配送中',
  '/rider/completed-orders': '已完成'
};

// 初始化
onMounted(() => {
  // 校验登录状态
  if (!localStorage.getItem('riderToken')) {
    router.push('/rider/login');
    return;
  }
  // 更新页面标题
  updatePageTitle();
  // 监听路由变化
  router.afterEach(() => updatePageTitle());
});

// 更新页面标题
const updatePageTitle = () => {
  pageTitle.value = titleMap[route.path] || '骑手端';
};

// 退出登录
const handleLogout = () => {
  localStorage.removeItem('riderToken');
  localStorage.removeItem('riderName');
  ElMessage.success('退出登录成功');
  router.push('/rider/login');
};
</script>

<style scoped>
.rider-layout {
  display: flex;
  height: 100vh;
  background-color: #f5f5f5;
}

/* 侧边栏 */
.sidebar {
  width: 220px;
  background-color: #4299e1;
  color: #fff;
  display: flex;
  flex-direction: column;
}

.sidebar-header {
  padding: 20px;
  border-bottom: 1px solid #3a86cf;
}

.logo-text {
  font-size: 16px;
  font-weight: 600;
  text-align: center;
}

.sidebar-menu {
  padding-top: 20px;
  flex: 1;
}

.menu-item {
  padding: 14px 20px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.menu-item:hover {
  background-color: #3a86cf;
}

.menu-item.active {
  background-color: #3182ce;
  border-left: 4px solid #fff;
}

/* 主内容区 */
.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: auto;
}

.content-header {
  padding: 0 24px;
  height: 60px;
  background-color: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}

.rider-info {
  font-size: 14px;
  color: #666;
}

.content-body {
  flex: 1;
  padding: 24px;
}

/* 响应式适配 */
@media (max-width: 767px) {
  .sidebar {
    width: 60px;
  }
  .logo-text, .menu-item span {
    display: none;
  }
  .menu-item {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 14px 0;
  }
  .content-header {
    padding: 0 16px;
  }
  .content-body {
    padding: 16px;
  }
}
</style>

管理员端页面布局


  '/admin/dashboard': '数据概览',
  '/admin/goods-manage': '商品管理',
  '/admin/order-manage': '订单管理'
};

// 初始化
onMounted(() => {
  // 校验登录状态
  if (!localStorage.getItem('adminToken')) {
    router.push('/admin/login');
    return;
  }
  // 更新页面标题
  updatePageTitle();
  // 监听路由变化
  router.afterEach(() => updatePageTitle());
});

// 更新页面标题
const updatePageTitle = () => {
  pageTitle.value = titleMap[route.path] || '管理后台';
};

// 退出登录
const handleLogout = () => {
  localStorage.removeItem('adminToken');
  localStorage.removeItem('adminName');
  ElMessage.success('退出登录成功');
  router.push('/admin/login');
};
</script>

<style scoped>
.admin-layout {
  display: flex;
  height: 100vh;
  background-color: #f5f5f5;
}

/* 侧边栏 */
.sidebar {
  width: 220px;
  background-color: #4299e1;
  color: #fff;
  display: flex;
  flex-direction: column;
}

.sidebar-header {
  padding: 20px;
  border-bottom: 1px solid #3a86cf;
}

.logo-text {
  font-size: 16px;
  font-weight: 600;
  text-align: center;
}

.sidebar-menu {
  padding-top: 20px;
  flex: 1;
}

.menu-item {
  padding: 14px 20px;
  font-size: 14px;
  cursor: pointer;
  transition: background-color 0.2s;
}

.menu-item:hover {
  background-color: #3a86cf;
}

.menu-item.active {
  background-color: #3182ce;
  border-left: 4px solid #fff;
}

/* 主内容区 */
.main-content {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: auto;
}

.content-header {
  padding: 0 24px;
  height: 60px;
  background-color: #fff;
  box-shadow: 0 1px 2px rgba(0,0,0,0.08);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.header-title {
  font-size: 18px;
  font-weight: 600;
  color: #333;
}

.user-info {
  font-size: 14px;
  color: #666;
}

.content-body {
  flex: 1;
  padding: 24px;
}
</style>

(测试)

任务:全系统集成测试
成果:覆盖主要流程,发现并修复2个小bug

  1. 本日小结与明日计划

今日总结:Alpha版本基本完成
存在问题:少量优化点
明日计划:收尾与总结

SchoolDash Alpha冲刺圆满完成!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值