尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SpringBoot+Vue+MySQL实现贸易行业CRM系统:从设计到部署全流程解析

SpringBoot+Vue+MySQL实现贸易行业CRM系统:从设计到部署全流程解析 做毕业设计选CRM系统的人十个里有八个会搜到一堆XX管理系统源码但下载下来能跑起来的少能跑起来还能写进论文、能通过答辩的更少。我当年做这套贸易行业CRM系统的时候主选型就是SpringBoot Vue MySQL前后端分离最后源码、数据库、论文、部署文档一体交付。现在回头看这套系统之所以没烂尾核心不在于代码多炫而在于每个设计点都能说清楚为什么这么干。这篇就围绕这套系统把从数据库建模、后端权限、前端联调到部署上线的完整链路讲透全是实操里趟过来的经验。1. 技术栈选型为什么是SpringBootVueMySQL这个组合而不是别的1.1 前后端分离架构在毕业设计里的天然优势很多同学在做选题时会纠结到底是JSPServlet老一套还是直接用若依之类的脚手架改一改我当时的判断很简单——这套系统是要给贸易公司的销售团队用的不是给自己交差用的那就得按企业级标准来做。SpringBoot负责后端接口Vue负责页面交互MySQL存业务数据这三者是目前中小型企业内部系统最主流的组合理。前后端分离的好处体现在开发效率上后端定义好接口文档前端可以并行开发。我在实际操作中把接口路径全部按/api/customer、/api/order、/api/contact这种资源化方式命名前端用axios封装统一请求入口。Vue这边用Vite初始化工程相比Webpack那套冷启动速度和热更新体验都好得多。这对毕设党来说很关键因为调试时间越短留给你写论文的时间越多。1.2 为什么不用更高级的框架和中间件组合我在做技术选型时主动砍掉了几样东西一是微服务二是Redis缓存三是消息队列。不是说这些东西没用而是贸易CRM这个场景根本不需要。一个中小型贸易公司用户量可能就几十人数据量最多几十万条单体架构完全能扛住。硬上微服务反而会让毕业设计变成一堆你驾驭不了的组件的拼凑。但有两个组件我必须用上MyBatis-Plus和Spring Security。MyBatis-Plus解决的是CRUD代码冗余问题内置的分页插件和字段自动填充能大幅减少工作量Spring Security负责登录认证和权限控制这是CRM系统能不能称为系统的分水岭。贸易行业还有个特点——销售手里有客户资料、价格信息权限必须分清楚否则系统上线就是事故。1.3 这套技术栈对找工作/考研复试的实际价值选技术栈还有一个私心SpringBoot和Vue在招聘市场是高频技能点。做完这套系统IPO、AOP、自动配置原理、常见注解这些都是后端面试的必问题。Vue的响应式原理、组件通信方式、路由守卫前端面试也会问。MySQL的事务隔离级别、索引优化、慢查询排查数据库面试更是跑不掉。一套毕设把这三块全部覆盖面试时讲我做过和讲我了解说服力完全不一样。2. 先从数据库设计说起贸易行业CRM的7张核心表和关键字段2.1 贸易业务场景的特殊性如何体现在表结构上做贸易CRM系统最忌讳的就是拿网上通用的客户管理表直接抄。贸易公司的客户有鲜明的业务特征客户可能是海外买家也可能是国内供应商交易涉及币种、汇率、贸易术语FOB、CIF、EXW这些业务员跟进一笔订单往往要跨多个时区。所以我在设计表时重点考虑了这些字段客户主表t_customer除了公司名称、联系人、电话这些基础信息外增加了customer_type区分进口商/出口商/代理商、country国家/地区、currency_type默认结算币种、payment_terms付款条件如T/T、L/C、credit_line授信额度这几个贸易专属字段。订单表t_order必填字段包括order_no订单编号用日期流水号生成、trade_term贸易术语、currency成交币种、exchange_rate成交时汇率、total_amount按币种存储的原币金额、total_amount_cny换算成本币金额的冗余字段。这样设计的好处我论文里花了整整一节来讲按币种存储金额可以保留业务原貌增加本币换算字段方便财务报表统计。在答辩时评委问为什么不做三张表关联换算你能答出冗余存储的使用场景这就是加分项。2.2 从客户到订单一对多关系的落地方式贸易行业的业务链路通常是销售录入客户 → 建立联系人 → 创建报价单 → 成交后转订单 → 后续做跟单记录。所以在数据库里我设计了一套完整的表关系客户表与联系人表一对多一个客户下挂多个联系人联系人的is_primary字段标记是否为默认联系人。客户表与报价单表一对多报价单里有version字段记录第几次修订报价。报价单与订单表一对一转化关系报价单确认后可以一键生成订单草稿quote_id在订单表里做外键关联。订单表与跟单记录表一对多每次与客户的沟通、发货、回款都追加一条记录。这个结构在E-R图上非常清晰。我当时画E-R图用的工具是draw.io导出矢量图放进论文里答辩PPT里也直接用了这张图。别小看这种图的价值论文评审老师拿到一篇带规范E-R图的论文和一篇纯文字表结构的论文印象分完全是两个档次。2.3 数据字典和状态字段的规范化处理做数据库设计最容易忽略的是状态字段该用什么类型。我见过有人用status直接存未开始/进行中/已完成这种中文查询没问题但一旦业务调整、状态增多程序里到处都是硬编码判断维护极难。我用的方案是状态字段全部存数字0/1/2...程序里定义枚举类。比如订单状态0-草稿1-已确认2-履约中3-已完成4-已取消。代码里写OrderStatusEnum.CONFIRMED.getValue()去设置和判断。数据库层面再配合TINYINT类型控制长度索引空间小查询也快。另外业务表基本都带create_by、create_time、update_by、update_time、deleted逻辑删除这五个字段。我用MyBatis-Plus的MetaObjectHandler做了字段自动填充插入时自动填create_time和create_by更新时自动填update_time这样业务代码里就不用反复写这五行set了。2.4 初始化SQL脚本里的数据分级别一上来给全世界密码一套合格的毕业设计源码附带的SQL脚本应该分级分类。我在交付文档里放了三个脚本schema.sql建库建表、data_dict.sql数据字典表和数据初始化、data_demo.sql演示数据含测试账号和模拟业务数据。这里有个很实用的技巧演示数据里的密码不能用明文我用BCrypt加密后存入并在部署文档里注明测试账号密码都是admin123。这样既保证了演示效果又不会让使用者误把弱口令当成设计的一部分。3. 后端落地登录鉴权、通用CRUD和核心业务接口的完整实现思路3.1 认证方案自己写JWT拦截器还是用Spring Security这是后端开发第一个分岔路口。我的做法是用Spring Security框架做底层过滤器链配合JWT做无状态Token认证但把大量配置简化掉了。为什么不自己写拦截器因为手工实现认证逻辑有太多边角问题密码加密方式BCrypt、Token过期刷新、接口白名单、匿名访问控制、CSRF防护……这些代码自己从头写一周都未必能写完而且容易出漏洞。Spring Security把这些处理成了一条清晰的过滤器链你只需要定制自己的SecurityFilterChain里的规则就行。核心配置逻辑大致是Bean public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http.csrf().disable() .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) .and() .authorizeHttpRequests(auth - auth .requestMatchers(/api/auth/login, /doc.html, /webjars/**).permitAll() .requestMatchers(/api/admin/**).hasRole(ADMIN) .anyRequest().authenticated() ) .exceptionHandling().authenticationEntryPoint(jwtAuthenticationEntryPoint) .and() .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class); return http.build(); }JWT过滤器的工作逻辑是从请求头Authorization里取Token → 解析出用户名 → 查数据库得到用户信息和权限列表 → 封装成Authentication对象放进SecurityContextHolder。这样Controller层只需要通过AuthenticationPrincipal就能拿到当前登录用户非常方便。3.2 基于RBAC的权限控制用户、角色、菜单、接口四个维度贸易公司里角色区分很明确老板要看全量数据销售经理要看自己团队的数据销售只能看自己的客户和订单财务只能看回款和发票。我把权限控制分成三层第一层是接口路径权限通过PreAuthorize(hasAuthority(customer:add))等注解控制。第二层是菜单显示权限前端根据当前用户拥有的权限标识动态生成侧边栏菜单。第三层是数据范围权限这是最容易被忽略的同一张t_customer表普通销售执行查询时只能返回create_by 当前用户ID的数据销售经理需要额外查下属的数据我用MyBatis-Plus的DataPermissionInterceptor做数据权限拦截自动拼接子查询条件。在数据库层面表结构是t_user用户、t_role角色、t_user_role用户角色关联、t_menu菜单/权限点、t_role_menu角色菜单关联。这套RBAC模型是CRM类系统最标准的权限模型论文里单开一节写完全够分量。3.3 核心业务接口的设计模式报价转订单的完整链路CRM系统里最有业务含金量的功能就是报价转订单这个流程设计好了论文的核心创新点就出来了。我实现的逻辑是销售在报价单页面点转订单前端携带quoteId调用后端接口。后端校验报价单必须处于已确认状态、未过期valid_until晚于当天、操作人拥有order:add权限。通过后系统从报价单表读取商品明细报价单头报价单明细两张表拷贝生成订单头和订单明细草稿把quote_id写入订单表同时把报价单状态更新为已转订单。返回生成的orderId前端跳转到订单编辑页销售补充实际成交信息后提交。这整个过程我建议用Transactional包裹防止出现报价单已转订但订单生成失败的数据不一致问题。实际上我在开发中就踩过这个坑第一次没加事务测试时连续点了两次转订单结果生成了两个订单草稿。后来在Service层加了事务并且在接口入口加了一个重复提交校验Redis分布式锁但为了简化学士论文我改成数据库唯一索引quote_id在t_order表里唯一重复提交直接报唯一键冲突问题就彻底解决了。3.4 通用响应体和统一异常处理告别堆if-else的接口后端接口返回格式我用一个统一结果类ResultT来包装{ code: 200, message: success, data: { ... } }所有接口无论成功失败都返回这个结构前端的axios拦截器统一处理。如果后端某个接口直接抛NullPointerException全局异常处理器RestControllerAdvice会捕获并返回code:500前端Toast弹窗提示系统异常请联系管理员。这样前端永远只需要关注code和data两个字段逻辑简单很多。全局异常处理里我细分了四种场景业务异常BusinessException如订单已取消无法修改、参数校验异常Valid触发、认证授权异常Token无效/无权限、系统未知异常。这在异常信息上做到可控、可解释答辩时如果评委问系统安全性怎么保证的你就可以把异常处理链路拿出来讲。4. 前端Vue工程从登录页到动态路由联调时最磨人的几个问题4.1 工程初始化和目录结构Vite Vue3 Element Plus Pinia前端的技术选型我直接用了Vue3的组合式API没用Vue2的Options API。Vite创建工程后我把目录拆成了api请求封装、assets静态资源、components通用组件、router路由、store状态管理、utils工具函数、views页面七大类。状态管理用的Pinia。登录后把Token存在localStorage里同时把用户信息和权限标识列表存到Pinia的userStore里。页面刷新时Pinia数据会丢失所以我在App.vue的onMounted里调了一次getUserInfo接口从Token里找回用户信息重新填充Pinia。这个刷新后恢复登录态的逻辑就是毕设演示时最容易露怯的地方提前处理好演示时关掉页面重开都不会掉登录。4.2 封装axios请求和响应拦截器让所有接口都自动携带Tokenaxios封装这一步值得细写因为它直接影响整个前端的开发体验。我在utils/request.js里做了一层封装所有用到的HTTP方法都经由这个封装const service axios.create({ baseURL: import.meta.env.VITE_API_BASE_URL || /api, timeout: 15000 }) service.interceptors.request.use(config { const token localStorage.getItem(token) if (token) { config.headers.Authorization Bearer ${token} } return config }) service.interceptors.response.use( response { const res response.data if (res.code 200) { return res.data } if (res.code 401) { localStorage.removeItem(token) router.push(/login) } ElMessage.error(res.message || 请求失败) return Promise.reject(new Error(res.message)) }, error { ElMessage.error(error.message || 网络异常请检查后端服务) return Promise.reject(error) } )统一拦截的好处是每个业务页面调接口时只需要写request.get(/customer/list, { params })完全不用关心Token和错误提示。这套封装让新增一个页面的成本降到了最低我在实际开发中一天能写完两个业务页面的前后端联调。4.3 动态路由和菜单权限不同角色登录后看到的界面完全不同动态路由是前端权限控制的核心。我初始化路由表只注册三个公共路由/login、/404、/403。用户登录成功后前端根据后端返回的权限标识如customer:list、order:add从前端预先定义好的完整路由映射表constantRoutes中过滤出当前用户可以访问的路由再用router.addRoute()动态注册。这样每个用户登录后访问的菜单和页面URL都不一样。这里有个很实用的经验过滤逻辑一定在前端做还是后端做我的方案是前端做菜单过滤后端做接口权限校验。前端只管显示什么后端管理能不能操作。因为如果前端把能访问的接口都返回给用户那就是把安全交到了客户端手里这是初学者常犯的错误。正确做法是后端接口有独立的权限校验前端动态路由只是提升体验、避免无关按钮出现真正的安全防线永远在后端。4.4 表格分页搜索、数据导出和ECharts看板论文里最能出彩的页面CRM系统核心页面一定包含客户列表和订单列表这两个列表我统一实现了四个能力条件搜索关键字搜索下拉筛选、分页后端分页前端传递pageNum和pageSize、排序按创建时间倒序、导出后端用EasyExcel导出Excel。导出功能别在论文里简单写实现了导出要写清楚实现方案前端点击导出按钮后端根据当前的搜索条件重新查询符合条件的数据生成Excel后通过HTTP流式返回。这个方案能保证导出的数据和当前列表页筛选结果一致而不是把全表数据导出。数据看板是给管理层看的我用了ECharts画了四个图表客户地区分布环形图、月度成交金额折线图、销售业绩排名横向柱状图、订单状态占比饼图。数据来源是后端封装好的统计接口SQL里用GROUP BY和聚合函数。ECharts这块在论文里的系统实现部分放两张截图效果比一堆文字描述强得多。4.5 联调阶段最坑的三个问题跨域、404、路由回退前后端联调时我遇到最多的问题就三个第一个是跨域。后端跑了8080端口前端跑了5173端口。开发模式下我在Vite的vite.config.js里配置了代理把/api开头的请求转发到http://localhost:8080这样浏览器看到的请求是同源的跨域问题在开发环境就不存在了。部署时后端接口通过Nginx的location /api反向代理到内网服务的8080端口生产环境也就没有跨域问题。前后端分离项目里这套代理配置是标配也是答辩时评委大概率会问的点。第二个是刷新404。Vue是单页应用路由用的是history模式刷新页面时浏览器按路径请求服务器资源如果Nginx没做回退配置就会返回404。解决办法是在Nginx配置里加上location / { try_files $uri $uri/ /index.html; }第三个是路由回退时页面白屏。这是因为菜单里面写死了当前路由但动态路由还没加载完就开始渲染。解决方法是加一个全局前置守卫在router.beforeEach里判断当前用户是否存在如果存在但没有动态路由就先把路由加载完再放行next({...to, replace: true})。5. 部署上线源码能跑起来只是第一步部署文档里必须交代清楚的五件事5.1 本地部署的三要素检查JDK版本、Node环境、MySQL版本很多同学在交付毕业设计时会写一句本地运行请参考部署文档但部署文档写得太简略老师拿到手根本跑不起来。我自己写部署文档时第一条就明确列出环境要求JDK1.8及以上我用的是JDK8因为SpringBoot 2.x对JDK8支持最稳Maven3.6及以上Node.js16及以上Vite 4要求Node 16MySQL5.7或8.0我用的是8.0注意MySQL 8.0的默认认证插件是caching_sha2_password驱动要用mysql-connector-java8.0本地部署最省心的是用IDEA直接打开后端工程等Maven把依赖拉完第一次会比较久然后修改application.yml里的数据库连接信息和Redis连接信息启动CrmApplication类前端在npm install之后运行npm run dev浏览器打开http://localhost:5173就能看到登录页。5.2 通过Maven打jar包文件上传路径和日志路径的正确姿势后端部署到服务器最省事的方案是打成可执行jar包用java -jar xxx.jar启动。但有几个配置必须提前想清楚一是文件上传路径。CRM系统里客户可能上传营业执照、合同附件不能把文件存到jar包所在目录的/static下因为jar包是只读的。我在application.yml里配置了一个动态上传路径file: upload-path: /data/crm/upload/代码里用一个FileConfig读取这个配置拼接成绝对路径存储文件同时在WebMvcConfig里加一个静态资源映射把/upload/**映射到这个目录。这样jar包和文件存储完全解耦升级系统时不会丢失历史附件。二是日志配置。默认的SpringBoot日志只会输出到控制台服务器重启后日志就没了。我在部署文档里建议使用logback-spring.xml做日志配置按天滚动生成日志文件保留30天。排查问题的时候crm-info.log看业务日志crm-error.log看异常堆栈效率比翻控制台高得多。5.3 前端打包后如何用Nginx托管以及反代后端的完整配置前端部署相对简单npm run build之后会在dist目录生成静态文件把整个dist目录上传到服务器的/data/crm/frontend/目录下然后在Nginx站点配置里指向这个目录。这里有一个非常关键的细节Vite的base配置。默认情况下Vite构建出的资源路径是绝对路径/assets/xxx.js如果你的项目部署在域名的根路径下没问题但如果是放在子路径如http://ip:8080/crm/下就必须在vite.config.js里设置base: /crm/。我在部署文档里默认让前端访问根路径避免这个坑。Nginx配置如下server { listen 80; server_name 你的服务器IP或域名; # 前端静态资源 location / { root /data/crm/frontend; index index.html; try_files $uri $uri/ /index.html; } # 后端接口反向代理 location /api/ { proxy_pass http://127.0.0.1:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } # 文件上传访问 location /upload/ { alias /data/crm/upload/; } }5.4 MySQL初始化脚本导入的顺序问题和时区问题部署文档里最容易忽略的一个步骤是数据库初始化。很多同学把SQL脚本一股脑塞给用户但脚本里可能已经包含了建库命令用户拿Navicat运行时如果没选目标库就会报Unknown database错误。我交付文档里写得很清楚先用CREATE DATABASE crm_system DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;建库然后双击这个库再运行schema.sql最后再按顺序运行data_dict.sql和data_demo.sql。MySQL连接串上也有个大坑时区问题。如果application.yml里写的是jdbc:mysql://localhost:3306/crm_system启动项目后时间字段可能比本地时间少8小时。解决办法是连接串上明确指定时区url: jdbc:mysql://localhost:3306/crm_system?useUnicodetruecharacterEncodingutf8mb4serverTimezoneAsia/Shanghai5.5 服务器部署的systemd服务配置实现开机自启和进程守护生产环境不建议用nohup java -jar这种裸奔方式管理后端进程。我一般在部署文档里附上systemd服务配置把SpringBoot jar包注册成一个系统服务[Unit] DescriptionCrm System Server Afternetwork.target mysql.service [Service] Typesimple Userroot WorkingDirectory/data/crm ExecStart/usr/local/java/bin/java -jar /data/crm/crm-server.jar --spring.profiles.activeprod Restartalways RestartSec10 [Install] WantedBymulti-user.target配置完成后systemctl daemon-reload然后systemctl start crm启动systemctl enable crm设置开机自启。这样做的好处是进程意外挂掉时systemd会自动拉起服务器重启后服务也会自动运行。这段配置写进部署文档里整篇文档的专业性立刻上一个台阶。6. 论文写作和答辩演示源码能跑通之后怎么把工作量讲成亮点6.1 论文结构怎么搭从选题背景到系统测试的逻辑链条写论文最容易犯的毛病是把系统代码照搬进文档大段粘贴类名和方法名。正确的做法是围绕业务需求→技术选型→系统设计→系统实现→系统测试这条线展开。我当时论文的目录大致是绪论背景、意义、国内外研究现状相关技术介绍SpringBoot、Vue、MySQL、JWT系统需求分析功能性需求、非功能性需求、可行性分析系统总体设计系统架构图、功能模块图、数据库设计系统详细设计与实现每个模块的时序图、核心代码、界面截图系统测试功能测试用例表、性能测试结果功能模块图用Visio或draw.io画数据库设计放E-R图和核心表结构系统实现每个功能模块配1-2张截图一段关键代码。测试章节要写具体的测试用例比如输入正确的账号密码→点击登录→跳转到首页预期结果和实际结果对应起来。6.2 答辩演示前必须过一遍的四条演示路径答辩演示是最容易翻车的环节我总结出四条必演的路径第一条是登录和权限演示用管理员账号登录展示功能菜单全貌退出后用普通销售账号登录展示菜单变少、只能看到自己的客户。第二条是客户全流程演示新增客户 → 新增联系人 → 创建报价单 → 报价转订单 → 提交订单 → 查看订单状态。第三条是数据看板演示展示首页ECharts图表说明数据来源和SQL逻辑。第四条是系统管理演示新增一个角色、给角色分配菜单权限、创建一个新用户并绑定角色然后用这个新用户登录验证权限是否生效。这四条路径覆盖了系统80%的功能点答辩时评委随机问任何一个环节你都能迅速演示出来。6.3 评委常问的三个技术问题和参考回答答辩时评委最喜欢问技术细节我整理了三个高频问题问题一你这个系统安全性怎么保证回答思路认证用的是JWT无状态Token密码用BCrypt加密权限控制用了RBAC模型按角色分配菜单和接口权限数据访问层做了数据权限隔离普通用户只能操作自己创建的数据SQL全部使用预编译防SQL注入前端提交的数据有参数校验。问题二数据库为什么这么设计回答思路先讲清楚业务背景贸易公司数据有币种、贸易术语等特殊字段再讲E-R图设计时如何分析实体关系最后强调遵循了三大范式部分字段的反范式存储如冗余本币金额是为了查询性能。这三步走完评委基本就满意了。问题三这个系统哪些地方你还能优化回答思路可以提引入Redis做热点数据缓存增加WebSocket做消息通知引入工作流引擎比如Flowable优化审批流程或者对接企业微信做客户提醒。这既展示了你的思考能力也暴露了系统局限性的边界比硬说系统已经很完善了好得多。6.4 PPT展示的截图规范背景统一、重点标注、连号编号答辩PPT的截图有个小技巧不要截大半个屏幕而是只截核心功能区域用红色框把重点操作区域圈出来。比如展示报价转订单功能时截一张报价单列表页、一张订单创建成功后的提示信息页面底部用文字注释说明流程逻辑。所有截图的尺寸尽量统一窗口缩放比例调成一致这样PPT会显得很整洁。准备一个查不到就准备清单登录页、客户列表、客户详情含联系人、报价单创建、报价转订单、订单列表、订单详情、打印订单、数据看板、用户管理、角色权限分配。截图时建议按模块连号命名如l-login.png、customer-list.png放进论文和PPT时引用路径清晰不会出现插错图的情况。7. 一些做完整套系统之后才想明白的事整套系统从数据库建表到服务器部署前前后后花了大概三周时间白天写代码晚上写论文周末部署上线加录演示视频。做完之后最深的感受是毕业设计真正考察的不是你会不会用某个框架而是你有没有能力把一堆零散的技术组合成一个能解决实际问题的系统。像权限控制这套逻辑单独拎出Spring Security的用法网上一搜一大把但怎么结合贸易公司的角色分工去设计权限模型、怎么让前端菜单跟着后端权限变化、怎么把数据范围从全部人收敛到仅本人这些问题代码书里不会写只有自己从需求出发一步步推演出来。部署也是一样java -jar能启动但让它开机自启、崩溃自愈、日志可查、上传文件不丢这些才是一个软件系统可用和能用的分界线。很多同学以为把源码压缩包发了就完事了实际上部署文档里的Nginx配置、systemd服务脚本、环境变量说明才是让老师能在自己电脑上把系统跑起来的关键。我后来还把这套系统稍作修改把客户表格的字段调整成通用字段又帮两个同学套了一版说明核心架构的扩展性也经得起验证。如果你也正在做类似题目的毕设我给的最实在的建议就是先把数据库表设计清楚再动手写代码写完业务模块后马上补权限和异常处理最后再美化界面和处理部署细节。按照这个顺序走每一步的产出都能对应到论文的某个章节写论文的时候你就不会觉得是在硬凑字数了。遇到不会的功能不要慌先去官方文档查再去博客搜别人的踩坑记录这两个动作能解决八成的问题。剩下的两成就靠你自己一行一行调试了而这个过程本身就比任何源码都值钱。
返回列表