news 2026/9/6 3:07:02

SSM+VUE家庭食谱管理系统:前后端分离开发实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SSM+VUE家庭食谱管理系统:前后端分离开发实战指南

这次我们来看一个基于SSM+VUE的家庭食谱管理系统,这是一个完整的计算机毕业设计项目,包含了从选题到答辩的全流程文档和代码实现。对于计算机科学与技术专业的学生来说,这样的项目不仅能够满足毕业设计的要求,更重要的是能够系统掌握前后端分离开发的核心技术栈。

这个项目的核心价值在于它提供了一个真实可用的家庭食谱管理解决方案,同时也是一个完整的技术学习案例。前端采用Vue.js框架构建用户界面,后端使用SSM(Spring+SpringMVC+MyBatis)框架处理业务逻辑,数据库使用MySQL存储数据。整个项目涵盖了用户管理、食谱分类、食材管理、营养分析等核心功能模块。

1. 核心能力速览

能力项说明
技术栈前端Vue.js + 后端SSM框架 + MySQL数据库
开发模式前后端分离架构,RESTful API接口
核心功能用户管理、食谱管理、食材管理、营养分析、收藏分享
部署方式本地开发环境部署,支持Docker容器化部署
适合场景计算机毕业设计、全栈开发学习、食谱管理应用开发
文档完整性包含选题报告、开题报告、任务书、中期检查、论文、答辩PPT

2. 适用场景与使用边界

这个家庭食谱管理系统主要面向计算机专业的学生和全栈开发学习者。对于正在准备毕业设计的同学来说,这个项目提供了一个完整的参考模板,涵盖了从项目立项到最终答辩的全过程文档。对于想要学习前后端分离开发的技术爱好者,项目展示了Vue.js与SSM框架的整合方式,以及RESTful API的设计规范。

从功能角度来看,系统适合家庭用户管理个人食谱、记录饮食习惯、分析营养摄入。系统支持食谱的增删改查、食材管理、营养信息计算等核心功能,能够满足基本的家庭食谱管理需求。

需要注意的是,这个项目主要定位为教学和毕业设计用途,如果要投入商业使用,需要考虑数据安全性、性能优化、用户规模扩展等问题。特别是在营养分析功能方面,系统的计算逻辑需要结合实际营养学知识进行完善。

3. 环境准备与前置条件

在开始部署和运行这个家庭食谱管理系统之前,需要确保开发环境满足以下要求:

3.1 硬件环境要求

  • 内存:至少8GB RAM,推荐16GB
  • 存储:至少10GB可用空间
  • 处理器:Intel i5或同等性能以上

3.2 软件环境要求

后端环境:

  • JDK 1.8或更高版本
  • Maven 3.6+
  • MySQL 5.7或8.0版本
  • Tomcat 8.5+或Spring Boot内嵌容器

前端环境:

  • Node.js 14.0或更高版本
  • npm 6.0+或yarn包管理器
  • Vue CLI 4.0+

3.3 开发工具准备

  • IDE:IntelliJ IDEA(后端)、VS Code(前端)
  • 数据库管理工具:Navicat、MySQL Workbench
  • API测试工具:Postman、Apifox
  • 版本控制:Git

3.4 环境验证步骤

在开始项目部署前,建议先验证基础环境是否正常:

# 验证Java环境 java -version javac -version # 验证Maven环境 mvn -version # 验证Node.js环境 node -v npm -v # 验证MySQL连接 mysql -u root -p

4. 数据库设计与初始化

家庭食谱管理系统的数据库设计是整个项目的核心基础,合理的表结构设计能够保证系统的稳定运行和扩展性。

4.1 主要数据表结构

用户表(user)

CREATE TABLE user ( id BIGINT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) UNIQUE NOT NULL, password VARCHAR(100) NOT NULL, email VARCHAR(100), phone VARCHAR(20), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP );

食谱表(recipe)

CREATE TABLE recipe ( id BIGINT PRIMARY KEY AUTO_INCREMENT, title VARCHAR(200) NOT NULL, description TEXT, cooking_time INT, difficulty_level ENUM('简单', '中等', '困难'), category_id BIGINT, user_id BIGINT, image_url VARCHAR(500), create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (category_id) REFERENCES category(id), FOREIGN KEY (user_id) REFERENCES user(id) );

食材表(ingredient)

CREATE TABLE ingredient ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, unit VARCHAR(20), calorie_per_unit DECIMAL(8,2), protein DECIMAL(8,2), fat DECIMAL(8,2), carbohydrate DECIMAL(8,2) );

4.2 数据库初始化脚本

项目提供完整的数据库初始化脚本,包含表结构创建和基础数据插入:

-- 创建数据库 CREATE DATABASE IF NOT EXISTS family_recipe DEFAULT CHARSET utf8mb4; -- 使用数据库 USE family_recipe; -- 创建用户表 -- 创建食谱分类表 -- 创建食谱表 -- 创建食材表 -- 创建食谱食材关联表 -- 插入初始数据

4.3 数据库连接配置

在后端项目的配置文件中,需要正确配置数据库连接信息:

# application.properties spring.datasource.url=jdbc:mysql://localhost:3306/family_recipe?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai spring.datasource.username=root spring.datasource.password=your_password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # MyBatis配置 mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.family.recipe.entity

5. 后端SSM框架整合与部署

SSM框架的整合是项目的技术核心,需要正确配置Spring、SpringMVC和MyBatis的协同工作。

5.1 Maven依赖配置

在pom.xml中配置项目依赖:

<dependencies> <!-- Spring核心依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.0</version> </dependency> <!-- SpringMVC依赖 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.0</version> </dependency> <!-- MyBatis整合Spring --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.6</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.25</version> </dependency> </dependencies>

5.2 Spring配置类

使用Java配置类替代传统的XML配置:

@Configuration @ComponentScan("com.family.recipe") @EnableWebMvc public class SpringMvcConfig implements WebMvcConfigurer { @Bean public ViewResolver viewResolver() { InternalResourceViewResolver resolver = new InternalResourceViewResolver(); resolver.setPrefix("/WEB-INF/views/"); resolver.setSuffix(".jsp"); return resolver; } @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static/**") .addResourceLocations("classpath:/static/"); } }

5.3 MyBatis配置与Mapper开发

配置MyBatis并开发数据访问层:

@Mapper public interface RecipeMapper { List<Recipe> selectAllRecipes(); Recipe selectRecipeById(Long id); int insertRecipe(Recipe recipe); int updateRecipe(Recipe recipe); int deleteRecipe(Long id); }

对应的XML映射文件:

<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.family.recipe.mapper.RecipeMapper"> <select id="selectAllRecipes" resultType="Recipe"> SELECT * FROM recipe WHERE status = 1 </select> <insert id="insertRecipe" useGeneratedKeys="true" keyProperty="id"> INSERT INTO recipe (title, description, cooking_time, difficulty_level, user_id) VALUES (#{title}, #{description}, #{cookingTime}, #{difficultyLevel}, #{userId}) </insert> </mapper>

6. 前端Vue.js项目构建

前端采用Vue.js框架,使用Vue CLI创建项目并配置路由、状态管理等核心功能。

6.1 项目初始化与依赖安装

使用Vue CLI创建项目并安装必要依赖:

# 创建Vue项目 vue create family-recipe-frontend # 进入项目目录 cd family-recipe-frontend # 安装路由和状态管理 npm install vue-router@4 vuex@4 # 安装UI组件库 npm install element-plus@2.0.0 # 安装axios用于API调用 npm install axios

6.2 项目目录结构规划

合理的目录结构有助于项目维护和团队协作:

src/ ├── components/ # 可复用组件 │ ├── RecipeCard.vue │ ├── IngredientList.vue │ └── NutritionChart.vue ├── views/ # 页面组件 │ ├── Home.vue │ ├── RecipeList.vue │ ├── RecipeDetail.vue │ └── UserProfile.vue ├── router/ # 路由配置 │ └── index.js ├── store/ # 状态管理 │ └── index.js ├── api/ # API接口 │ └── recipe.js └── assets/ # 静态资源

6.3 路由配置与页面导航

配置前端路由实现页面跳转:

// router/index.js import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import RecipeList from '../views/RecipeList.vue' const routes = [ { path: '/', name: 'Home', component: Home }, { path: '/recipes', name: 'RecipeList', component: RecipeList }, { path: '/recipe/:id', name: 'RecipeDetail', component: () => import('../views/RecipeDetail.vue') } ] const router = createRouter({ history: createWebHistory(), routes }) export default router

6.4 API接口封装与调用

封装统一的API调用方法:

// api/recipe.js import axios from 'axios' const api = axios.create({ baseURL: 'http://localhost:8080/api', timeout: 10000 }) // 请求拦截器 api.interceptors.request.use(config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }) // 响应拦截器 api.interceptors.response.use( response => response.data, error => { console.error('API调用错误:', error) return Promise.reject(error) } ) export const recipeApi = { // 获取食谱列表 getRecipes(params) { return api.get('/recipes', { params }) }, // 获取食谱详情 getRecipeById(id) { return api.get(`/recipes/${id}`) }, // 创建新食谱 createRecipe(data) { return api.post('/recipes', data) }, // 更新食谱 updateRecipe(id, data) { return api.put(`/recipes/${id}`, data) }, // 删除食谱 deleteRecipe(id) { return api.delete(`/recipes/${id}`) } }

7. 核心功能模块实现

家庭食谱管理系统包含多个核心功能模块,每个模块都需要前后端协同实现。

7.1 用户认证与权限管理

实现用户登录、注册、权限验证功能:

// 后端登录接口 @RestController @RequestMapping("/api/auth") public class AuthController { @PostMapping("/login") public ResponseEntity<LoginResponse> login(@RequestBody LoginRequest request) { // 验证用户名密码 User user = userService.authenticate(request.getUsername(), request.getPassword()); if (user != null) { // 生成JWT token String token = jwtUtil.generateToken(user.getUsername()); LoginResponse response = new LoginResponse(); response.setToken(token); response.setUserInfo(user); return ResponseEntity.ok(response); } else { return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); } } }

前端登录组件实现:

<template> <div class="login-container"> <el-form :model="loginForm" :rules="rules" ref="loginFormRef"> <el-form-item prop="username"> <el-input v-model="loginForm.username" placeholder="用户名"></el-input> </el-form-item> <el-form-item prop="password"> <el-input type="password" v-model="loginForm.password" placeholder="密码"></el-input> </el-form-item> <el-button type="primary" @click="handleLogin">登录</el-button> </el-form> </div> </template> <script> import { ref } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' import { authApi } from '@/api/auth' export default { setup() { const router = useRouter() const loginForm = ref({ username: '', password: '' }) const rules = { username: [{ required: true, message: '请输入用户名', trigger: 'blur' }], password: [{ required: true, message: '请输入密码', trigger: 'blur' }] } const handleLogin = async () => { try { const response = await authApi.login(loginForm.value) localStorage.setItem('token', response.token) ElMessage.success('登录成功') router.push('/') } catch (error) { ElMessage.error('登录失败,请检查用户名和密码') } } return { loginForm, rules, handleLogin } } } </script>

7.2 食谱管理功能实现

食谱管理包括食谱的增删改查、分类管理、搜索筛选等功能。

后端食谱控制器:

@RestController @RequestMapping("/api/recipes") public class RecipeController { @Autowired private RecipeService recipeService; @GetMapping public ResponseEntity<PageResult<Recipe>> getRecipes( @RequestParam(defaultValue = "1") int page, @RequestParam(defaultValue = "10") int size, @RequestParam(required = false) String keyword, @RequestParam(required = false) Long categoryId) { PageResult<Recipe> result = recipeService.getRecipes(page, size, keyword, categoryId); return ResponseEntity.ok(result); } @PostMapping public ResponseEntity<Recipe> createRecipe(@RequestBody Recipe recipe) { Recipe savedRecipe = recipeService.createRecipe(recipe); return ResponseEntity.status(HttpStatus.CREATED).body(savedRecipe); } @PutMapping("/{id}") public ResponseEntity<Recipe> updateRecipe(@PathVariable Long id, @RequestBody Recipe recipe) { recipe.setId(id); Recipe updatedRecipe = recipeService.updateRecipe(recipe); return ResponseEntity.ok(updatedRecipe); } }

前端食谱列表页面:

<template> <div class="recipe-list"> <div class="search-bar"> <el-input v-model="searchKeyword" placeholder="搜索食谱" @input="handleSearch"></el-input> <el-select v-model="selectedCategory" placeholder="选择分类" @change="handleCategoryChange"> <el-option v-for="category in categories" :key="category.id" :label="category.name" :value="category.id"></el-option> </el-select> </div> <div class="recipe-grid"> <recipe-card v-for="recipe in recipes" :key="recipe.id" :recipe="recipe"></recipe-card> </div> <el-pagination :current-page="pagination.current" :page-size="pagination.size" :total="pagination.total" @current-change="handlePageChange"> </el-pagination> </div> </template> <script> import { ref, onMounted } from 'vue' import RecipeCard from '@/components/RecipeCard.vue' import { recipeApi } from '@/api/recipe' export default { components: { RecipeCard }, setup() { const recipes = ref([]) const categories = ref([]) const searchKeyword = ref('') const selectedCategory = ref('') const pagination = ref({ current: 1, size: 12, total: 0 }) const loadRecipes = async () => { try { const params = { page: pagination.value.current, size: pagination.value.size, keyword: searchKeyword.value, categoryId: selectedCategory.value } const result = await recipeApi.getRecipes(params) recipes.value = result.data pagination.value.total = result.total } catch (error) { console.error('加载食谱失败:', error) } } const handleSearch = () => { pagination.value.current = 1 loadRecipes() } const handleCategoryChange = () => { pagination.value.current = 1 loadRecipes() } const handlePageChange = (page) => { pagination.value.current = page loadRecipes() } onMounted(() => { loadRecipes() }) return { recipes, categories, searchKeyword, selectedCategory, pagination, handleSearch, handleCategoryChange, handlePageChange } } } </script>

7.3 营养分析功能实现

营养分析是食谱管理系统的特色功能,通过计算食材的营养成分来评估食谱的营养价值。

营养计算服务:

@Service public class NutritionService { public NutritionInfo calculateNutrition(Recipe recipe) { NutritionInfo nutritionInfo = new NutritionInfo(); for (RecipeIngredient ingredient : recipe.getIngredients()) { Ingredient ing = ingredient.getIngredient(); double quantity = ingredient.getQuantity(); // 计算热量 nutritionInfo.addCalorie(ing.getCaloriePerUnit() * quantity); // 计算蛋白质 nutritionInfo.addProtein(ing.getProtein() * quantity); // 计算脂肪 nutritionInfo.addFat(ing.getFat() * quantity); // 计算碳水化合物 nutritionInfo.addCarbohydrate(ing.getCarbohydrate() * quantity); } return nutritionInfo; } }

前端营养分析图表组件:

<template> <div class="nutrition-chart"> <div ref="chartEl" style="width: 100%; height: 300px;"></div> <div class="nutrition-summary"> <el-row :gutter="20"> <el-col :span="6"> <div class="nutrition-item"> <div class="value">{{ nutritionInfo.calorie }}</div> <div class="label">热量(kcal)</div> </div> </el-col> <el-col :span="6"> <div class="nutrition-item"> <div class="value">{{ nutritionInfo.protein }}</div> <div class="label">蛋白质(g)</div> </div> </el-col> <el-col :span="6"> <div class="nutrition-item"> <div class="value">{{ nutritionInfo.fat }}</div> <div class="label">脂肪(g)</div> </div> </el-col> <el-col :span="6"> <div class="nutrition-item"> <div class="value">{{ nutritionInfo.carbohydrate }}</div> <div class="label">碳水(g)</div> </div> </el-col> </el-row> </div> </div> </template> <script> import { ref, onMounted, watch } from 'vue' import * as echarts from 'echarts' export default { props: { nutritionInfo: { type: Object, required: true } }, setup(props) { const chartEl = ref(null) let chartInstance = null const initChart = () => { if (!chartEl.value) return chartInstance = echarts.init(chartEl.value) const option = { tooltip: { trigger: 'item' }, legend: { orient: 'vertical', left: 'left' }, series: [ { name: '营养构成', type: 'pie', radius: '50%', data: [ { value: props.nutritionInfo.protein, name: '蛋白质' }, { value: props.nutritionInfo.fat, name: '脂肪' }, { value: props.nutritionInfo.carbohydrate, name: '碳水化合物' } ], emphasis: { itemStyle: { shadowBlur: 10, shadowOffsetX: 0, shadowColor: 'rgba(0, 0, 0, 0.5)' } } } ] } chartInstance.setOption(option) } watch(() => props.nutritionInfo, () => { if (chartInstance) { chartInstance.dispose() initChart() } }) onMounted(() => { initChart() }) return { chartEl } } } </script>

8. 系统部署与运行测试

完成开发后,需要进行系统部署和全面的功能测试。

8.1 后端项目打包部署

使用Maven进行项目打包:

# 清理并打包项目 mvn clean package -DskipTests # 运行Spring Boot应用 java -jar target/family-recipe-1.0.0.jar # 或者使用Docker部署 docker build -t family-recipe . docker run -p 8080:8080 family-recipe

8.2 前端项目构建部署

构建生产环境版本:

# 安装依赖 npm install # 构建项目 npm run build # 预览构建结果 npm run serve # 部署到Nginx # 将dist目录内容复制到Nginx的html目录

8.3 功能测试用例

编写完整的测试用例确保系统稳定性:

@SpringBootTest class RecipeServiceTest { @Autowired private RecipeService recipeService; @Test void testCreateRecipe() { Recipe recipe = new Recipe(); recipe.setTitle("测试食谱"); recipe.setDescription("这是一个测试食谱"); Recipe savedRecipe = recipeService.createRecipe(recipe); assertNotNull(savedRecipe.getId()); assertEquals("测试食谱", savedRecipe.getTitle()); } @Test void testSearchRecipes() { PageResult<Recipe> result = recipeService.getRecipes(1, 10, "测试", null); assertTrue(result.getData().size() > 0); assertTrue(result.getTotal() > 0); } }

8.4 性能测试与优化

进行压力测试和性能优化:

# 使用Apache Bench进行压力测试 ab -n 1000 -c 100 http://localhost:8080/api/recipes # 监控系统资源使用情况 top -p $(pgrep -f family-recipe)

9. 毕业设计文档编写指南

完整的毕业设计项目需要包含规范的文档材料。

9.1 开题报告编写要点

开题报告应包含以下内容:

  • 项目背景与研究意义
  • 国内外研究现状
  • 研究目标与内容
  • 技术路线与实施方案
  • 预期成果与创新点
  • 进度安排与风险评估

9.2 系统设计文档

系统设计文档需要详细描述:

  • 系统架构设计
  • 数据库设计
  • 接口设计规范
  • 模块功能设计
  • 安全性设计考虑

9.3 论文撰写规范

毕业论文应遵循学术规范:

  • 摘要(中英文)
  • 目录结构清晰
  • 正文逻辑严谨
  • 参考文献规范
  • 致谢真诚得体

9.4 答辩PPT制作技巧

答辩PPT应突出重点:

  • 项目背景与意义(1-2页)
  • 系统架构与技术选型(2-3页)
  • 核心功能演示(3-4页)
  • 创新点与难点(1-2页)
  • 总结与展望(1页)

10. 常见问题与解决方案

在项目开发和部署过程中可能会遇到各种问题,这里总结一些常见问题的解决方法。

10.1 环境配置问题

问题:MySQL连接失败

解决方案: 1. 检查MySQL服务是否启动 2. 验证数据库连接参数是否正确 3. 检查防火墙设置是否允许3306端口访问 4. 确认数据库用户权限设置

问题:Node.js版本兼容性问题

解决方案: 1. 使用nvm管理Node.js版本 2. 检查package.json中的引擎要求 3. 清除node_modules重新安装依赖

10.2 前后端联调问题

问题:跨域访问错误

// 后端解决方案:配置CORS @Configuration public class CorsConfig implements WebMvcConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("http://localhost:3000") .allowedMethods("GET", "POST", "PUT", "DELETE") .allowCredentials(true); } }

问题:API接口调用超时

解决方案: 1. 检查网络连接稳定性 2. 调整前端axios超时设置 3. 优化后端接口响应时间 4. 考虑使用接口缓存机制

10.3 性能优化建议

数据库优化:

  • 为常用查询字段添加索引
  • 避免SELECT *,只查询需要的字段
  • 使用连接池管理数据库连接

前端优化:

  • 使用路由懒加载减少初始包大小
  • 图片资源进行压缩优化
  • 合理使用浏览器缓存机制

后端优化:

  • 使用Redis缓存热点数据
  • 数据库查询结果分页处理
  • 异步处理耗时操作

这个SSM+VUE家庭食谱管理系统项目为计算机专业学生提供了一个完整的学习和实践平台。通过这个项目,不仅能够掌握前后端分离开发的技术栈,还能了解软件工程的全流程管理。建议在开发过程中注重代码规范、文档编写和测试覆盖,这些都是成为合格软件工程师的重要素养。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/6 3:05:56

DoIP诊断全解析:从DHCP/AutoIP到车辆发现与路由激活

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 3:04:32

国产FPGA突围:从芯片架构到开发工具链的全流程实战解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 3:03:26

基于Pytest与Requests的接口自动化测试框架搭建实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/6 3:02:41

基于SpringBoo的班级评优评奖管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

作者头像 李华
网站建设 2026/9/6 3:02:13

国产MCU替代STM32:Pin-to-Pin兼容背后的5个隐藏坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华