简介:面向毕业设计与物联网实战的作业场所粉尘危害监测预警系统,以粉尘浓度监测与预警为核心场景,适合物联网、嵌入式或底层系统方向的小白与进阶学习者,也可直接用于毕设、课程设计、大作业或工程实训。压缩包共952个文件,约5.07MB,以png、js、css、html为主,涵盖界面截图、前端逻辑、页面骨架与样式配置,另有json、map、md等文件辅助配置和说明,整体是一个可直接运行验证的项目工程。源码经过严格测试,能快速跑通粉尘监测预警流程;借助内容预览中的layui、皮肤样式、toast等前端组件,读者可清晰了解界面层封装思路,并在此基础上修改复刻,扩展更多传感器接入或告警功能。已有32人学习下载,对于需要积累物联网项目经验或准备毕业设计的开发者来说,具备较好的借鉴与二次开发价值。
1. 粉尘监测预警系统,是物联网毕业设计里少有的能完整闭环的选题
在木材加工、石材切割这类作业场所,粉尘浓度超标不能靠肉眼判断,更不能靠人工巡检兜底——等看见扬尘时,浓度往往已经远超限值。基于物联网的作业场所粉尘危害监测预警系统,就是把这个过程做成闭环:传感器采集浓度,主控板读数,无线网络上传,服务端按阈值分级判定,触发声光警报和短信通知,历史数据留存追溯。它适合物联网工程、电子信息、自动化专业的毕业设计,也适合想拿完整实战项目参加技能竞赛的同学。感知、传输、存储、报警、展示每一层都有可检验的交付物,答辩时不缺内容,现场演示也直观,是物联网毕业设计选题里少见的“全链路”题目。
2. 系统架构与硬件选型:先把感知、传输、应用三层算清楚再下单
2.1 三层架构与数据流向:从粉尘浓度到预警消息,中间经过哪几步
做粉尘监测预警系统,最忌讳拿到板子就写代码。先问自己三个问题:粉尘浓度用什么传感器测,数据怎么传到机房,报警消息发给谁。这三个问题恰好对应感知层、传输层、应用层,任何一层选错,后期都要返工。
感知层是数据源头。监测预警系统的可信度完全取决于传感器读数,粉尘传感器必须按作业场所的实际浓度范围选,不能随便拿个室内空气质量模块凑数。传输层负责把数据从车间送到服务端,常见方案有 WiFi、4G Cat.1、NB-IoT 和 LoRa,按现场有没有网、有没有电、点位多不多来定。应用层包括数据接收、入库、阈值判定、报警推送和可视化看板,这部分直接决定系统能不能真正用起来。
我的习惯是先把数据流画在纸上,再动手买板子:
粉尘传感器 → 主控板 → 无线模块 → MQTT Broker → 后端服务 → 数据库 → 预警服务 → 声光报警 / 短信通知 / Web 看板
这个流程画完,每个环节用什么硬件、走什么接口,心里就有数了。跳过这一步行事,焊完板子再想架构,后面大概率拆了重来。整个系统里最容易被低估的是“断电恢复”,车间里三相设备启动、大功率电机频繁启停,电源闪断很常见,数据链路一旦断了不能自动恢复,整套系统在用户眼里就是废的。
2.2 粉尘传感器选型:激光散射方案才是预警系统的默认答案
粉尘浓度传感器按原理分两类,一类是红外散射,一类是激光散射。二者都基于光散射测量颗粒物浓度,但红外传感器光源波长宽、抗干扰差,读数在湿度大的环境里会明显偏高,分辨率通常只有 1 µg/m³ 档,量程也窄,多数在 0~500 µg/m³ 附近。激光散射传感器使用半导体激光器作为光源,单色性好,配合光电二极管和算法的卷积处理,分辨率和重复性都好一截,量程可以做到 0~1000 µg/m³,部分型号还能同时输出 PM2.5、PM10 和 TSP 三组数据。
作业场所粉尘监测预警系统里,传感器部署在车间、仓库、装卸区,环境比室内复杂得多,我一般直接用激光散射方案。常见的 PMS 系列传感器就是这类,串口输出,占空比低,带风扇主动采样,响应时间在 10 秒以内。参数上重点看三个:量程、分辨率、数据输出方式。量程覆盖到 1000 µg/m³ 以上才不用频繁切换;分辨率低于 1 µg/m³ 的模块基本不可用;输出方式最好选 UART 串口,I2C 在长线场景容易受干扰。
还有一点容易忽略:传感器标称浓度是“标准颗粒物质量浓度”,跟作业场所要测的“总尘”或“呼尘”不是一回事。毕设阶段直接用 PM2.5/PM10 作为预警指标完全够用,但如果后面要对接职业卫生限值,就需要在算法里加一个换算系数,或者直接选带 TSP 输出的模块。红外传感器便宜,不是完全不能碰,做课程设计、演示原理可以,做监测预警系统不推荐,湿度稍大读数就飘,现场维护的人会觉得这套系统就是个摆设。
2.3 主控与通信通道:ESP32 直连 WiFi 还是 4G Cat.1,现场条件一票否决
主控板选择相对简单。毕设场景下大多数同学选 ESP32,原因很直接:双核、主频高、自带 WiFi 和蓝牙,ADC 引脚够用,价格也压得下来。ESP8266 更便宜,但单核、GPIO 少,后期要同时接传感器、继电器、蜂鸣器、状态灯的时候捉襟见肘。如果现场点位少、只做单点监测,ESP8266 也能跑;要做多点或者带屏幕,直接上 ESP32,省得第二次换板子重写代码。
通信通道才是真正一票否决的选型点,而且几乎没有后悔药。室内环境有企业 WiFi,ESP32 直连是最省事的方案,数据直接走 MQTT 上云,代码量最小,调试也最方便。但车间不是写字楼,厂房深处、金属货架密集区域、地下室仓库,WiFi 信号衰减非常快,连上了也经常断。这种情况下我一般选 4G Cat.1 模块,插物联网卡,走运营商网络,不依赖现场 WiFi,缺点是每张卡有流量费,代码里还要处理网络注册、拨号、Socket 重连。
如果作业场所面积大、点位分散,还有一种做法是 LoRa 组网:每个监测点用 LoRa 节点把数据发到网关,网关再走 4G 或以太网上云。LoRa 穿透力强,厂房里穿两三堵墙没问题,但网关节点要自己维护,组网复杂度上了一个台阶。毕设阶段通常不需要上 LoRa,ESP32 加 WiFi,或者 ESP32 加 Cat.1 模块,都能把链路讲清楚。
选型阶段还要算好供电。传感器、主控、通信模块加起来电流不小,ESP32 峰值电流能到 300mA 以上,4G 模块在弱信号环境下更夸张。用 USB 供电的毕设演示没问题,真正放现场至少要 12V/2A 的适配器,并通过 DC-DC 降压给主控供电。锂电池供电要看功耗预算,后面第 6 章再展开。
3. 搭建数据链路与预警判定:MQTT 上云、分级阈值、多重报警
3.1 传感器数据读取:PySerial 解析串口粉尘帧,不要自己拼协议
在 PC 或者树莓派上先做数据读取验证,是最稳的起步方式。PMS 系列粉尘传感器是串口输出,波特率常见 9600,数据帧固定 32 字节,帧头是 0x42 0x4D。解析逻辑不复杂,但校验一定要做,否则一条错帧就会让后面的 PM2.5、PM10 全部错位。
import serial import struct ser = serial.Serial('/dev/ttyUSB0', 9600, timeout=2) buffer = bytearray() while True: buffer += ser.read(ser.in_waiting or 64) head = buffer.find(b'\x42\x4d') if head >= 0 and len(buffer) >= head + 32: frame = buffer[head:head + 32] del buffer[:head + 32] # 帧校验:帧头到数据末尾之和,拆成高字节和低字节,与帧尾两字节比对 checksum = sum(frame[:30]) if (checksum >> 8) == frame[30] and (checksum & 0xFF) == frame[31]: # 第 4~5 字节是 PM2.5 标准值,第 6~7 字节是 PM10 标准值,大端序 pm25 = struct.unpack('>H', frame[4:6])[0] pm10 = struct.unpack('>H', frame[6:8])[0] # 浓度单位是 µg/m³ print(f'PM2.5={pm25} PM10={pm10}') else: print('checksum mismatch, skip frame')逻辑说明:先把串口读到的字节累积到 buffer,通过查找 0x42 0x4D 定位帧头。找到后确认缓冲区里至少有 32 字节,再取出一整帧处理。校验和是整个帧前 30 字节的累加和,正确解析后 PM2.5 和 PM10 都是无符号 16 位大端整数,单位是 µg/m³。
参数说明:timeout=2是每次读串口的超时时间,单位秒,设太短容易读不完整,设太长会让循环卡住;ser.read(ser.in_waiting or 64)表示把当前缓冲区里已有的数据全部读出来,如果没有数据就尝试读最多 64 字节,这样脚本在任何平台跑都不会空转。这段代码里的循环是纯 CPU 轮询,精度足够,毕设数据采集不需要上中断。
3.2 数据入库与设备管理:MQTT Broker + FastAPI + MySQL 的最小链路
传感器数据上云,我一般用 MQTT,不直接走 HTTP。原因很简单:MQTT 是发布订阅模式,设备端只负责推送,服务端只需要订阅主题,设备多了也不怕;而且 MQTT Broker 自带会话保持,设备断线重连后能续上消息,HTTP 轮询做不到这个效果。
主题设计按点位和设备区分,例如dust/{device_id}/data,设备端上报 JSON 负载,包含设备编号、PM2.5、PM10、湿度、时间戳。后端订阅这个主题,收到消息后写入数据库,同时交给预警服务做判断。
import json import time import pymysql import paho.mqtt.client as mqtt def save_to_db(payload): # 每次新建连接,简单直接,毕设阶段够用 conn = pymysql.connect(host='localhost', user='iot', password='iot123456', database='dust') try: with conn.cursor() as cur: sql = """ INSERT INTO readings (device_id, pm25, pm10, humidity, created_at) VALUES (%s, %s, %s, %s, %s) """ cur.execute(sql, ( payload['device_id'], payload['pm25'], payload['pm10'], payload['humidity'], time.strftime('%Y-%m-%d %H:%M:%S') )) conn.commit() finally: conn.close() def on_message(client, userdata, msg): payload = json.loads(msg.payload.decode()) save_to_db(payload) # 这里只是把数据写库,预警判定单独走函数,保持逻辑隔离 client = mqtt.Client() client.on_message = on_message client.connect('broker.example.com', 1883, 60) client.subscribe('dust/+/data') client.loop_forever()逻辑说明:on_message回调里做两件事,解析 JSON、写数据库。dust/+/data中的+是 MQTT 通配符,表示匹配任意设备编号,这样新增设备不用改代码。写库用INSERT语句,created_at直接用服务器时间,避免设备端时钟不准导致报表时间错乱。
参数说明:connect的第三个参数 60 是 keepalive 秒数,设备端心跳间隔大于这个值会被 Broker 判定离线,所以 ESP32 上 MQTT 心跳一般设 30~45 秒。loop_forever()是阻塞式网络循环,异常掉线会自动退出,实际部署时外面要再加一层重试逻辑。
这里有个容易被忽略的设计点:数据表和预警记录表要分开。readings只存原始浓度数据,预警表单独记录触发时间、点位、级别、确认状态,这样数据回溯和报警记录互不干扰,答辩时也能说清楚系统设计的分层思想。
3.3 预警判定不能只看单次读数:连续超标、分级阈值与去毛刺
粉尘浓度本身波动大,车间里一台叉车开过、一阵风吹过,都可能让读数瞬间冲高。预警判定如果写成“超过 1000 就报警”,这套系统一天能报几百次,现场的人最后会把报警器拆了。所以判定逻辑要处理两件事:分级和去毛刺。
我一般设三级阈值,对应黄、橙、红三种状态。比如 PM10 浓度超过 400 µg/m³ 触发黄色提醒,超过 700 触发橙色预警,超过 1000 触发红色报警。每级还要配一个“持续确认”规则:最近 5 条数据里至少 4 条超过阈值才真正报警,单次毛刺自动滤掉。
def judge_alert(recent_records, limit, confirm_count=4): if len(recent_records) < 5: return None over = sum(1 for r in recent_records if r['pm10'] >= limit) if over >= confirm_count: return 'alert' return None # 使用示例 records = get_recent_readings('001', minutes=5) level = judge_alert(records, limit=700)逻辑说明:judge_alert接收最近 5 条记录,统计超过阈值的条数,超过 4 条才返回报警信号。这个“5 条里 4 条”的规则并不是拍脑袋定的,它等效于在时间维度上做了一个 80% 占空比的窗口,响应延迟在 MQTT 上报间隔合理的前提下大约 1~2 分钟,既不会漏报,也滤掉了大部分瞬时干扰。
参数说明:limit就是分级阈值,不同点位可以单独配置。比如电焊工位旁边 PM10 背景浓度本身就高,阈值可以适当上调,而办公区、控制室这种清洁区域要从严。confirm_count=4是确认次数,现场粉尘越剧烈,这个值要调得越大,否则还是会被偶发高浓度欺骗。
实际工程项目里还会加一个“报警恢复”状态机:触发报警后,必须连续 3 条数据低于阈值的 80% 才恢复绿色。否则报警状态一直卡在高位,后续再触发新报警会被合并掉。这个逻辑会稍微增加代码量,但演示效果和实际体验都提升一大截。
4. Web 监控大屏与答辩演示:把预警闭环做成评委看得懂的效果
4.1 实时大屏:ECharts 折线加 WebSocket 推送,刷新间隔别瞎取
后端数据有了,预警逻辑有了,现在要把系统“看见”。Web 监控大屏是物联网毕业设计最直观的交付物,看着屏幕上折线实时跳动、颜色从绿变红,评委比看任何文字都容易理解。实时性用 WebSocket 做,不用 HTTP 轮询,浏览器一个连接挂着,服务端推送一条数据更新一次图表。
const recent = []; const chart = echarts.init(document.getElementById('dustChart')); function updateChart(data) { recent.push({ time: new Date().toLocaleTimeString(), pm25: data.pm25, pm10: data.pm10 }); // 只保留最近 200 个点,避免浏览器内存无限增长 if (recent.length > 200) recent.shift(); chart.setOption({ xAxis: { type: 'category', data: recent.map(p => p.time) }, yAxis: { type: 'value', name: 'µg/m³', max: 1200 }, series: [ { name: 'PM2.5', type: 'line', smooth: true, data: recent.map(p => p.pm25) }, { name: 'PM10', type: 'line', smooth: true, data: recent.map(p => p.pm10) }, { // 阈值参考线,黄色预警线 700 name: '预警线', type: 'line', markLine: { silent: true, data: [{ yAxis: 700, label: { formatter: '预警线 700' } }] } } ] }); } const socket = new WebSocket('ws://your-server/ws/dust'); socket.onmessage = function (event) { const data = JSON.parse(event.data); updateChart(data); };逻辑说明:ECharts 的setOption是增量更新,不是全量重绘,所以每次只把新的时间戳和数据塞进去,性能足够。recent.shift()控制数组长度,长时间挂着也不会把浏览器拖垮。预警参考线用markLine画在 700 µg/m³,折线一越线,视觉上立刻有冲击力。
参数说明:图表数据刷新频率取决于 MQTT 上报间隔。传感器主动模式下每秒出一次数据,但不需要每一条都推到浏览器,我一般让设备端每 10 秒上报一次,WebSocket 收到后直接更新,浏览器端不需要再设setInterval。如果用的是被动读取模式,服务端拿到数据就往 WebSocket 广播,前端也不用管轮询。把刷新间隔做成可配置项,演示时能调得更顺滑,平时能调得更省流量,答辩时这个细节也能讲两句。
4.2 历史报表导出:CSV 加 utf-8-sig,Excel 打开不乱码
预警系统不能只看实时画面,还得能回答“昨天下午三点浓度为什么超了”。历史查询和报表导出是这个问题的标准解法。后端用 FastAPI 写一个导出接口,按点位和时间段查询数据库,生成 CSV 返回给前端下载。
import csv import io from fastapi.responses import StreamingResponse @app.get('/export') def export_readings(device_id: str, start: str, end: str): rows = query_readings(device_id, start, end) buffer = io.StringIO() writer = csv.writer(buffer) writer.writerow(['device_id', 'pm25', 'pm10', 'humidity', 'created_at']) writer.writerows(rows) # utf-8-sig 带 BOM,Excel 直接打开不会乱码 return StreamingResponse( io.BytesIO(buffer.getvalue().encode('utf-8-sig')), media_type='text/csv', headers={'Content-Disposition': f'attachment; filename=dust_{device_id}.csv'} )逻辑说明:StreamingResponse把 CSV 内容作为附件流返回,浏览器收到后自动下载,不需要前端生成文件。utf-8-sig编码是给 Excel 用的,直接utf-8导出的文件,Windows 上 Excel 打开中文表头大概率乱码,这个细节不说可能有同学要踩。
表格的主题、字段、格式都是有讲究的。字段里除了 PM2.5、PM10,还要带湿度和时间戳,湿度对粉尘读数有解释作用;文件名带上设备编号和时间范围,归档方便。答辩演示时可以现场导出一次报表,展示 Excel 打开的效果,比口头说“支持历史查询”有说服力得多。
4.3 现场演示脚本:90 秒走完“正常→报警→恢复”全流程
系统做完了,最后一步是设计演示流程。很多同学忽略这个,结果答辩现场传感器读数一直在一个范围波动,等了五分钟都没触发报警,气氛很尴尬。提前规划演示脚本,把整个闭环的效果串起来。
我的做法是准备一个透明亚克力密封箱,传感器放在箱子里,演示时先让读数稳定在 30~50 µg/m³,录一段正常画面。然后点一小段蚊香放进箱子,或者用面粉通过筛网轻轻抖入,粉尘浓度会在 20 秒内快速爬升,达到黄色预警线,触发第一次声光报警。这时不要停,再抖一点面粉,浓度超过红色阈值,短信通知发出来,Web 大屏上的折线越过预警线,颜色变红。最后把箱子打开通风,浓度回落,系统自动恢复绿色。
整个过程大约 90 秒,分三个阶段,正好对应预警逻辑里的黄、橙、红三级。演示的关键点是提前在箱子侧面留一个小孔用于插传感器线缆或者温度探针,不要让粉尘散到答辩教室。另外一个细节:蚊香的烟是气溶胶,粒径和粉尘有差异,但触发传感器足够了,动作干脆,不拖泥带水。演示前至少完整跑两遍,确保报警阈值设置合理,不会出现还没抖面粉就报警的情况。
5. 粉尘监测系统避坑指南:血泪换来的 5 条现场经验,每一条都能毁掉一次演示
5.1 传感器读数负值、零点漂移:校零、预热与软件截断
现象:传感器刚上电时读数直接显示 “-5” 或 “-12”,运行一小时后慢慢回到零附近;或者前一天还正常,第二天开机整体偏高 20 到 30 个单位。
原因:激光散射传感器出厂时内置算法依赖光强基准,上电瞬间激光器和光电二极管没有达到热平衡,配合不同批次器件的一致性差异,就会在零点附近出现负偏。另外传感器镜头上有积尘,也会让零点缓慢漂移。
解决:上行前预热 5 分钟,让激光器达到工作温度;模块上电后清零算法做一次初始校准;软件侧对负值直接截断为 0,避免图表上出现负浓度这种一眼假的数据。如果点位环境灰尘大,还要定期用压缩空气吹镜头,或者干脆换带防尘滤网的传感器外壳。
5.2 湿度一高浓度就爆表:凝露误报怎么区分和规避
现象:连续阴雨天,没有作业活动,系统却在凌晨频繁触发黄色预警,报表里 PM10 数值大面积超过 600,甚至飙到 900 以上。
原因:激光散射原理在空气相对湿度超过 70% 时,水汽会包裹颗粒物,导致颗粒物等效粒径变大、散射截面增强,读数虚高一倍不止。凌晨湿度最大,所以误报集中在夜间。
解决:数据链路里同时接入温湿度传感器,在预警判定逻辑里加湿度补偿,或者湿度超过 75% 时把粉尘读数标记为“受湿度影响”,降低预警级别。还有一种做法是传感器安装在有遮挡、通风良好的位置,不要让雨水或者高湿气流直接吹到传感器风道上。这个坑最大的危害是让人对系统失去信任,误报比不报更致命。
5.3 厂房深处 4G 信号弱:数据延迟和本地缓存
现象:ESP32 加 4G 模块部署在厂房角落,Web 大屏上数据每隔几分钟才更新一次,点击“实时刷新”时长时间没有新数据。
原因:厂房金属结构对无线信号屏蔽严重,4G 模块进入弱信号区,上行数据重传次数增加。更隐蔽的是通信模块在弱信号下会主动降低发射功率,导致连接频繁超时。
解决:优先给通信模块接外置吸盘天线,天线吸在窗户玻璃上或者厂房立柱顶棚,效果立竿见影。如果信号依旧差,换用 Cat.1 模块里接收灵敏度更高的型号。数据链路侧加本地缓存,主控板每读到一组数据就写入 SD 卡,网络恢复后再补传,这样即使断网半小时,数据也是完整的。
5.4 粉尘毛刺触发连环报警:滑动窗口与确认周期
现象:运行一星期后,系统在中午和傍晚各触发一次红色报警,但现场当时并没有明显扬尘,工人也反馈没看到异常。
原因:粉尘浓度的瞬时脉冲,比如一辆叉车驶过卷起的地面积尘,或者附近有电焊烟尘飘过,单次读数瞬间超过阈值。如果预警逻辑是“单条数据超阈值就报警”,这种毛刺就会直接触发。
解决:把单点判断改成滑动窗口,最近 5 条数据里至少 4 条超过阈值才报警,报警后还要连续 3 条低于阈值的 80% 才恢复。之所以设置“恢复延迟”,是因为浓度下降和粉仓沉降都需要时间,瞬时回落后又立刻升高的情况很常见。
5.5 断电重启后数据错乱:时钟丢失与数据去重
现象:现场偶然断电,系统来电重启后,Web 大屏的时间轴出现凌晨一点、上午十点交替跳动,报表里同一分钟出现两条相同数据。
原因:主控板没有实时时钟芯片,重启后时间从编译固件时的初始时间开始跑,或者干脆用了复位时间;同时 MQTT 会话恢复后,Broker 里积压的旧消息和新消息一起被消费,导致重复入库。
解决:设备端增加时间校准,重启后通过 MQTT 向服务端请求一次 NTP 时间,再用这个时间戳标记数据;数据库里对设备编号和设备时间戳做联合唯一索引,重复消息提交时直接跳过。这个坑最容易在最后一周被翻出来,因为平时都在实验室调试,根本没经历过断电。
6. 验证与升级:把“演示能跑”变成“连续运行不掉链子”
6.1 对标测试与修正系数
系统能跑通闭环只是第一步,真正要说服评委和现场使用人员,要用数据说话。找一个手持式粉尘浓度仪作为参考,把两个设备放在同一位置,连续采集一小时,每 10 秒记录一组读数,然后做线性回归。
| 采集时间段 | 参考仪器 PM10 (µg/m³) | 自建节点 PM10 (µg/m³) | 偏差百分比 |
|---|---|---|---|
| 10:00-10:10 | 45 | 52 | 15.6% |
| 10:10-10:20 | 78 | 83 | 6.4% |
| 10:20-10:30 | 156 | 162 | 3.8% |
| 10:30-10:40 | 320 | 341 | 6.6% |
| 10:40-10:50 | 540 | 572 | 5.9% |
低浓度段偏差大是正常现象,激光散射传感器在颗粒物稀少时光子计数统计涨落更明显。把参考仪器的读数作为纵轴,自建节点的读数作为横轴,求一个线性拟合系数,得到y = a*x + b,把系数写进服务端,每次入库前先修正再判断阈值。修正后系统读数与参考仪器的偏差能控制在 10% 以内,这个结论直接写进答辩报告里,比“做了个系统”有说服力得多。
6.2 低功耗与多监测点联动
下一步升级方向是低功耗。当前方案里传感器风扇和通信模块是耗电大户,测量周期改成测量 30 秒、休眠 300 秒,平均电流能降一个数量级,锂电池加太阳能板就可以支撑。如果作业场所没有市电,这个方案就是唯一解,也正好接上“无源物联网”的方向,把环境能量采集和低功耗传感结合起来,是连续两年国赛和毕业论文里的热门选题。
多监测点联动也是一条可走的路。每个粉尘监测点独立报警已经够用,但把 10 个点位的浓度空间分布叠加到一张厂区地图上,就能判断粉尘是从哪个工位扩散出来的,这已经从“监测预警系统”延伸到了“溯源分析系统”,工作量不大,展示效果却上了一个档次。
回看这个系统,最大的教训是不要等到答辩前一周才做联动调试,传感器、通信、后端、前端每一个环节单独都正常,连起来才暴露问题,而这些问题的排查耗时远超预期。把它当成一个完整的交付物来做,每连接一层就验证一层,现场演示时才不会掉链子。希望帮到你。
本文还有配套的精品资源,点击获取