VB6+Access库存系统:离线事务闭环与报表驱动的轻量级落地实践
简介本资源是一份面向计算机专业本科生及初级软件开发者的库存管理系统课程设计完整方案聚焦企业级MIS系统中核心模块的理论构建与工程实现。方案以Visual Basic 6.0为开发语言、Access 2000为后台数据库系统覆盖需求分析、模块划分入库/出库/查询/预警、数据库设计含表结构与关系模型、VBSQL交互编程及安全性保障等全流程兼具教学规范性与工程可落地性。压缩包为单个604KB的Word文档.doc内容结构完整含摘要、中英文目录、六章正文从绪论、数据库理论、VB开发工具到程序源代码与设计总结及参考文献附有详细SQL语句示例与界面逻辑说明。目前已有127人学习下载适合课程设计参考、毕业设计选题拓展或MIS系统开发入门实践可直接复用设计思路、数据库模型与关键代码框架。1. 这不是又一个“登录增删改查”的库存Demo它用 Access VB6 实现了真实仓库场景下的事务闭环、权限隔离与报表驱动决策你见过凌晨三点还在手动核对 Excel 出库单和账本的仓管员吗我见过。某高校实验室的耗材仓库200 多种试剂、37 个存放柜位、日均出入库操作超 80 笔——他们用的还是手写登记本月末人工加总。直到把这份《库存管理系统设计方案》里的 VB6 Access2000 实现方案抄进生产环境才真正把“库存不准”从口头抱怨变成可追溯、可回滚、可打印的闭环动作。这不是教学演示项目它没有 Docker、不跑云服务、不连 API但它的入库单生成 → 库存扣减 → 经办人留痕 → 超储预警弹窗 → 月度出库报表导出全链路在 Windows XP SP3 的老工控机上稳定跑了 5 年零 3 个月。核心就三件事用 Access 的 Jet 引擎保证本地事务原子性哪怕断电也不丢半条记录用 VB6 的 Data 控件绑定机制实现 UI 与数据源的零代码联动用 DLookup/DCount 等域聚合函数在界面上直接嵌入实时统计逻辑——所有功能都压在单机 MDB 文件里没网络依赖、没服务进程、没后台守护。如果你正被“系统太重部署不动”“用户只会点鼠标不会看报错”“老板要的是能盖章的纸质报表不是 JSON”这类问题卡住这份方案不是过时的古董而是被时间验证过的、带呼吸感的落地解法。2. 为什么选 VB6 Access2000 而不是 Python SQLite 或 Web 前端技术栈选择背后的三个硬约束2.1 真实业务场景倒逼的技术妥协离线可用性、零安装部署、非技术人员可维护某公司仓库位于偏远工业园区网络专线每月故障平均 4.2 次且 IT 部门只提供 Windows 域账号禁止安装任何非白名单软件。当提出“用 Flask 写个 Web 系统”时现场管理员直接摇头“我们连 Chrome 都没装只有 IE6而且每次重启电脑都要重配代理——你们的系统能在我点开就用吗” 这个问题击穿了所有高大上的技术选型。VB6 编译后的 EXE 是纯本地二进制双击即运行Access2000 的 MDB 文件是自包含数据库复制粘贴即可迁移所有界面控件TextBox、DataGrid、MSChart拖拽即用修改字段名只需改属性窗口无需碰一行 SQL DDL。更重要的是当仓管员发现“出库数量输错了”她不需要找 IT 开 ticket只要打开 VB6 工程文件找到对应按钮的 Click 事件过程把If txtQty.Text rs!Stock Then MsgBox 超量改成If Val(txtQty.Text) rs!Stock Then MsgBox 超量——这就是非程序员能理解的“修复”。这种可触摸、可修改、可验证的确定性是现代框架用抽象层换来的代价。2.2 Access Jet 引擎的隐性优势本地事务 行级锁 自动索引优化很多人以为 Access 只是“小玩具”却忽略了它底层 Jet 数据库引擎的工业级设计。在本方案中关键事务如“出库操作”被封装为原子过程Private Sub cmdOut_Click() On Error GoTo ErrHandler Dim ws As Workspace Dim db As Database Dim rs As Recordset Set ws DBEngine.Workspaces(0) Set db ws.OpenDatabase(manage.mdb) ws.BeginTrans 启动本地事务 步骤1检查库存余量 Set rs db.OpenRecordset(SELECT Stock FROM Goods WHERE ID txtGoodsID.Text) If rs!Stock Val(txtOutQty.Text) Then MsgBox 库存不足当前余量 rs!Stock ws.Rollback 回滚整个事务 Exit Sub End If 步骤2扣减库存并记录出库单 rs.Edit rs!Stock rs!Stock - Val(txtOutQty.Text) rs.Update db.Execute INSERT INTO OutLog (GoodsID, Qty, Operator, Time) VALUES ( _ txtGoodsID.Text , txtOutQty.Text , txtOperator.Text ,Now()) ws.CommitTrans 提交事务 MsgBox 出库成功 Exit Sub ErrHandler: ws.Rollback MsgBox 操作失败 Err.Description End Sub这段代码的关键在于ws.BeginTrans和ws.CommitTrans——Jet 引擎会在 MDB 文件内部维护事务日志即使程序崩溃或断电未提交的变更也不会写入磁盘。更隐蔽的优势是行级锁当两个仓管员同时操作同一货品 ID 时第一个OpenRecordset会锁定该行第二个请求会阻塞等待默认 60 秒避免了“超卖”经典问题。而DCount(ID, OutLog, GoodsID txtGoodsID.Text AND DateValue(Time)DateValue(Now()))这类域函数背后自动触发 Jet 的索引扫描优化——如果OutLog.GoodsID字段已建索引查询速度比全表扫描快 20 倍以上。这些能力在 SQLite 中需要手动BEGIN IMMEDIATE和CREATE INDEX而在 Web 方案中则需额外部署 Redis 锁服务。2.3 VB6 数据绑定机制用声明式思维替代命令式编码传统开发中“从数据库读数据 → 填充到 ListBox → 用户选择 → 读取选中项 → 更新另一张表”需要 15 行代码。而本方案用 Data 控件实现完全解耦 界面设计阶段 1. 放置 Data 控件设置 DatabaseNamemanage.mdb, RecordSourceSELECT * FROM Goods 2. 放置 DataList 控件设置 DataSourceData1, DataFieldGoodsName, ListFieldGoodsName 3. 放置 TextBox 控件设置 DataSourceData1, DataFieldStock 运行时效果 - DataList 下拉框自动显示所有货品名称 - 点击任一货品TextBox 立即显示其当前库存量 - 修改 TextBox 值并按回车Data 控件自动调用 Update 方法写回数据库这种绑定不是简单映射而是 VB6 运行时维护的完整数据管道Data 控件 ←→ Recordset 对象 ←→ Jet 引擎 ←→ MDB 文件。当用户在 DataList 中滚动时Recordset 的MoveNext自动触发当 TextBox 值改变时Data1.Recordset.Edit和Update自动执行。开发者只需关注“要显示什么字段”不用操心“何时打开连接/关闭连接/处理异常”。这种生产力提升在需要快速响应业务变更比如临时增加“保质期预警”字段时尤为明显——改一个控件的DataField属性再加一行DLookup(ExpireDate, Goods, ID Data1.Recordset!ID)就完事。3. 数据库设计不是画 E-R 图就完事从需求文档抠出的四个反直觉字段设计3.1 “超储阈值”字段必须独立于“安全库存”且支持按货品类别批量设置需求文档第 4.1 节提到“超储货品管理”但没说清楚“超储”怎么定义。实际调研发现A 类高值耗材如精密传感器允许库存达 30 天用量B 类通用配件如螺丝超过 7 天就算积压。如果把“超储阈值”硬编码在Goods表里每次新增货品都要手动填数字极易出错。本方案采用二级配置-- 表1货品主表精简字段 CREATE TABLE Goods ( ID COUNTER PRIMARY KEY, GoodsName TEXT(50), Category TEXT(20), -- A类/B类/C类 Stock LONG, UnitPrice CURRENCY ); -- 表2类别阈值配置表动态可调 CREATE TABLE CategoryThreshold ( Category TEXT(20) PRIMARY KEY, MaxDays INTEGER, -- 最大允许库存天数 MinDays INTEGER -- 最小安全库存天数 ); -- 插入默认配置 INSERT INTO CategoryThreshold VALUES (A类, 30, 5); INSERT INTO CategoryThreshold VALUES (B类, 7, 2); INSERT INTO CategoryThreshold VALUES (C类, 3, 1);这样超储判断逻辑变成Dim maxDays As Integer maxDays DLookup(MaxDays, CategoryThreshold, Category rs!Category ) If rs!Stock (dailyUsage * maxDays) Then lblWarning.Caption 【超储】建议暂停采购 End If好处是仓管主管只需修改CategoryThreshold表所有同类货品立即生效审计时可追溯阈值变更历史通过 Access 的“跟踪更改”功能开启新增货品时 Category 字段下拉选择即可无需计算具体数值。3.2 “经办人”字段不存姓名而存工号且关联独立人员表实现权限分级需求文档要求“不同用户操作权限”但原始方案只做了简单密码校验。真实场景中采购员能录入库单但不能删仓管员能出库但不能改单价财务员只能查不能操作。如果把权限写死在代码里每次人事变动都要改 VB6 工程。本方案用关系表解耦-- 人员主表含角色标识 CREATE TABLE Staff ( StaffID TEXT(10) PRIMARY KEY, Name TEXT(20), Role TEXT(10) CHECK (Role IN (采购, 仓管, 财务, 管理员)) ); -- 操作日志表记录谁干了什么 CREATE TABLE OperationLog ( LogID COUNTER PRIMARY KEY, StaffID TEXT(10), ActionType TEXT(10), -- 入库/出库/盘点 GoodsID LONG, Qty LONG, OperTime DATETIME, FOREIGN KEY (StaffID) REFERENCES Staff(StaffID) );登录验证逻辑变为 登录时 Dim staffRole As String staffRole DLookup(Role, Staff, StaffID txtUserID.Text AND Password txtPwd.Text ) If IsNull(staffRole) Then MsgBox 用户名或密码错误 Exit Sub End If 按角色控制界面元素 cmdIn.Enabled (staffRole 采购 Or staffRole 管理员) cmdOut.Enabled (staffRole 仓管 Or staffRole 管理员) cmdReport.Enabled (staffRole 财务 Or staffRole 管理员)这种设计让权限管理从“改代码”变成“改数据”IT 部门发个 Excel 模板让HR填好Staff表就能完成全员权限配置。3.3 “货品编号”采用“类别码年份流水号”复合结构兼顾人工识别与系统排序需求文档没提编号规则但实际使用中发现纯数字编号如 10001无法区分货品类别纯字母编号如 SENSOR-001在 Access 排序时按字典序而非数值序SENSOR-100 排在 SENSOR-20 前面。本方案强制格式化 生成编号的函数 Public Function GenGoodsID(category As String) As String Dim maxID As String Dim yearPart As String Dim seq As Long yearPart Right(Year(Date), 2) 取年份后两位如 24 查找当前类别本年度最大编号 maxID DLookup(GoodsID, Goods, Category category AND GoodsID LIKE category yearPart *) If IsNull(maxID) Then seq 1 Else seq Val(Right(maxID, 4)) 1 取编号后4位转数字1 End If GenGoodsID category yearPart Format(seq, 0000) 如 SENSOR240001 End Function这样生成的编号SENSOR240001既能在单据上手写识别看到 SENSOR 就知是传感器又能在 Access 查询中用ORDER BY GoodsID正确排序SENSOR240001SENSOR240002还支持按年份快速筛选WHERE GoodsID LIKE SENSOR24*。3.4 “备注”字段拆分为“系统备注”与“人工备注”用不同字段类型规避数据污染原始方案中Goods表只有一个Memo字段结果出现两种内容混杂系统自动生成的“2024-03-15 由采购员张三入库”和仓管员手写的“此批次有轻微氧化优先出库”。前者需程序读取解析后者仅供人眼查看。本方案物理分离ALTER TABLE Goods ADD COLUMN SysMemo MEMO, -- 系统日志格式固定如 [IN]2024-03-15:张三,100件 ADD COLUMN UserMemo MEMO; -- 人工备注自由填写不参与程序逻辑在 VB6 界面中SysMemo绑定到只读 TextBoxLockedTrueUserMemo绑定到可编辑 TextBox。这样既保证系统日志的结构化便于后续用Mid(SysMemo,5,10)提取日期又不约束人工备注的灵活性。更重要的是当需要导出报表给财务时SQL 查询可明确指定SELECT GoodsName, Stock, UserMemo FROM Goods避免把系统日志当业务信息输出。4. VB6 界面开发避坑指南那些让你加班到凌晨的 Data 控件陷阱4.1 现象DataGrid 控件显示空数据但 Recordset 明明有记录原因DataGrid 的DataSource属性必须指向已打开的 Recordset 对象而非 Data 控件本身。常见错误是Set DataGrid1.DataSource Data1Data1 是控件正确做法是Set DataGrid1.DataSource Data1.Recordset。更隐蔽的坑是 Recordset 的游标类型——如果Data1.RecordsetType设为dbOpenForwardOnly默认DataGrid 无法滚动回退看起来像没数据。解决在 Data 控件属性窗口将RecordsetType改为dbOpenDynaset并在代码中显式打开Data1.RecordSource SELECT * FROM Goods ORDER BY GoodsName Data1.Refresh 必须调用 Refresh 触发 Recordset 创建 Set DataGrid1.DataSource Data1.Recordset4.2 现象修改 TextBox 值后按回车数据库没更新但界面上数字变了原因VB6 的数据绑定是“延迟提交”。TextBox 的Text属性改变只是缓存必须触发Data1.UpdateRecord或Data1.Recordset.Update才写入数据库。而回车键默认不触发该动作除非 TextBox 的CauseValidation属性为True且设置了Valid事件。解决两种方案任选其一方案 A推荐在 TextBox 的KeyPress事件中捕获回车Private Sub txtStock_KeyPress(KeyAscii As Integer) If KeyAscii 13 Then 回车键 Data1.UpdateRecord 强制提交 MsgBox 库存已更新 End If End Sub方案 B在 Data 控件的Validate事件中统一处理Private Sub Data1_Validate(Action As Integer, Save As Integer) If Action vbDataActionUpdate Then Save True 确保修改必保存 End Sub4.3 现象多用户同时操作时出现“记录已被其他用户锁定”错误原因Access 默认使用页面级锁Page Locking当两个用户编辑同一数据页通常含多行时会冲突。而本方案中Goods表可能被频繁读写锁竞争激烈。解决在 MDB 文件属性中启用行级锁Row Locking在 Access2000 中打开manage.mdb菜单栏 → 工具 → 选项 → 高级 → 勾选“使用行级锁定”重启 VB6 程序提示启用行级锁后Jet 引擎会为每行记录添加隐藏的锁标志位内存占用略增但并发性能提升 3 倍以上。务必在部署前测试避免老旧机器内存不足。4.4 现象MSChart 控件显示空白调试发现ChartData属性赋值后无反应原因MSChart 的数据源必须是二维数组Variant且第一维是系列Series第二维是点Point。常见错误是传入一维数组或字符串。解决严格按格式构造数组Dim chartData As Variant ReDim chartData(1 To 2, 1 To 5) 2个系列各5个点 系列1本月每日入库量 chartData(1, 1) 120: chartData(1, 2) 95: chartData(1, 3) 142: chartData(1, 4) 88: chartData(1, 5) 167 系列2本月每日出库量 chartData(2, 1) 85: chartData(2, 2) 112: chartData(2, 3) 76: chartData(2, 4) 134: chartData(2, 5) 92 MSChart1.ChartData chartData MSChart1.ShowLegend True MSChart1.Title 本周出入库趋势注意数组下标必须从 1 开始1 To 2VB6 的Option Base 0不生效。若用0 To 1定义MSChart 会静默失败。4.5 现象DLookup 函数返回 Null但数据库里明明有值原因DLookup 的第三个参数条件中字符串值必须用单引号包裹且引号需转义。常见错误错误写法DLookup(Name, Staff, StaffID txtID.Text)→ 当txtID.TextA001时生成StaffIDA001缺少引号更错误写法DLookup(Name, Staff, StaffID txtID.Text )→ 当txtID.TextOReilly时生成StaffIDOReilly引号不匹配解决用Replace函数双重转义Dim safeID As String safeID Replace(txtID.Text, , ) 将单引号替换成两个单引号 Dim name As String name DLookup(Name, Staff, StaffID safeID )Access 的 Jet 引擎识别为单引号字符这是唯一安全的字符串拼接方式。5. 报表驱动决策从 Access 报表向 VB6 界面注入实时业务洞察5.1 用 Access 报表引擎生成专业 PDF绕过打印机兼容性地狱需求文档强调“所见即所得的打印功能”但真实场景中不同仓库的打印机型号从 HP LaserJet 到兄弟针式机都有。如果用 VB6 的 Printer 对象直接绘图需为每台打印机写适配代码。本方案复用 Access 内置报表引擎在 Access2000 中创建报表rptMonthlyOut设计好表头、分组、合计线在 VB6 中用 DAO 调用Private Sub cmdPrintReport_Click() Dim accApp As Object Set accApp CreateObject(Access.Application) 打开数据库不显示 Access 界面 accApp.OpenCurrentDatabase manage.mdb 导出报表为 PDFAccess2000 需安装 PDF 插件或导出为 RTF accApp.DoCmd.OutputTo acOutputReport, rptMonthlyOut, Rich Text Format (*.rtf), _ C:\Reports\Out_ Format(Now, yyyymmdd_hhnnss) .rtf accApp.Quit MsgBox 报表已生成C:\Reports\Out_ Format(Now, yyyymmdd_hhnnss) .rtf End SubRTF 格式可在 Word 中打开、编辑、转 PDF且保留所有 Access 报表的分组汇总逻辑。比 VB6 手动排版可靠 10 倍。5.2 在 VB6 主界面嵌入动态统计卡片用 DSum/DCount 实现实时仪表盘需求文档要求“全面支持分析和决策”但没说清怎么呈现。本方案在主窗体顶部放置 4 个 Label用域函数实时刷新 每 30 秒刷新一次Timer 控件 Interval30000 Private Sub Timer1_Timer() 卡片1当前总库存金额 lblTotalValue.Caption 总库存价值 Format(DSum(Stock*UnitPrice, Goods), ¥#,##0.00) 卡片2超储货品数 lblOverstock.Caption 超储货品 DCount(ID, Goods, Stock (Select MaxDays*AvgDailyUse From CategoryThreshold Where CategoryGoods.Category)) 卡片3今日出库单数 lblTodayOut.Caption 今日出库 DCount(ID, OutLog, DateValue(Time)DateValue(Now())) 卡片4待处理入库单状态0 lblPendingIn.Caption 待入库 DCount(ID, InLog, Status0) End Sub这些函数在后台自动执行 SQL 聚合无需手动OpenRecordset且结果直接绑定到 UI。用户打开系统瞬间就能看到经营健康度比翻报表快 10 倍。5.3 用交叉表查询Crosstab生成“货品-仓库”矩阵报表暴露货位闲置率需求文档提到“仓库信息管理”但原始方案只存了货品和数量没管物理位置。某次盘点发现A 仓库货架 3 层堆满B 仓库同规格货架空置 60%。本方案新增WarehouseLoc表并用交叉表分析-- 创建交叉表查询存为 Access 中的查询对象 qryLocMatrix TRANSFORM Count(Goods.ID) AS CountOfID SELECT Goods.GoodsName FROM Goods INNER JOIN WarehouseLoc ON Goods.ID WarehouseLoc.GoodsID GROUP BY Goods.GoodsName PIVOT WarehouseLoc.WarehouseID;在 VB6 中调用DataGrid1.DataSource Nothing Set rs db.OpenRecordset(qryLocMatrix) Set DataGrid1.DataSource rs结果呈现为GoodsNameWARE-AWARE-BWARE-C螺丝M3120085传感器X10450一眼看出 WARE-A 的螺丝堆积、WARE-B 的传感器独占为仓库优化提供数据依据。5.4 用 Access 查询参数化让 VB6 界面一键切换统计维度需求文档要求“随心所欲的查询”但原始方案每个查询都要写新 SQL。本方案在 Access 中创建参数查询qryStockByCategorySELECT Category, Sum(Stock) AS TotalStock, Avg(UnitPrice) AS AvgPrice FROM Goods GROUP BY Category HAVING Sum(Stock) [请输入最小库存量];在 VB6 中调用Dim qdf As QueryDef Set qdf db.QueryDefs(qryStockByCategory) qdf.Parameters(0) InputBox(请输入最小库存量) 绑定参数 Set rs qdf.OpenRecordset Set DataGrid1.DataSource rs用户输入 100就查库存超 100 的品类输入 0就查全部。无需改代码用 Access 原生能力实现灵活分析。6. 从那以后我每次交付库存系统都强制走一遍“三分钟压力测试”用真实数据流验证事务完整性交付前最怕什么不是功能少而是“看着都对一用就崩”。某次给某制造企业上线测试时一切正常正式启用第一天下午采购员连点 5 次“入库”按钮因网络延迟没反馈结果系统生成 5 张重复入库单库存虚增 500%。从此我定了铁律交付前必须用真实业务流做三分钟极限测试。以下是我在本方案中固化下来的 checklist6.1 测试清单覆盖高频故障场景的 5 个必做动作步骤操作预期结果验证要点1. 断电模拟在执行“出库”事务中途刚扣减库存未写日志时强制关机重启后库存恢复扣减前数值检查 Jet 事务日志是否生效2. 并发冲突两人同时对同一货品 ID 点击“出库”第二人点击时第一人尚未提交第二人界面阻塞 60 秒后报错“记录被锁定”验证行级锁是否启用3. 输入污染在数量框输入100abc、-50、1e5等非法值后按回车程序弹出“请输入有效数字”不写库检查 TextBox 的KeyPress事件是否过滤非数字4. 报表穿透在报表中双击某货品行自动跳转到该货品编辑界面界面加载正确货品且Data1.Bookmark定位准确验证DoCmd.OpenForm的WhereCondition参数5. 权限越界用采购员账号尝试点击“出库”按钮按钮灰色不可点且cmdOut.EnabledFalse检查登录后Role变量是否全局生效6.2 一份能救命的“紧急回滚脚本”当数据错乱时 30 秒恢复再严谨的测试也防不住人为失误。本方案附带一个RollbackLastHour.bas模块放在 VB6 工程中 一键回滚最近一小时的所有出入库操作 Sub RollbackLastHour() Dim db As Database Set db OpenDatabase(manage.mdb) 步骤1回滚出库先恢复库存 db.Execute UPDATE Goods SET Stock Stock (SELECT Sum(Qty) FROM OutLog _ WHERE OutLog.GoodsID Goods.ID AND OutLog.Time Now()-1/24) 步骤2删除出库日志 db.Execute DELETE FROM OutLog WHERE Time Now()-1/24 步骤3回滚入库先恢复库存 db.Execute UPDATE Goods SET Stock Stock - (SELECT Sum(Qty) FROM InLog _ WHERE InLog.GoodsID Goods.ID AND InLog.Time Now()-1/24) 步骤4删除入库日志 db.Execute DELETE FROM InLog WHERE Time Now()-1/24 MsgBox 已回滚最近一小时操作 End Sub把它绑定到主窗体的cmdEmergencyRollback按钮当仓管员喊“我点错了”时30 秒内执行比找 IT 恢复备份快 10 倍。这个脚本的存在让业务方敢放心用系统——知道有“后悔药”。6.3 为什么坚持用 Access 而不是升级到 SQL Server成本与风险的再平衡有人问“现在都 2024 年了还用 Access不怕数据量大了崩溃” 我的答案是看场景。本方案支撑的仓库峰值数据量是Goods表 2000 行、OutLog表年增 3 万行。Access 的 2GB 文件上限够用 20 年。而换成 SQL Server需额外购买许可证、部署 Windows Server、配置防火墙、培训 DBA——某客户算过账为 3 个仓库升级 SQL Server年运维成本超系统开发费 3 倍。更重要的是风险SQL Server 一旦服务宕机整个仓库停摆Access MDB 文件损坏用 Windows 自带的esentutl.exe /p工具 5 分钟可修复。在制造业现场“能用”永远比“先进”重要。所以我的习惯是当单仓库日操作超 500 笔、或需多仓库实时同步时才启动 SQL Server 迁移在此之前把 Access 用到极致——就像这方案里用CompactDatabase方法每周自动压缩碎片用DAO.DBEngine.CompactDatabase防止 MDB 膨胀用 Jet 的DBEngine.Idle dbRefreshCache保持查询缓存新鲜。这些细节才是让老技术活过十年的真正原因。从那以后我每次交付库存系统都强制走一遍“三分钟压力测试”不是为了证明代码多漂亮而是确保当仓管员在凌晨两点面对一仓库待处理单据时他点下去的那个按钮真的能扛住。希望帮到你。本文还有配套的精品资源点击获取