news 2026/10/21 9:03:06

社团信息管理系统毕业设计实战:从建表到部署的完整指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
社团信息管理系统毕业设计实战:从建表到部署的完整指南

简介:这份资源是面向高校计算机相关专业学生与指导教师的毕业设计/课程设计参考项目,主题为社团信息管理系统,适合需要完成同类选题、缺少完整可运行代码与文档支撑的开发者。压缩包共129个文件,约1.18MB,以111个php文件为核心业务实现,配合5个css与2个js完成前端样式与交互,另含1个sql数据库脚本、5个pdf说明文档、2个htaccess配置及README.md等,整体结构清晰,便于按模块阅读与二次修改。目前已有78人学习下载,可作为选题参考与实现模板。读者可从中获取社团信息管理系统的完整源码框架、数据库建表脚本、页面样式与脚本组织方式,以及部署所需的服务器配置示例,适合用于毕业设计答辩准备、课程设计提交或作为PHP项目练手素材,帮助快速理解系统模块划分与前后端协作流程。

1. 社团信息管理系统:从一份毕业设计压缩包说起

很多计算机毕业设计的起点,都是一个几十兆的压缩包,名字往往就叫「社团信息管理系统.zip」。打开之后,里面通常躺着一份 SQL 建表脚本、一个后端工程、一个前端工程,外加一份 Word 版论文。问题在于,直接跑起来的人少,跑起来之后能讲清楚「为什么这么设计」的人更少。社团信息管理系统这个题目看着简单,实际上它把用户权限、活动报名、成员审核、公告发布这几条业务线揉在一起,是一个非常适合练手的 CRUD 综合项目。它适合正在做计算机毕业设计、软件工程毕业设计的同学,也适合想拿一个完整项目练前后端分离的初级开发者。这一篇不讲空话,只讲这套系统怎么拆、怎么跑、参数怎么调、坑在哪。

2. 社团信息管理系统的业务边界与数据模型

2.1 先想清楚系统到底管什么

社团信息管理系统不是「学生管理系统」的换皮。它的核心对象是社团、成员、活动、公告四类,彼此之间是多对多和一对多的混合关系。一个学生可以加入多个社团,一个社团有多个成员,一个社团可以发起多场活动,一场活动有多个报名记录。如果一开始把「学生」和「成员」当成同一张表,后面做退社、转社、历史记录的时候一定会翻车。

我一般会先把业务边界画成三句话:谁能进系统、进来能干什么、干完留下什么记录。对应到表结构上,就是用户表、角色表、社团表、成员关系表、活动表、报名表、公告表。角色至少分三种:系统管理员、社团负责人、普通学生。权限的粒度不要一上来就做 RBAC 全套,毕业设计的体量用「角色字段 + 接口拦截」就够了,做太复杂反而讲不清。

常见做法是把成员关系单独抽一张club_member表,字段包括club_id、user_id、role_in_club、status、join_time。status用 0/1/2 表示待审核、已通过、已退出。这样社团负责人审核入社申请时,只需要改状态,不用删数据,历史记录天然保留。

2.2 建表脚本与关键字段说明

下面这份 SQL 是这套系统最常见、也最稳的建表方式,MySQL 8.0 直接能跑。注意字符集用utf8mb4,否则社团名称里带生僻字或者 emoji 会直接报错。

-- 社团信息管理系统核心表结构 CREATE DATABASE IF NOT EXISTS club_ms DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE club_ms; -- 用户表:所有角色共用一张,用 role 区分 CREATE TABLE `sys_user` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `username` VARCHAR(50) NOT NULL COMMENT '学号或工号', `password` VARCHAR(100) NOT NULL COMMENT 'BCrypt 加密后的密码', `real_name` VARCHAR(50) DEFAULT NULL, `role` TINYINT NOT NULL DEFAULT 0 COMMENT '0学生 1社团负责人 2管理员', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_username` (`username`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 社团表 CREATE TABLE `club` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `category` VARCHAR(30) DEFAULT NULL COMMENT '学术/体育/文艺/公益', `leader_id` BIGINT DEFAULT NULL COMMENT '负责人 user_id', `description` TEXT, `status` TINYINT DEFAULT 1 COMMENT '0停用 1正常', `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `idx_leader` (`leader_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 成员关系表:入社、退社、审核都在这张表上做 CREATE TABLE `club_member` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `club_id` BIGINT NOT NULL, `user_id` BIGINT NOT NULL, `role_in_club` VARCHAR(20) DEFAULT 'member' COMMENT 'member/manager', `status` TINYINT DEFAULT 0 COMMENT '0待审核 1已通过 2已退出', `join_time` DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `uk_club_user` (`club_id`,`user_id`), KEY `idx_user` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 活动表 CREATE TABLE `activity` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `club_id` BIGINT NOT NULL, `title` VARCHAR(200) NOT NULL, `start_time` DATETIME NOT NULL, `end_time` DATETIME NOT NULL, `location` VARCHAR(200) DEFAULT NULL, `capacity` INT DEFAULT 0 COMMENT '0表示不限人数', `status` TINYINT DEFAULT 1 COMMENT '0取消 1报名中 2已结束', PRIMARY KEY (`id`), KEY `idx_club` (`club_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; -- 活动报名表 CREATE TABLE `activity_signup` ( `id` BIGINT NOT NULL AUTO_INCREMENT, `activity_id` BIGINT NOT NULL, `user_id` BIGINT NOT NULL, `signup_time` DATETIME DEFAULT CURRENT_TIMESTAMP, `status` TINYINT DEFAULT 1 COMMENT '0取消 1已报名', PRIMARY KEY (`id`), UNIQUE KEY `uk_act_user` (`activity_id`,`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这份脚本里有两个唯一索引值得单独说。club_member上的uk_club_user保证同一个学生不会在同一个社团里出现两条记录,退社再入社时应该更新原记录的状态而不是插入新行。activity_signup上的uk_act_user保证一个人不能重复报名同一场活动,前端按钮点两下也不会脏数据。

capacity字段用 0 表示不限人数,这是一个约定,不是数据库约束。报名时要在业务层判断:先查活动当前报名数,再和capacity比较。如果capacity是 0 就跳过人数校验。这个判断必须放在事务里,否则并发报名会超员。

提示:建表时把create_time默认值设成CURRENT_TIMESTAMP,能省掉大量手动赋值,也方便后面按时间排序。

3. 后端接口与权限拦截的最小实现

3.1 用 Spring Boot 搭出可运行的骨架

后端用 Spring Boot 3.x 加 MyBatis-Plus 是最省事的组合,毕业设计里也最常见。核心依赖就四个:spring-boot-starter-web、mybatis-plus-boot-starter、mysql-connector-j、spring-boot-starter-validation。配置文件里把数据源和 MyBatis-Plus 的驼峰映射打开。

# application.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/club_ms?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: 你的密码 driver-class-name: com.mysql.cj.jdbc.Driver mybatis-plus: configuration: map-underscore-to-camel-case: true global-config: db-config: logic-delete-field: deleted logic-delete-value: 1 logic-not-delete-value: 0

serverTimezone必须写,不写的话 MySQL 8 驱动会报时区错误,这是血泪经验。map-underscore-to-camel-case打开后,数据库的real_name会自动映射到 Java 的realName,不用每个字段写@TableField。

权限拦截用一个最简单的HandlerInterceptor就够。登录成功后把用户信息放进 session 或者 JWT,拦截器里判断当前路径需要的角色。不要一上来就上 Spring Security,配置复杂、调试困难,毕业设计答辩时反而讲不清楚。

// 权限拦截器:按角色放行 @Component public class AuthInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 放行登录和静态资源 String uri = request.getRequestURI(); if (uri.startsWith("/api/auth/") || uri.startsWith("/static/")) { return true; } HttpSession session = request.getSession(false); if (session == null || session.getAttribute("userId") == null) { response.setStatus(401); response.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}"); return false; } // 管理员接口校验角色 if (uri.startsWith("/api/admin/")) { Integer role = (Integer) session.getAttribute("role"); if (role == null || role != 2) { response.setStatus(403); response.getWriter().write("{\"code\":403,\"msg\":\"无权限\"}"); return false; } } return true; } }

这段代码的关键在preHandle的返回值和response.setStatus。返回false会中断请求,但如果不手动写响应体,前端只会拿到一个空响应,排查起来很痛苦。/api/admin/前缀是约定,所有管理员接口都挂在这个路径下,拦截规则就一条,简单可靠。

3.2 入社审核与活动报名的接口写法

入社审核是这套系统里最典型的「状态流转」接口。学生提交申请时插入一条status=0的记录,社团负责人审核时把status改成 1 或删除。这里要注意:如果学生之前退过社,uk_club_user唯一索引会导致插入失败,所以正确做法是先查有没有历史记录,有就更新状态,没有才插入。

// 入社申请:存在历史记录则更新,否则新增 @PostMapping("/api/club/{clubId}/apply") public Result apply(@PathVariable Long clubId, HttpSession session) { Long userId = (Long) session.getAttribute("userId"); // 先查历史记录 ClubMember exist = clubMemberMapper.selectOne( new LambdaQueryWrapper<ClubMember>() .eq(ClubMember::getClubId, clubId) .eq(ClubMember::getUserId, userId) ); if (exist != null) { if (exist.getStatus() == 1) { return Result.fail("你已是该社团成员"); } // 退社后重新申请,更新状态即可 exist.setStatus(0); exist.setJoinTime(new Date()); clubMemberMapper.updateById(exist); return Result.ok("申请已提交"); } ClubMember member = new ClubMember(); member.setClubId(clubId); member.setUserId(userId); member.setStatus(0); clubMemberMapper.insert(member); return Result.ok("申请已提交"); }

活动报名的难点在并发。两个人同时点报名,如果先查人数再插入,中间有时间窗口,可能超员。稳妥做法是在报名接口上加一个基于活动 ID 的锁,或者直接用数据库的SELECT ... FOR UPDATE锁住活动行。

// 活动报名:事务内校验人数,防止超员 @Transactional(rollbackFor = Exception.class) @PostMapping("/api/activity/{activityId}/signup") public Result signup(@PathVariable Long activityId, HttpSession session) { Long userId = (Long) session.getAttribute("userId"); // 锁住活动行,串行化报名 Activity activity = activityMapper.selectByIdForUpdate(activityId); if (activity == null || activity.getStatus() != 1) { return Result.fail("活动不可报名"); } if (activity.getCapacity() > 0) { Long count = signupMapper.selectCount( new LambdaQueryWrapper<ActivitySignup>() .eq(ActivitySignup::getActivityId, activityId) .eq(ActivitySignup::getStatus, 1) ); if (count >= activity.getCapacity()) { return Result.fail("报名人数已满"); } } ActivitySignup signup = new ActivitySignup(); signup.setActivityId(activityId); signup.setUserId(userId); signup.setStatus(1); signupMapper.insert(signup); return Result.ok("报名成功"); }

selectByIdForUpdate需要在 Mapper 里手写 SQL,加FOR UPDATE。这个锁只在事务内有效,事务提交后释放。如果活动报名量不大,这个方案完全够用;如果量很大,可以考虑 Redis 预扣名额,但毕业设计没必要。

注意:@Transactional只对 RuntimeException 回滚,所以rollbackFor = Exception.class要显式写上,否则校验失败抛出的异常不会回滚。

4. 前端页面与接口联调的落地细节

4.1 Vue3 项目里最容易出错的三个配置

前端用 Vue3 + Vite + Axios 是当前毕业设计的主流。vite.config.js里的代理配置是联调第一道坎,配错了就是 404 或者跨域。

// vite.config.js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], server: { port: 5173, proxy: { // 所有 /api 开头的请求转发到后端 '/api': { target: 'http://localhost:8080', changeOrigin: true } } } })

changeOrigin: true必须加,否则后端拿到的 Host 是前端地址,某些安全校验会失败。代理路径/api要和后端接口前缀一致,不要一边写/api一边写/club,对不上就是 404。

Axios 实例里要统一处理 401 和 403。401 跳登录页,403 弹提示。不要在每个请求里单独判断,那样代码会散得到处都是。

// request.js import axios from 'axios' import { ElMessage } from 'element-plus' import router from '@/router' const request = axios.create({ baseURL: '/api', timeout: 10000 }) // 响应拦截:统一处理登录态和权限 request.interceptors.response.use( res => res.data, err => { const status = err.response?.status if (status === 401) { ElMessage.warning('请先登录') router.push('/login') } else if (status === 403) { ElMessage.error('没有操作权限') } else { ElMessage.error(err.response?.data?.msg || '请求失败') } return Promise.reject(err) } ) export default request

baseURL设成/api之后,页面里写request.get('/club/list')实际请求的是/api/club/list,和代理规则对上。这个约定要在项目一开始就定好,中途改会牵一发动全身。

4.2 社团列表与报名按钮的状态管理

社团列表页通常要展示社团名称、类别、负责人、当前人数,还要根据当前用户是否已加入显示不同按钮。这里的状态判断不要放在模板里写一堆v-if,抽成计算属性更清晰。

// ClubList.vue 片段 const clubList = ref([]) const myClubIds = ref(new Set()) // 加载社团列表和我的社团 async function loadData() { const [clubs, mine] = await Promise.all([ request.get('/club/list'), request.get('/club/my') ]) clubList.value = clubs.data myClubIds.value = new Set(mine.data.map(m => m.clubId)) } // 判断按钮状态:已加入 / 审核中 / 可申请 function btnState(clubId) { if (myClubIds.value.has(clubId)) return 'joined' return 'apply' }

Promise.all并发请求能减少页面加载时间,但要注意两个接口都成功才渲染,否则会出现列表有了、状态没对的闪烁。如果接口有依赖关系,就不能用Promise.all,要改成串行。

报名按钮点击后要禁用,防止重复提交。这个禁用状态用loading变量控制,请求结束后无论成功失败都要恢复。

const submitting = ref(false) async function handleSignup(activityId) { if (submitting.value) return submitting.value = true try { await request.post(`/activity/${activityId}/signup`) ElMessage.success('报名成功') await loadActivities() } finally { submitting.value = false } }

finally里恢复状态是关键,如果只在try里恢复,请求抛异常后按钮就永远禁用了,用户只能刷新页面。这种小细节在答辩演示时特别容易暴露。

5. 部署上线与常见问题排查

5.1 打包部署的两种方式和参数对照

毕业设计最后要交付一个能跑的系统,部署方式通常有两种:前后端分离部署和合并部署。两种方式的参数差异集中在端口、路径和跨域上。

项目分离部署合并部署
前端端口5173(开发)/ 80(生产)无独立端口
后端端口80808080
前端请求前缀/api经 Nginx 转发/api同源直连
跨域配置Nginx 加Access-Control-Allow-Origin不需要
静态资源位置Nginx 的root目录Spring Boot 的static目录
打包命令npm run build+mvn packagenpm run build后拷贝到static

合并部署适合答辩演示,一个 jar 包双击就能跑。做法是把npm run build生成的dist目录内容拷到 Spring Boot 的src/main/resources/static下,然后重新打包。注意 Vue Router 要用 hash 模式,history 模式在合并部署下刷新会 404,需要额外配置。

# 合并部署打包流程 cd frontend npm run build # 清空并拷贝静态资源 rm -rf ../backend/src/main/resources/static/* cp -r dist/* ../backend/src/main/resources/static/ cd ../backend mvn clean package -DskipTests # 生成的 jar 在 target 目录下,直接运行 java -jar target/club-ms-1.0.jar

-DskipTests在打包时能省时间,但正式交付前建议跑一遍测试。java -jar启动后访问http://localhost:8080就能看到页面,不需要单独起前端服务。

5.2 五条踩坑记录:现象、原因、解决

第一条:登录后刷新页面就退出登录。现象是登录成功跳转到首页,按 F5 后回到登录页。原因是登录态存在了 Vuex/Pinia 里,刷新后内存清空。解决是把 token 存到localStorage,应用启动时从localStorage恢复。如果用 session,要确保后端 session 超时时间够长,并且前端每次请求都带上 cookie。

第二条:社团列表查出来是空的,但数据库里有数据。现象是接口返回 200,data是空数组。原因通常是 MyBatis-Plus 的逻辑删除字段配置了但数据库表里没有deleted列,或者查询条件里带了deleted=0而实际数据是NULL。解决是检查实体类有没有@TableLogic注解,以及数据库列默认值是不是 0。

第三条:活动报名提示唯一索引冲突。现象是报名接口报Duplicate entry。原因是用户之前取消过报名,记录还在,status=0,再次报名时插入新行触发唯一索引。解决是把报名逻辑改成「先查后更新」,和入社申请一样,存在记录就更新status和signup_time。

第四条:中文社团名称在页面上显示成问号。现象是数据库里存的中文正常,但页面显示乱码。原因是数据库连接串没写characterEncoding=utf8,或者表字符集是latin1。解决是建库建表统一用utf8mb4,连接串加useUnicode=true&characterEncoding=utf8,前端 HTML 的meta声明charset=utf-8。

第五条:打包后的 jar 运行报找不到主类。现象是java -jar提示no main manifest attribute。原因是pom.xml里没配spring-boot-maven-plugin,或者打包时用了maven-jar-plugin而不是 Spring Boot 的插件。解决是在pom.xml的build节点里加上spring-boot-maven-plugin,然后重新mvn package。

提示:排查接口问题时,先看浏览器 Network 面板的请求 URL、状态码、响应体,再看后端控制台日志。大部分问题在前两步就能定位,不用急着改代码。

6. 把社团系统做成能讲清楚的作品

6.1 用接口文档和测试用例反向验证设计

系统跑起来只是第一步,答辩时老师更关心「你怎么保证它是对的」。一个成本很低但效果很好的做法是:用 Postman 或者 Apifox 把核心接口整理成一份集合,每个接口配两到三个测试用例,覆盖正常流程和边界情况。比如入社申请要测「首次申请」「重复申请」「退社后重新申请」三种;活动报名要测「正常报名」「满员报名」「重复报名」三种。

这份集合不用写自动化脚本,手动跑一遍截图放进论文附录就够了。但它能逼着你把每个接口的输入输出想清楚,答辩时被问到「如果用户重复提交怎么办」,你可以直接翻到对应的测试用例。

// 活动报名接口的测试用例结构(Apifox/Postman 通用) { "name": "报名-满员场景", "request": { "method": "POST", "url": "/api/activity/1/signup", "header": { "Cookie": "JSESSIONID=xxx" } }, "assert": [ { "field": "code", "operator": "eq", "value": 500 }, { "field": "msg", "operator": "contains", "value": "人数已满" } ] }

断言里不要只判断 HTTP 状态码,业务失败通常也是 200,要看响应体里的code和msg。这个习惯在真实工作里同样重要,很多线上问题就是「接口返回 200 但业务失败」被监控漏掉了。

6.2 一个能加分的扩展点:操作日志

如果时间还够,加一张operation_log表,记录谁在什么时候对哪个社团或活动做了什么操作。字段就五个:user_id、action、target_type、target_id、create_time。在关键接口里插一条日志,比如审核入社、发布活动、取消报名。

这个功能代码量不大,但答辩时很能体现「工程思维」。老师问「怎么追溯谁把活动取消了」,你可以直接演示日志查询页面。实现上用 AOP 切面最优雅,但毕业设计里手动在 Service 里插一条也完全可以,关键是别漏掉关键操作。

// 手动记录操作日志,简单直接 private void log(Long userId, String action, String targetType, Long targetId) { OperationLog log = new OperationLog(); log.setUserId(userId); log.setAction(action); log.setTargetType(targetType); log.setTargetId(targetId); operationLogMapper.insert(log); }

调用时机放在业务操作成功之后、事务提交之前。如果放在事务外面,业务回滚了日志还在,就变成脏数据。这个细节我在第一次做的时候没注意,后来查日志发现对不上,才回头改成事务内插入。

6.3 交付前我一般会做的三件事

第一件是把数据库脚本、后端 jar、前端 dist、论文、接口文档放在同一个目录下,写一个README.md说明启动步骤。不要指望别人会看论文里的部署章节,一个能照着敲的命令列表比什么都强。

第二件是在一台没装过开发环境的电脑上跑一遍。我自己的机器上装了一堆依赖,跑得起来不代表别人跑得起来。找同学借台电脑,从装 JDK 开始走一遍流程,缺什么补什么。这一步能暴露 80% 的环境问题。

第三件是把所有默认密码改掉。数据库密码、管理员账号密码,不要用123456或者root。答辩演示时被看到弱密码,印象分直接扣。改成稍微复杂一点的,写在 README 里,但别写在论文正文里。

这套社团信息管理系统看起来简单,但把权限、状态流转、并发报名、部署这几块都走一遍,对前后端分离的理解会扎实很多。我做完之后最大的习惯变化是:任何涉及状态变更的接口,先想清楚「重复调用会怎样」,再动手写代码。希望帮到你。

本文还有配套的精品资源,点击获取

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

2026 IDE简记:多语言混编、远程开发与AI辅助编程实践

眼下这批项目的技术栈是越来越杂了&#xff0c;Java服务、Python脚本、前端页面、Go工具链全混在一个仓库里&#xff0c;我花了不少时间折腾IDE的选型、配置、插件、还有AI辅助编程的工作流。写这篇“IDE简记&#xff1a;2026更新”&#xff0c;主要是把这一年多反复试错之后留…

作者头像 李华
网站建设 2026/10/16 14:17:05

大数据生产隐私泄露风险拆解与脱敏权限防御实操

大数据生产中的隐私泄露&#xff1a;一次全景式风险拆解与防御实操干大数据这行的人&#xff0c;多少都有过那种后背发凉的时刻。我亲眼见过一个团队&#xff0c;辛苦搭建的数据平台终于跑起来了&#xff0c;结果上线第三个月&#xff0c;安全团队就收到一条告警&#xff1a;某…

作者头像 李华
网站建设 2026/10/19 21:36:58

继续教育论文降AI率实战:千笔与知文AI对比评测

继续教育阶段的论文定稿&#xff0c;十有八九是卡在最后一关的AI检测上。这半年我帮几位成人本科、在职研究生朋友改论文&#xff0c;被问得最多的就是“降AI率”两个字&#xff0c;而他们提到名字最频繁的&#xff0c;一个是千笔专业降AI率智能体&#xff0c;一个是知文AI。说…

作者头像 李华
网站建设 2026/10/21 9:02:56

技能分类学习法:四类技能匹配不同策略,效率翻倍

技能学习这件事&#xff0c;我踩过不少坑。早年间我什么都想学&#xff0c;今天看别人写代码厉害就去啃语法&#xff0c;明天看同事演讲出色又去报口才班&#xff0c;结果每样都学了个皮毛&#xff0c;一到真刀真枪的场合就露怯。后来我才慢慢想明白一个道理&#xff1a;不同类…

作者头像 李华
网站建设 2026/10/16 5:15:59

Vue2入门不必先上脚手架:CDN方式构建实例与常用指令全解析

第一次接触Vue2&#xff0c;很多朋友是被脚手架劝退的。装了Node、配了npm源&#xff0c;一创建项目又跳出一堆文件&#xff0c;还没写几行代码&#xff0c;先被webpack配置和目录结构搞晕了。我自己入门前端时也是这样&#xff0c;直到某天接手一个用CDN引入vue.js的旧页面&am…

作者头像 李华
网站建设 2026/10/17 0:57:17

MAC地址原理与实战:从48位硬件标识到网络排障核心

1. 这不是“查个MAC”那么简单&#xff1a;从一串十六进制数字看懂设备身份的底层逻辑你点开网络设置&#xff0c;翻三页找到“物理地址”&#xff0c;看到一串像00:1A:2B:3C:4D:5E这样的字符&#xff0c;心里可能只闪过一个念头&#xff1a;“哦&#xff0c;这就是MAC地址”。…

作者头像 李华