当前位置: 首页 > article >正文

Java+SpringBoot的校园餐厅在线点餐管理系统 技术:Java、SpringBoot、MyBatis、HTML、Vue.js、MySQL、Echarts

JavaSpringBoot的校园餐厅在线点餐管理系统技术Java、SpringBoot、MyBatis、HTML、Vue.js、MySQL、Echarts系统分用户和管理员2个角色用户模块登录注册餐品搜索加入餐车订单支付餐品评价餐厅公告留言交流个人信息等管理员模块用户管理评价管理餐品管理公告管理留言管理订单管理销售报表个人信息等系统前后端不分离主要实现业务流程注册-登录-点餐-餐车-结算-配送-收货-评价-完成源码数据库万字文档开发工具另赠E-R图用例图功能图等完整的系统前后端不分离、万字文档、数据库等直接在这里生成几万行完整代码和文档是不现实的受限于输出长度限制。数据库设计 (MySQL)核心表的DDL语句。后端核心代码 (SpringBoot MyBatis)实体类、Mapper、Service、Controller的关键实现。前端核心页面 (HTML Vue.js)在不分离架构下如何在Thymeleaf中嵌入Vue进行点餐和购物车操作。业务逻辑流程从点餐到评价的代码逻辑演示。你可以基于这个骨架快速扩展出完整的系统。一、数据库设计 (MySQL)这是系统的基石对应你的E-R图。– 创建数据库CREATE DATABASE IF NOT EXISTS campus_canteen DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;USE campus_canteen;– 1. 用户表 (包含管理员和普通用户)CREATE TABLE user (id bigint(20) NOT NULL AUTO_INCREMENT,username varchar(50) NOT NULL COMMENT ‘账号’,password varchar(100) NOT NULL COMMENT ‘加密密码’,role tinyint(4) NOT NULL DEFAULT 1 COMMENT ‘1:普通用户 2:管理员’,nickname varchar(50) DEFAULT NULL,phone varchar(20) DEFAULT NULL,avatar varchar(255) DEFAULT NULL,create_time datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (id),UNIQUE KEY uk_username (username)) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT‘用户表’;– 2. 餐品分类表CREATE TABLE category (id bigint(20) NOT NULL AUTO_INCREMENT,name varchar(50) NOT NULL,sort int(11) DEFAULT 0,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;– 3. 餐品表CREATE TABLE dish (id bigint(20) NOT NULL AUTO_INCREMENT,category_id bigint(20) DEFAULT NULL,name varchar(100) NOT NULL,price decimal(10,2) NOT NULL,image varchar(255) DEFAULT NULL,description text,status tinyint(4) DEFAULT 1 COMMENT ‘1:在售 0:下架’,sales int(11) DEFAULT 0 COMMENT ‘销量’,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;– 4. 订单表CREATE TABLE orders (id bigint(20) NOT NULL AUTO_INCREMENT,order_no varchar(64) NOT NULL COMMENT ‘订单号’,user_id bigint(20) NOT NULL,total_amount decimal(10,2) NOT NULL,status tinyint(4) NOT NULL DEFAULT 0 COMMENT ‘0:待支付 1:待配送 2:配送中 3:已完成 4:已取消’,address varchar(255) DEFAULT NULL,remark varchar(255) DEFAULT NULL,create_time datetime DEFAULT CURRENT_TIMESTAMP,pay_time datetime DEFAULT NULL,finish_time datetime DEFAULT NULL,PRIMARY KEY (id),UNIQUE KEY uk_order_no (order_no)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;– 5. 订单详情表CREATE TABLE order_item (id bigint(20) NOT NULL AUTO_INCREMENT,order_id bigint(20) NOT NULL,dish_id bigint(20) NOT NULL,dish_name varchar(100) NOT NULL,price decimal(10,2) NOT NULL,quantity int(11) NOT NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;– 6. 评价表CREATE TABLE comment (id bigint(20) NOT NULL AUTO_INCREMENT,order_id bigint(20) NOT NULL,user_id bigint(20) NOT NULL,dish_id bigint(20) NOT NULL,content text,rating tinyint(4) NOT NULL COMMENT ‘1-5星’,create_time datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;– 7. 公告表CREATE TABLE notice (id bigint(20) NOT NULL AUTO_INCREMENT,title varchar(100) NOT NULL,content text,publish_time datetime DEFAULT CURRENT_TIMESTAMP,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETutf8mb4;二、后端核心代码 (SpringBoot MyBatis)依赖配置 (pom.xml 片段)确保引入了 mybatis-spring-boot-starter, lombok, thymeleaf (前后端不分离必备), mysql-connector-java。实体类 (Entity) - 以 Order 为例DataTableName(“orders”)public class Order {TableId(type IdType.AUTO)private Long id;private String orderNo;private Long userId;private BigDecimal totalAmount;private Integer status; // 0:待支付, 1:待配送…private String address;private Date createTime;// 关联查询用非数据库字段TableField(exist false)private List items;TableField(exist false)private User user;}Mapper 接口 (MyBatis)Mapperpublic interface OrderMapper extends BaseMapper {// 自定义复杂查询例如查看销售报表Select(“SELECT DATE(create_time) as date, SUM(total_amount) as total FROM orders WHERE status 1 GROUP BY DATE(create_time)”)List selectSalesReport();// 关联查询订单详情 Select(SELECT * FROM orders WHERE id #{id}) Results(id orderMap, value { Result(property items, column id, javaType List.class, many Many(select com.canteen.mapper.OrderItemMapper.selectByOrderId)) }) Order selectDetailById(Long id);}Service 层 - 核心业务流程 (下单)这是最关键的业务逻辑事务控制、库存扣减如果有、生成订单。Servicepublic class OrderService {Autowired private OrderMapper orderMapper; Autowired private OrderItemMapper orderItemMapper; Autowired private DishMapper dishMapper; Transactional(rollbackFor Exception.class) public String createOrder(Long userId, List cartItems, String address) { // 1. 生成订单号 String orderNo ORD System.currentTimeMillis() new Random().nextInt(1000); // 2. 计算总价并构建订单对象 BigDecimal total BigDecimal.ZERO; Order order new Order(); order.setOrderNo(orderNo); order.setUserId(userId); order.setAddress(address); order.setStatus(0); // 待支付 for (CartItem item : cartItems) { Dish dish dishMapper.selectById(item.getDishId()); if (dish null || dish.getStatus() 0) { throw new RuntimeException(餐品 item.getDishName() 已售罄或下架); } total total.add(dish.getPrice().multiply(new BigDecimal(item.getQuantity()))); } order.setTotalAmount(total); // 3. 插入订单主表 orderMapper.insert(order); // 4. 插入订单详情表 更新销量 for (CartItem item : cartItems) { OrderItem orderItem new OrderItem(); orderItem.setOrderId(order.getId()); orderItem.setDishId(item.getDishId()); orderItem.setDishName(item.getDishName()); orderItem.setPrice(item.getPrice()); orderItem.setQuantity(item.getQuantity()); orderItemMapper.insert(orderItem); // 更新销量 Dish dish dishMapper.selectById(item.getDishId()); dish.setSales(dish.getSales() item.getQuantity()); dishMapper.updateById(dish); } return orderNo; } // 模拟支付成功回调 public void paySuccess(String orderNo) { Order order orderMapper.selectOne(new QueryWrapper().eq(order_no, orderNo)); if (order ! null order.getStatus() 0) { order.setStatus(1); // 变为待配送 order.setPayTime(new Date()); orderMapper.updateById(order); } }}Controller 层ControllerRequestMapping(“/order”)public class OrderController {Autowired private OrderService orderService; // 跳转到确认订单页 (前后端不分离返回Thymeleaf页面) GetMapping(/confirm) public String confirmPage(Model model, HttpSession session) { User user (User) session.getAttribute(currentUser); if (user null) return redirect:/login; // 从Session或Redis获取购物车数据 List cart (List) session.getAttribute(cart); model.addAttribute(cart, cart); return order_confirm; } // 提交订单 PostMapping(/submit) ResponseBody // 返回JSON给前端Vue处理或者返回重定向字符串 public Result submit(RequestBody OrderSubmitDTO dto, HttpSession session) { try { User user (User) session.getAttribute(currentUser); String orderNo orderService.createOrder(user.getId(), dto.getItems(), dto.getAddress()); return Result.success(orderNo); } catch (Exception e) { return Result.error(e.getMessage()); } } // 管理员查看报表数据 (供Echarts使用) GetMapping(/admin/sales-data) ResponseBody public List getSalesData() { return orderService.getSalesReport(); }}三、前端核心实现 (HTML Vue.js Thymeleaf)由于是前后端不分离我们使用 Thymeleaf 作为模板引擎但在页面内部引入 Vue.js 来处理动态交互如购物车、搜索、图表。菜单点餐页 (menu.html)校园餐厅 - 在线点餐 .dish-card { border: 1px solid #ddd; padding: 10px; margin: 10px; display: inline-block; width: 200px; } .cart-fixed { position: fixed; bottom: 0; right: 0; background: #ff6b6b; color: white; padding: 20px; } {{ dish.name }} 价格: ¥{{ dish.price }} 销量: {{ dish.sales }} 加入购物车 0 共 {{ totalCount }} 件, 总计: ¥{{ totalPrice }} 去结算 // 后端通过 Thymeleaf 将数据注入到 JS 变量中 var initialDishes /**/ []; new Vue({ el: #app, data: { dishes: initialDishes, searchKey: , cart: [] // 购物车数组 }, computed: { filteredDishes() { if (!this.searchKey) return this.dishes; return this.dishes.filter(d d.name.includes(this.searchKey)); }, totalCount() { return this.cart.reduce((sum, item) sum item.quantity, 0); }, totalPrice() { return this.cart.reduce((sum, item) sum (item.price * item.quantity), 0).toFixed(2); } }, methods: { addToCart(dish) { let item this.cart.find(c c.id dish.id); if (item) { item.quantity; } else { this.cart.push({...dish, quantity: 1}); } // 实际项目中这里应该调用Ajax更新后端Session中的购物车或者暂时存在本地 }, goToSettle() { // 将购物车数据暂存跳转结算页 // 简单做法存入Session需要Ajax请求后端或者表单提交 alert(跳转结算页...); window.location.href /order/confirm; }, filterDishes() { // 搜索逻辑由 computed 属性自动处理 } } });管理员报表页 (admin_report.html) - 集成 Echarts销售统计报表 var salesData /**/ []; // 后端传入的 List var myChart echarts.init(document.getElementById(mainChart)); // 处理数据格式适配 Echarts var dates salesData.map(item item.date); var totals salesData.map(item item.total); var option { title: { text: 每日销售额趋势 }, tooltip: {}, xAxis: { data: dates }, yAxis: {}, series: [{ name: 销售额, type: bar, data: totals, itemStyle: { color: #5470C6 } }] }; myChart.setOption(option);四、如何完成“万字文档”与“图表”的建议绪论背景校园食堂排队久、效率低、意义数字化管理、国内外现状。相关技术介绍Java/SpringBoot解释IOC、AOP、自动配置。Vue.js解释双向绑定、组件化即使不分离局部使用也是组件化思想。MyBatisORM映射优势。MySQL/Echarts数据存储与可视化。系统分析用例图 (Use Case)画两个Actor用户、管理员连线到各自的功能登录、点餐、管理等工具推荐ProcessOn, StarUML。业务流程图注册-登录-浏览-加购-下单-支付-配送-评价。系统设计E-R图根据第一部分的SQL画出实体用户、订单、餐品及其关系1对多多对多。数据库设计列出上述表格结构。功能模块设计详细描述每个模块的输入输出。系统实现截图运行系统截取登录页、菜单页、购物车、后台管理页、报表页。核心代码展示复制上面的Service层下单逻辑、Vue的购物车逻辑。系统测试编写测试用例例如库存不足时能否下单未登录能否访问个人中心。总结与展望。五、开发工具推荐IDE: IntelliJ IDEA (后端), VS Code (前端编辑虽然不分离但写JS方便)。数据库管理: Navicat Premium 或 DBeaver。接口测试: Postman (如果需要测试纯JSON接口)。绘图工具: ProcessOn (在线画图适合画E-R图、流程图、用例图)。构建工具: Maven。六、下一步操作指南初始化项目使用 Spring Initializr 创建一个包含 Web, MyBatis, MySQL, Lombok, Thymeleaf 的项目。建库建表执行第一部分提供的 SQL 语句。配置连接在 application.yml 中配置数据库账号密码。复制代码将 Entity, Mapper, Service, Controller 代码填入对应包结构。编写页面在 src/main/resources/templates 下创建 HTML 文件技术栈JavaSpringBootMyBatisHTMLVue.js我将为你实现这个页面的完整代码。这个页面将包含菜品信息展示区图片、名称、价格、促销、描述。购买操作区数量选择器、加入购物车按钮。菜品评价区展示用户对该菜品的评价列表。我们将采用前后端不分离的模式使用 Thymeleaf 作为模板引擎来渲染页面并在页面中嵌入 Vue.js 来处理动态交互如数量加减。一、后端代码 (SpringBoot)实体类 (Entity)我们需要两个新的实体类Dish (餐品) 和 Comment (评价)。// Dish.javaDataTableName(“dish”)public class Dish {TableId(type IdType.AUTO)private Long id;private Long categoryId;private String name;private BigDecimal price;private String image;private String description;private Integer status;private Integer sales;// 关联字段非数据库列 TableField(exist false) private String categoryName; TableField(exist false) private List comments;}// Comment.javaDataTableName(“comment”)public class Comment {TableId(type IdType.AUTO)private Long id;private Long orderId;private Long userId;private Long dishId;private String content;private Integer rating; // 1-5星private Date createTime;// 关联字段用于显示用户名 TableField(exist false) private String username;}Mapper 接口// DishMapper.javaMapperpublic interface DishMapper extends BaseMapper {// 查询菜品详情并关联查询其所有评价Select(“SELECT d.*, c.name as category_name FROM dish d LEFT JOIN category c ON d.category_id c.id WHERE d.id #{id}”)Results(id “dishMap”, value {Result(property “categoryName”, column “category_name”),Result(property “comments”, column “id”, javaType List.class,many Many(select “com.canteen.mapper.CommentMapper.selectByDishId”))})Dish selectDetailById(Long id);}// CommentMapper.javaMapperpublic interface CommentMapper extends BaseMapper {// 根据菜品ID查询评价并关联查询用户名Select(“SELECT c.*, u.username FROM comment c LEFT JOIN user u ON c.user_id u.id WHERE c.dish_id #{dishId} ORDER BY c.create_time DESC”)List selectByDishId(Long dishId);}Service 层Servicepublic class DishService {Autowiredprivate DishMapper dishMapper;public Dish getDishDetail(Long id) { return dishMapper.selectDetailById(id); }}Controller 层这是处理请求的核心它从数据库获取数据然后传递给 Thymeleaf 模板进行渲染。ControllerRequestMapping(“/dish”)public class DishController {Autowired private DishService dishService; /** 访问菜品详情页 URL: /dish/detail?id1 */ GetMapping(/detail) public String detailPage(RequestParam Long id, Model model) { // 1. 从服务层获取菜品详细信息包含评价列表 Dish dish dishService.getDishDetail(id); if (dish null) { // 如果菜品不存在可以跳转到错误页或首页 return redirect:/; } // 2. 将数据放入ModelThymeleaf会自动将其注入到HTML中 model.addAttribute(dish, dish); // 3. 返回视图名称对应 templates/dish_detail.html return dish_detail; }}二、前端代码 (HTML Vue.js Thymeleaf)创建文件 src/main/resources/templates/dish_detail.html。菜品详情 body { font-family: Microsoft YaHei, sans-serif; background-color: #f5f5f5; margin: 0; padding: 20px; } .container { max-width: 900px; margin: 0 auto; background: white; padding: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } /* 菜品信息区域样式 */ .dish-info { display: flex; border-bottom: 1px solid #eee; padding-bottom: 20px; } .dish-image img { width: 300px; height: 300px; object-fit: cover; border-radius: 8px; } .dish-details { margin-left: 30px; flex: 1; } .dish-title { font-size: 24px; color: #333; margin-bottom: 10px; } .dish-price { color: #ff6b6b; font-size: 20px; font-weight: bold; } .dish-promotion { color: #ff9f43; font-size: 14px; margin: 5px 0; } .dish-desc { color: #666; line-height: 1.6; margin: 15px 0; } /* 购买操作区域样式 */ .buy-section { margin-top: 20px; } .quantity-control { display: inline-flex; align-items: center; border: 1px solid #ddd; border-radius: 4px; } .quantity-control button { background: none; border: none; padding: 5px 10px; cursor: pointer; font-size: 18px; } .quantity-control input { width: 40px; text-align: center; border: none; font-size: 16px; } .add-to-cart-btn { background-color: #4facfe; color: white; border: none; padding: 10px 20px; border-radius: 4px; cursor: pointer; font-size: 16px; margin-left: 20px; } .add-to-cart-btn:hover { background-color: #00f2fe; } /* 评价区域样式 */ .comments-section { margin-top: 30px; } .section-title { font-size: 18px; color: #333; border-left: 4px solid #4facfe; padding-left: 10px; margin-bottom: 15px; } .comment-item { border-bottom: 1px dashed #eee; padding: 15px 0; } .comment-user { font-weight: bold; color: #555; } .comment-content { color: #666; margin: 5px 0; } .comment-time { color: #999; font-size: 12px; text-align: right; } {{ dish.name }} 价格¥ {{ dish.price }} 促销{{ dish.promotion || 无 }} {{ dish.description }} 数量 - 加入餐车 菜品评价 ({{ dish.comments.length }}) {{ comment.username || 匿名用户 }} {{ comment.content }} {{ formatDate(comment.createTime) }} 暂无评价快来尝鲜吧 // 【关键步骤】使用 Thymeleaf 的 th:inlinejavascript 将后端传来的 dish 对象转换为 JS 对象 var dishData /**/ {}; new Vue({ el: #app, data: { dish: dishData, // 将后端数据赋值给 Vue 实例 quantity: 1 // 默认数量为1 }, methods: { increaseQuantity() { this.quantity; }, decreaseQuantity() { if (this.quantity 1) { this.quantity--; } }, addToCart() { // 这里需要调用后端API将商品加入购物车 // 由于是前后端不分离通常通过表单提交或Ajax alert(已将 {this.quantity} 份 {this.dish.name} 加入购物车); // 示例使用 fetch API 发送 POST 请求 /* fetch(/cart/add, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ dishId: this.dish.id, quantity: this.quantity }) }).then(response { if(response.ok) { alert(添加成功); } }); */ }, formatDate(dateString) { // 简单的日期格式化函数 const date new Date(dateString); return date.toLocaleString(zh-CN, { hour12: false }); } } });三、如何运行准备数据确保你的 dish 表和 comment 表中有测试数据。例如插入一条糖醋里脊的记录和几条相关的评价。启动项目运行你的 SpringBoot 应用。我们将继续使用 SpringBoot Thymeleaf Vue.js 的技术栈来实现这个功能。核心思路是后端提供一个接口或页面将当前用户的购物车数据通常存储在 Session 或 Redis 中传递给前端。前端使用 Vue.js 渲染列表并处理“增减数量”、“删除商品”等动态操作。“提交订单”按钮会触发一个表单提交或 Ajax 请求将购物车数据转化为订单。一、后端代码 (SpringBoot)实体类 (Entity) - 购物车项我们需要一个临时的 CartItem 类来表示购物车中的单个商品。它不是一个数据库表而是一个数据传输对象DTO。Datapublic class CartItem {private Long dishId;private String dishName;private String dishImage;private BigDecimal price; // 原价private BigDecimal discount; // 折扣率例如 0.8 表示8折private Integer quantity; // 数量// 计算小计价格 * 数量 * 折扣 public BigDecimal getSubtotal() { return this.price.multiply(new BigDecimal(this.quantity)).multiply(this.discount); }}Service 层 - 购物车服务这里我们模拟从 Session 中获取和更新购物车数据。在实际项目中你可能会使用 Redis 来存储购物车以提高性能。Servicepublic class CartService {/** 从 Session 中获取购物车 */ public List getCart(HttpSession session) { List cart (List) session.getAttribute(cart); if (cart null) { cart new ArrayList(); session.setAttribute(cart, cart); } return cart; } /** 添加商品到购物车 */ public void addToCart(HttpSession session, CartItem newItem) { List cart getCart(session); // 检查是否已存在该商品如果存在则增加数量 for (CartItem item : cart) { if (item.getDishId().equals(newItem.getDishId())) { item.setQuantity(item.getQuantity() newItem.getQuantity()); return; } } // 不存在则添加新项 cart.add(newItem); } /** 更新购物车中某商品的数量 */ public void updateQuantity(HttpSession session, Long dishId, int quantity) { List cart getCart(session); for (CartItem item : cart) { if (item.getDishId().equals(dishId)) { if (quantity cart getCart(session); cart.removeIf(item - item.getDishId().equals(dishId)); } /** 清空购物车 */ public void clearCart(HttpSession session) { session.removeAttribute(cart); } /** 计算总金额 */ public BigDecimal calculateTotal(List cart) { return cart.stream() .map(CartItem::getSubtotal) .reduce(BigDecimal.ZERO, BigDecimal::add); }}Controller 层ControllerRequestMapping(“/cart”)public class CartController {Autowired private CartService cartService; /** 访问购物车页面 */ GetMapping public String viewCart(Model model, HttpSession session) { // 1. 获取购物车数据 List cart cartService.getCart(session); // 2. 计算总金额 BigDecimal totalAmount cartService.calculateTotal(cart); // 3. 将数据放入 Model model.addAttribute(cart, cart); model.addAttribute(totalAmount, totalAmount); // 4. 返回视图名称 return cart; } /** 添加商品到购物车 (Ajax 调用) */ PostMapping(/add) ResponseBody public Result addToCart(RequestBody CartItem item, HttpSession session) { try { cartService.addToCart(session, item); return Result.success(添加成功); } catch (Exception e) { return Result.error(e.getMessage()); } } /** 更新商品数量 (Ajax 调用) */ PostMapping(/update) ResponseBody public Result updateQuantity(RequestParam Long dishId, RequestParam int quantity, HttpSession session) { try { cartService.updateQuantity(session, dishId, quantity); return Result.success(更新成功); } catch (Exception e) { return Result.error(e.getMessage()); } } /** 删除商品 (Ajax 调用) */ PostMapping(/remove) ResponseBody public Result removeFromCart(RequestParam Long dishId, HttpSession session) { try { cartService.removeFromCart(session, dishId); return Result.success(删除成功); } catch (Exception e) { return Result.error(e.getMessage()); } } /** 提交订单 */ PostMapping(/submit) public String submitOrder(HttpSession session, RedirectAttributes redirectAttributes) { List cart cartService.getCart(session); if (cart.isEmpty()) { redirectAttributes.addFlashAttribute(error, 购物车为空无法下单); return redirect:/cart; } // TODO: 调用 OrderService 创建订单 // String orderNo orderService.createOrder(...); // 清空购物车 cartService.clearCart(session); // 重定向到订单成功页或订单列表页 return redirect:/order/success; }}二、前端代码 (HTML Vue.js Thymeleaf)创建文件 src/main/resources/templates/cart.html。我的餐车 - 餐厅在线点餐系统 body { font-family: Microsoft YaHei, sans-serif; background-color: #f5f5f5; margin: 0; padding: 20px; } .container { max-width: 900px; margin: 0 auto; background: white; padding: 20px; box-shadow: 0 2px 5px rgba(0,0,0,0.1); } /* 表格样式 */ table { width: 100%; border-collapse: collapse; margin-top: 20px; } th, td { padding: 12px; text-align: left; border-bottom: 1px solid #eee; } th { background-color: #e8f5e9; color: #2e7d32; font-weight: bold; } tr:hover { background-color: #f9f9f9; } .dish-info { display: flex; align-items: center; } .dish-info img { width: 60px; height: 60px; object-fit: cover; border-radius: 4px; margin-right: 10px; } .quantity-control { display: inline-flex; align-items: center; border: 1px solid #ddd; border-radius: 4px; } .quantity-control button { background: none; border: none; padding: 5px 8px; cursor: pointer; font-size: 16px; } .quantity-control input { width: 40px; text-align: center; border: none; font-size: 14px; } .delete-btn { background-color: #ff6b6b; color: white; border: none; padding: 5px 10px; border-radius: 4px; cursor: pointer; font-size: 12px; } .delete-btn:hover { background-color: #ee5a5a; } .total-section { text-align: right; margin-top: 20px; padding-top: 20px; border-top: 2px solid #eee; } .total-amount { font-size: 18px; color: #ff6b6b; font-weight: bold; } .submit-btn { background-color: #4facfe; color: white; border: none; padding: 10px 25px; border-radius: 4px; cursor: pointer; font-size: 16px; margin-left: 20px; } .submit-btn:hover { background-color: #00f2fe; } .submit-btn:disabled { background-color: #ccc; cursor: not-allowed; } 全部菜品 ({{ cart.length }}) 0 菜品 单价 数量 折扣 小计 操作 {{ item.dishName }} ¥ {{ item.price }} - {{ formatDiscount(item.discount) }} ¥ {{ item.subtotal.toFixed(2) }} 删除 购物车空空如也快去挑选美食吧 0 应付金额¥ {{ totalAmount.toFixed(2) }} 提交订单 // 【关键步骤】将后端传来的购物车数据转换为 JS 对象 var cartData /**/ []; var totalAmountData /**/ 0; new Vue({ el: #app, data: { cart: cartData, totalAmount: totalAmountData }, methods: { increaseQuantity(item) { item.quantity; this.updateServer(item); }, decreaseQuantity(item) { if (item.quantity 1) { item.quantity--; this.updateServer(item); } }, removeItem(item) { if(confirm(确定要删除这道菜吗)) { // 从本地数组移除 const index this.cart.indexOf(item); if (index -1) { this.cart.splice(index, 1); } // 调用后端API同步删除 this.callApi(/cart/remove, { dishId: item.dishId }); } }, submitOrder() { // 提交订单跳转到后端Controller // 这里可以直接用表单提交也可以用Ajax window.location.href /cart/submit; }, // 辅助方法调用后端API更新购物车 updateServer(item) { this.callApi(/cart/update, { dishId: item.dishId, quantity: item.quantity }); }, callApi(url, params) { // 使用 fetch API 发送 POST 请求 const formData new URLSearchParams(); for (let key in params) { formData.append(key, params[key]); } fetch(url, { method: POST, headers: { Content-Type: application/x-www-form-urlencoded }, body: formData }) .then(response response.json()) .then(data { if (!data.success) { alert(data.message || 操作失败); } // 无论成功失败都重新计算总金额因为本地数据已经变了 this.recalculateTotal(); }) .catch(error { console.error(Error:, error); alert(网络错误请稍后重试); }); }, recalculateTotal() { this.totalAmount this.cart.reduce((sum, item) sum item.subtotal, 0); }, formatDiscount(discount) { // 将 0.8 格式化为 8折 return (discount * 10) 折; } } });三、关键点说明数据来源购物车数据最初由后端从 Session 中取出并通过 Thymeleaf 的 /**/ 语法注入到前端的 JavaScript 变量中。动态交互所有对购物车的修改增删改数量都会通过 Vue 的方法触发并立即更新本地数据this.cart同时通过 fetch API 异步通知后端保证前后端数据一致。金额计算前端 Vue 实例会实时根据 cart 数组中的数据重新计算总金额 (recalculateTotal)确保用户看到的金额是最新的。提交订单点击“提交订单”按钮时会直接跳转到后端的 /cart/submit 接口由后端负责创建订单并清空购物车。这个实现完美复现了截图中的功能和布局并且具备了完整的业务逻辑。你可以直接将这段代码集成到你的项目中。

相关文章:

Java+SpringBoot的校园餐厅在线点餐管理系统 技术:Java、SpringBoot、MyBatis、HTML、Vue.js、MySQL、Echarts

JavaSpringBoot的校园餐厅在线点餐管理系统 技术:Java、SpringBoot、MyBatis、HTML、Vue.js、MySQL、Echarts 系统分用户和管理员2个角色: 用户模块: 登录注册,餐品搜索,加入餐车,订单支付,餐品…...

图形化界面工具 - webUI使用Page Assist 插件

图形化界面工具 - webUI使用 1、Page Assist 插件下载 安装 Page Assist 插件:本地 AI 模型的 Web UI 界面。 1 - 打开网站 Crx 搜搜:https://www.crxsoso.com 2 - 搜索:Page Assist,下载 crx 安装包 2、插件安装 谷歌浏览器打…...

【剪映9.9 全功能绿化版】剪映免费绿色版,2026最新全部功能可用

【剪映全功能绿化版】剪映免费绿色版,2026最新全部功能可用 领取方式如下:领取方法自取⬇️(平台不让放链接)①复制完整 关键词 :“筷莱廀牢玤齾虪夺郝” ,②然后再打开手机「夸克APP 或者 夸克网盘APP」没…...

模仿学习2.5:IQ-Learn

概念 直接从专家行为数据中学习 Q 函数,巧妙地绕过了传统方法中复杂且不稳定的对抗训练过程,同时隐式地表达了奖励函数和最优策略,只需用一个变量(Q 函数)来表示这两个函数,从而高效、稳定地让智能体从专家…...

Agent总是记不住?字节跳动开源OpenViking,用文件系统重构记忆

title: 字节跳动开源OpenViking:用「文件系统」重构AI Agent记忆,这才是正解 date: 2026-03-15 tags: [AI Agent, OpenViking, 字节跳动, 上下文管理] status: draft 说实话,做 AI Agent 开发最头疼的是什么?不是模型不够聪明&am…...

go-micro生成一个通用的grpc接口

创建一个基于 go-micro 的通用 gRPC 接口示例。go-micro 是一个流行的 Go 微服务框架,对 gRPC 提供了很好的支持。以下是一个完整的通用 gRPC 接口实现:1. 项目结构plain复制grpc-generic/ ├── proto/ │ └── generic.proto # Protocol Buf…...

COMSOL模拟干热岩地热开发中的THM耦合效应分析

COMSOL干热岩-地热THM耦合COMSOL桌面突然弹出一个未保存的模型——这已经是我这周第三次遇到岩层裂缝扩展不收敛的问题了。盯着屏幕上红彤彤的报错提示,突然意识到搞干热岩THM耦合仿真就像在虚拟地底玩俄罗斯方块,得把热力场(T)、渗流场(H)、应力场(M)三…...

基于扩散渗流的双孔介质煤层瓦斯流动模型,可模拟抽采半径,分析不同工况的抽采效果等COMSOL-...

基于扩散渗流的双孔介质煤层瓦斯流动模型,可模拟抽采半径,分析不同工况的抽采效果等COMSOL-双重介质煤层瓦斯抽采模拟案例 双重介质煤层瓦斯抽采模拟 包括 单孔抽采模拟-不同初始瓦斯压力和多孔抽采模型-不同抽采负压打开COMSOL时突然想到&#…...

分布式驱动下的直接横摆力矩控制MPC

分布式驱动 直接横摆力矩控制MPC 自己写的,效果如图踩下电门的那一刻,四台轮毂电机同时爆发的推背感让我意识到——这辆分布式驱动实验车和普通家用车根本不是一个维度的生物。但真正让我着迷的,是当它在80km/h急变道时,四个车轮像…...

Comsol相场法压裂案例:“裂纹相场法模拟及参考文献”

Comsol相场法压裂案例及相关参考文献 裂纹相场法模拟包括 1. 单边裂纹受剪切载荷作用 2. 单边裂纹受拉伸载荷作用 3. 初始地应力场作用下多孔介质水力压裂相场法压裂:用COMSOL玩转裂纹的三种姿势Comsol相场法压裂案例及相关参考文献 裂纹相场法模拟包括 1. 单边裂纹…...

COMSOL流沙层注浆数值模拟研究案例

COMSOL流沙层注浆数值模拟研究 案例本模型来源于文献复现,该文献分析了流沙层地质结构特点,应用有限元分析软件COMSOL Multiphysics对流沙层渗透注浆进行稳态与瞬态的数值模拟研究,分别计算了静水条件下和动水条件下注浆浆液扩散过程&#xf…...

【Java程序员转大模型开发 基础篇-向量数据库 看这一篇全盘掌握】

前期知识回顾 Java程序员转大模型开发 基础篇 Java程序员转大模型开发 实战篇-rag系统连接redis 提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前期知识回顾前言什么是向量数据库为什么要使用向量数据库向量数据…...

web个人博客系统——测试报告

一、报告概述1.项目背景本次测试的项目为个人博客系统,是为满足个人博客撰写而研发的Web 系统。系统主要实现用户登录、博客编辑、博客管理等业务能力,用于提供个人博客网页。为保障系统上线后功能正常、流程稳定、用户体验良好,按照项目计划…...

手把手玩转UUV三维路径跟踪

UUV三维路径跟踪,LOS制导PID控制实现 模型参数可调,期望轨迹可调,速度可调 MATLAB编程实现想要可直接联系~~~水下无人潜器(UUV)的路径跟踪听着高大上,但其实只要抓住LOS制导PID这对黄金组合,配合…...

高清互联双雄@ACP#GSV6155 与 SD642 赋能多场景显示切换新生态

在 Type-C 拓展坞、多屏显示终端、KVM 切换器等设备爆发式增长的当下,信号传输的稳定性、兼容性与切换灵活性成为产品竞争力的核心。基石酷联(GSCoolink)推出的 GSV6155 高性能信号重定时器与 SD642 高速多路复用器,以互补协同的产…...

基于三菱PLC的五层电梯控制系统设计探索

No.614 基于三菱PLC的五层电梯控制系统的设计5层电梯在自动化控制领域,电梯控制系统一直是一个经典且复杂的项目。今天咱就聊聊基于三菱PLC的五层电梯控制系统设计,这个系统设计可不简单,涉及到好多关键的逻辑和功能实现。 硬件框架 五层电梯…...

【Java SE】super 关键字详解

super 关键字详解 super 的本质与作用super 的三种用法a) 调用父类构造方法b) 访问父类属性b) 调用父类方法 深入理解 super 的查找机制(查找路径规则)super 与 this 的完整对比常见陷阱陷阱1:在静态方法中使用 super陷阱2:super …...

【2026年拼多多春招- 3月15日 -第四题- 多多的扩容计划】(题目+思路+JavaC++Python解析+在线测试)

题目内容 多多最近在做一条服务链路的大促扩容预案。他拿到了未来nnn个时间点的负载预测。 第iii个时间点 业务需求为aia_i...

洛谷 B4500:[GESP202603 三级] 凯撒密码 ← 字符串

【题目来源】 https://www.luogu.com.cn/problem/B4500 【题目描述】 凯撒密码是一种替换加密技术,明文中的所有字母都在字母表上向后(或向前)按照一个固定数目进行偏移后被替换成密文。例如,当偏移量是 3 的时候,所有…...

基于超螺旋滑模观测器的永磁同步电机无位置传感器控制策略全套仿真与公式推导资料更新及调试过程图文详解

最新版基于超螺旋滑模观测器的永磁同步电机无位置传感器控制策略 全套仿真、公式推导、配套论文以及调试过程如图 补充最新资料最近在实验室折腾永磁同步电机的无感控制,发现超螺旋滑模观测器这玩意儿真香。传统滑模观测器抖得跟筛糠似的,信号噪声能把人…...

Java 文件操作与 IO 流入门指南

在 Java 开发中,文件操作与 IO 流是处理数据持久化、数据交互的核心能力。本文将结合代码实例与核心概念,带你系统梳理 Java 文件 IO 的基础用法与设计思想。一、核心前置认知:输入输出的本质在文件 IO 体系中,输入与输出的定义需…...

目标检测数据集 第130期-基于yolo标注格式的纺织品纹理缺陷检测数据集(含免费分享)

目录 目标检测数据集 第130期-基于yolo标注格式的纺织品纹理缺陷检测数据集(含免费分享) 超实用纺织品纹理缺陷检测数据集分享,助力计算机视觉研究! 1、背景 2、数据详情 2.1 数据规模与构成 2.2 图像内容与场景 2.3 标注规范 3、应用场景 3.1 …...

如何在中国国内下载GitLab的源代码

明德融创工作室(Minter Fusion Studio, MFS) 出品 一、GitLab概述 1. Git Git是一个免费、开源、高性能的分布式版本控制系统。它可以记录下你对文件所做出的每一次修改,你可以随时查看谁在什么时候修改了文件的哪个部分。 Git是当今世界上…...

【测试基础】02-软件的生命周期和软件的测试流程

什么是软件工程(了解) 软件开发语言 机器码->汇编语言->c语言->高级语言(java) 定义 随着我们c语月的诞生,开发软件变得更加的团队化,因此暴露出一个巨大的问题,就是协作。 我们迫切需要一个方法和手段来约束我们的开发过程&#xf…...

Sentinel 集群限流:分布式服务统一控流

在分布式微服务架构成为企业标配的今天,流量管控早已告别“单机单打独斗”的时代。当一个服务部署数十甚至上百个实例,单机限流的局限性愈发凸显——单实例流量未超阈值,集群总流量却可能因分布不均而超限,最终导致服务雪崩、业务…...

【RAG】【Data-Processor】【node_parsers03】语义双重合并分块(Semantic Double Merging Chunking)案例分析

本案例展示了如何使用LlamaIndex中的语义双重合并分块器(SemanticDoubleMergingSplitterNodeParser)来智能地分割文本,这是一种更高级的语义分块方法,通过双重合并过程来创建语义连贯的文本块。1. 案例目标本案例的主要目标是:演示语义双重合…...

深入浅出计算机网络基石:详解 OSI 模型、TCP/IP 模型及数据通信全过程

前言大家好,这里是程序员阿亮,今天也是在公司里面当起黑奴了,实习生,大小周,不包饭,吃公司饭还要收钱。。。但是博客还是要写滴今天来给大家讲解一下OSI七层模型和TCP/IP模型这是计算机网络中的基础架构&am…...

【“小白致命坑”】[特殊字符] 备战蓝桥杯C++:别再写嵌套循环了!一招掌握 STL 结构体多级排序

【“小白致命坑”】🚀 备战蓝桥杯C:别再写嵌套循环了!一招掌握 STL 结构体多级排序 在备战蓝桥杯的过程中,我们经常会遇到一类非常经典的排序题(比如历年真题中的“奖学金”问题): “先按总分从…...

C语言_指针函数double字面量运算符

1、/除法运算符 //printf( 1/3); 输出是0, 无打印 printf( 4/3); 报错 printf(“%d\n”, 4/3); 正常,输出1 确保printf的第一个参数是一个字符串字面量,即直接用双引号括起来的字符串。 2、指针: 地址运算符& 和间接运算符 * &nur…...

免费开源毕设:基于YOLO的道路缺陷检测系统

🛣️ 基于 YOLO 的道路缺陷检测系统 (Road Defect Detection System) 本项目是一个功能完整的道路缺陷(如路面坑洼 Pothole)智能检测系统。基于最新的 Ultralytics YOLO 系列模型构建,并提供了一个美观、易用的 Streamlit Web 交…...