简介基于小程序的农产品自主供销系统源码是面向毕业设计与课程设计的完整实践项目覆盖管理员、农户、用户三角色解决农户与消费者信息不对称的问题。系统采用 Java 开发搭配 JDK1.8、MySQL5.7 与 Tomcat7 部署环境包含首页、个人中心、用户管理、农户管理、产品分类、农产品管理、咨询回复等九大功能模块业务流程完整。压缩包共 1401 个文件体积 19.39MB以 Java 后端、Vue 管理端、小程序 wxml/wxss 页面及数据库脚本为主同时包含设计文档与部署配置导入 mysql 后即可快速运行目录结构清晰便于按模块学习。目前已有 54 人学习浏览。对需要快速搭建农产品电商小程序原型、理解前后端分离开发与小程序发布流程的读者来说这份源码既可作毕设框架也能用于课程项目演练。1. 农产品自主供销小程序源码能直接交毕设的完整工程每年毕业季都有人拿着“基于小程序的农产品自主供销小程序源码”来找我帮看说怕下载下来跑不起来。这套资源包含 Java 后端、微信小程序前端和配套的毕业设计 LW 文档是计算机毕业设计或课程设计场景下非常典型的一类完整工程。它不是网上那种只有首页和登录页的残次品而是覆盖了农户发布农产品、买家发布需求、系统按分类和地区做双向匹配、订单状态流转、个人中心管理这一整条业务闭环。适合正在赶毕设需要快速跑通演示的学生也适合刚学 Spring Boot 想找完整项目练手的人。下面我按自己拆项目的习惯从技术栈、表结构、核心逻辑、部署排错到加分优化一层一层说清楚。2. 技术栈与系统设计先理清 Spring Boot 和小程序的分工2.1 技术选型为什么是 Spring Boot 加微信小程序原生框架小程序类毕业设计的主流做法是前后端分离前端跑在微信开发者工具里后端是独立的 Java 服务。这套源码的后端用 Spring Boot前端用微信小程序原生框架数据库用 MySQL。选 Spring Boot 而不是 SSM理由很现实第一内嵌 Tomcat不用单独装外部服务器部署 war 包IDEA 里直接运行 main 方法服务就起来了第二配置集中在 application.yml 一个文件里数据源、端口、上传大小、日志级别一眼扫完第三启动错误堆栈定位精准对新手排错非常友好。用 SSM 的话需要维护 web.xml、spring-mvc.xml、mybatis-config.xml 好几个配置文件一个地方写错报错信息往往和真正原因对不上排查成本高出一截。我给学生的建议是除非学校强制要求 SSM否则 Spring Boot 是更稳的选择尤其是答辩时讲注解、自动配置、starter 这些点比讲 XML 配置好展开得多。对比项Spring BootSSMSpringSpringMVCMyBatis启动方式内置 Tomcat直接运行 main 方法外置 Tomcat打 war 包部署配置复杂度application.yml 覆盖大多数配置多个 XML 互相引用排错体验堆栈精准日志直观配置问题层层绕答辩展开度注解、自动配置、starter 都好讲绕不开 XML 手动装配小程序端选择原生框架而不是 uni-app原因也不复杂。原生 WXML/WXSS/JS 没有跨端编译层接口调用最直接断点调试最直观。毕设里用到的基本就是页面跳转、网络请求、本地缓存、表单校验这些基础能力原生完全够用。等以后真要跑抖音小程序或者支付宝小程序再考虑 uni-app 那套跨端方案不是一个赛道的事。2.2 数据库设计用户、商品、供销需求与订单四张核心表拿到源码第一件事我会先看 SQL 脚本整个系统的业务逻辑基本都体现在表结构里。这套设计里最核心的是四张表用户表、商品表、供销需求表、订单表。用户表里除了常规的 id、username、password关键字段是 role 和 regionrole 区分普通买家和农户两种角色region 记录地区是后面匹配逻辑的地基。商品表挂在农户登录态下每条记录带 seller_id、category、price、unit、stock、region这些字段几乎全是为供销匹配服务的。供销需求表是这个项目“自主供销”概念的落点买家可以主动发布想买的农产品农户可以发布供应的农产品系统按分类和地区做双向聚合。订单表记录交易过程状态字段至少包含待付款、待发货、待收货、已完成、已取消五个状态每次状态变更都要写库不允许前端只改页面临时状态。CREATE DATABASE IF NOT EXISTS farm_supply DEFAULT CHARACTER SET utf8mb4; USE farm_supply; CREATE TABLE user ( id INT PRIMARY KEY AUTO_INCREMENT, username VARCHAR(50) NOT NULL UNIQUE, password VARCHAR(255) NOT NULL, role TINYINT NOT NULL DEFAULT 0 COMMENT 0-普通用户 1-农户, phone VARCHAR(20), region VARCHAR(50), avatar VARCHAR(255), create_time DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE product ( id INT PRIMARY KEY AUTO_INCREMENT, seller_id INT NOT NULL, name VARCHAR(100) NOT NULL, category VARCHAR(50) NOT NULL COMMENT 蔬菜/水果/粮油/禽蛋, price DECIMAL(10,2) NOT NULL, unit VARCHAR(10) DEFAULT 斤, stock INT DEFAULT 0, description VARCHAR(500), image VARCHAR(255), region VARCHAR(50), status TINYINT DEFAULT 1 COMMENT 1-上架 0-下架, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (seller_id) REFERENCES user(id) ); CREATE TABLE supply_demand ( id INT PRIMARY KEY AUTO_INCREMENT, user_id INT NOT NULL, category VARCHAR(50) NOT NULL, product_name VARCHAR(100), quantity INT, region VARCHAR(50), demand_type TINYINT COMMENT 1-供应 2-需求, status TINYINT DEFAULT 1, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (user_id) REFERENCES user(id) ); CREATE TABLE orders ( id INT PRIMARY KEY AUTO_INCREMENT, order_no VARCHAR(32) NOT NULL UNIQUE, buyer_id INT NOT NULL, seller_id INT NOT NULL, product_id INT NOT NULL, quantity INT NOT NULL, total_price DECIMAL(10,2) NOT NULL, status TINYINT DEFAULT 0 COMMENT 0-待付款 1-待发货 2-待收货 3-已完成 4-已取消, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, update_time DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, FOREIGN KEY (buyer_id) REFERENCES user(id), FOREIGN KEY (seller_id) REFERENCES user(id), FOREIGN KEY (product_id) REFERENCES product(id) );这里有几个参数细节值得记一下。price 用 DECIMAL(10,2) 而不用 FLOAT农产品单价虽然大多数是个位数但涉及金额计算就必须保证精度FLOAT 在算总价时容易出现 0.1 加 0.2 不等于 0.3 的尴尬结果。第二个是显式指定 utf8mb4 字符集不指定的话存商品描述里的表情符号或生僻字会直接报错现在建库建表我都会默认带上。第三个是订单表单独存了 buyer_id 和 seller_id订单创建时就把买卖双方固定下来后面即使商品记录被下架订单历史查询也不受影响。再提醒两个建表时容易翻车的点。表名不要用 order这是 SQL 的保留字很多人第一次建订单表卡在这SQL 直接飘红所以脚本里写的是 orders。时间字段建议用 DATETIME 而不是 TIMESTAMPTIMESTAMP 有 2038 年问题并且受时区影响DATETIME 更稳定。2.3 后端包结构与小程序端目录对照后端工程打开后package 目录一般这样分controller、service、mapper、entity、config、common。controller 接收 HTTP 请求并调度 serviceservice 放业务逻辑供销匹配和订单状态机都在这层mapper 对应 SQL 操作通常基于 MyBatis-Plus 的 BaseMapperentity 映射数据库表字段名和表字段保持一致config 里放跨域配置、拦截器、JWT 过滤器common 放统一返回结果类 Result 和分页封装。小程序端每个页面一个文件夹包括 wxml、wxss、js、json 四个文件。首页是 pages/index商品列表是 pages/goods发布商品是 pages/publish订单列表是 pages/order个人中心是 pages/me。页面跳转通过 wx.navigateTo 或 wx.switchTab页面间传参靠 url 拼接这些机制要先搞懂逛页面代码时才不会被跳转逻辑绕晕。后端包名职责对应小程序端切入点controller暴露 HTTP 接口pages 下各页面的请求入口service业务逻辑含匹配和状态机多个页面共同调用mapperSQL 操作无直接对应entity表结构映射无config跨域、拦截器、token 校验utils/request.jscommon统一返回体和异常utils/auth.js2.4 毕业设计 LW 文档跳出源码的答辩材料这套资源附带 LW 文档在国内毕设生态里很重要因为多数学校要求“代码加论文加答辩演示”三件套。LW 文档通常包含绪论、需求分析、系统设计、数据库设计、系统实现、测试、总结这些章节和毕业论文模板基本对应。但要注意每个学校对格式要求不一样字体字号、图表编号、页眉页脚拿到后先对照自己学校的模板确认一遍再交。我改这种文档的习惯是先建一个格式对照表把自己学校模板的章标题、节标题、图注、表注、参考文献格式列出来然后逐段替换。文档里需要刻意准备呼源码的素材有四处数据库 E-R 图、系统架构图、核心代码截图、测试用例表。这些直接从源码里截取就行重点是让导师看到你确实把系统跑通并理解了项目。3. 核心业务实现登录、供销匹配与订单状态机3.1 小程序端登录与用户授权code 换 openid 的完整链路小程序登录和传统 Web 登录不一样核心是用 wx.login 返回的 code 换 openid。很多第一次写小程序的人会踩一个经典的坑以为 code 就是用户身份直接存起来用。实际上 code 有效期只有五分钟需要用 code 调微信接口换 openid 和 session_key。后端拿到 openid 后把它当成用户唯一标识查一下用户表没有就自动注册。因为 openid 不像手机号那样有明文可见的体验所以登录和注册接口实际上可以合并成一个加一个角色字段默认普通用户在个人中心再切换农户身份。RestController RequestMapping(/api/user) public class UserController { Autowired private UserService userService; PostMapping(/login) public Result login(RequestBody LoginRequest req) { // 1. 调用微信接口用 code 换 openid String openid userService.code2Openid(req.getCode()); if (openid null) { return Result.error(登录凭证已失效请重试); } // 2. 查用户是否存在不存在则自动注册 User user userService.findByOpenid(openid); if (user null) { user new User(); user.setOpenid(openid); user.setRole(0); userService.register(user); } // 3. 签发自定义 token后续请求带 token 访问 String token JwtUtil.generateToken(user.getId(), user.getRole()); return Result.success(new LoginResponse(token, user.getId(), user.getRole())); } }这段逻辑里有三个关键点要说。code2Openid 这一步通常在 application.yml 里配置小程序的 AppID 和 AppSecret如果填成测试号的配置真机登录时会匹配不上。JwtUtil 生成 token 时把 userId 和 role 塞进 payload后续 controller 通过拦截器就能拿到当前用户是谁、是买家还是农户不用每次查数据库。Result 是统一返回体格式是 code、message、data 三件套小程序端 request.js 里先判断 code 再继续业务逻辑这是前后端联调的基本约定。3.2 供销匹配不是简单列表而是双向推荐“自主供销”这四个字重点在自主匹配。常见做法是用户既能发布供应信息也能发布需求信息系统按分类和地区自动做双向推荐。这个逻辑听起来简单写起来容易翻车的点在匹配规则设计上。毕设场景不建议用太复杂的推荐算法协同过滤、用户画像这类名词虽然唬人但老师追问原理讲不清楚反而扣分。用规则匹配就够先按 category 精确匹配再叠加 region 条件最后按 create_time 倒序取最近几条每一条规则答辩时都能展开讲清楚。Service public class SupplyDemandServiceImpl implements SupplyDemandService { Autowired private SupplyDemandMapper supplyDemandMapper; public ListSupplyDemandVO matchSupply(DemandQuery query) { // 规则一按分类匹配保证供给和需求属于同一品类 ListSupplyDemand list supplyDemandMapper.selectByCategory(query.getCategory()); // 规则二按地区过滤优先推荐同城农产品 ListSupplyDemand filtered list.stream() .filter(item - query.getRegion().equals(item.getRegion())) .collect(Collectors.toList()); // 规则三按时间倒序只取最近发布的 8 条 filtered.sort((a, b) - b.getCreateTime().compareTo(a.getCreateTime())); return filtered.stream().limit(8).map(SupplyDemandVO::from).collect(Collectors.toList()); } }三个规则对应三种真实用户场景。分类匹配解决的是农产品品类太多、乱推荐没意义的问题地区过滤解决的是不同城市价格差异大、用户优先看本地的问题时间倒序解决的是农产品可能已经卖完或过期、优先看最新发布的问题。需要注意的边界情况是如果同分类下过滤结果不足 8 条前端要处理空态显示“暂无匹配供给试试切换地区或分类”不能白屏。我一般会在匹配逻辑里再加一个开关把地区过滤做成可选的。因为有些农产品是跨区域流通的比如海南的椰子本地没供给需要放宽地区限制才能匹配到。所以查询参数加一个 cityScope 字段默认 1 表示只查本地置 0 时忽略地区条件这样通用性会好很多。3.3 订单状态机五个状态之间的合法迁移路径订单系统最怕乱改状态所以必须用状态机约束流转方向。这套系统里五个状态的合法迁移路径是待付款可以到待发货或已取消待发货可以到待收货待收货可以到已完成已完成和已取消是终态。不能用两个 if 直接判断完事散落的状态判断在后期改需求时会非常痛苦集中管理才是正确的做法。public boolean changeOrderStatus(Long orderId, Integer oldStatus, Integer newStatus, Long userId) { Order order orderMapper.selectById(orderId); if (order null) { throw new BusinessException(订单不存在); } // 用 Map 定义合法状态迁移避免散落一地的 if 判断 MapInteger, ListInteger transferMap new HashMap(); transferMap.put(0, Arrays.asList(1, 4)); transferMap.put(1, Arrays.asList(2)); transferMap.put(2, Arrays.asList(3)); ListInteger nextStatus transferMap.get(oldStatus); if (nextStatus null || !nextStatus.contains(newStatus)) { throw new BusinessException(非法状态迁移); } // 校验操作人身份只有买家能付款只有卖家能发货 if (oldStatus 0 newStatus 1) { if (!order.getBuyerId().equals(userId)) { throw new BusinessException(只有买家可操作付款); } } if (oldStatus 1 newStatus 2) { if (!order.getSellerId().equals(userId)) { throw new BusinessException(只有卖家可操作发货); } } orderMapper.updateStatus(orderId, newStatus); return true; }这里有一个很容易忽略的点oldStatus 必须从数据库实时读取而不是从前端传。前端页面上显示的状态可能是陈旧数据如果直接信任前端传过来的状态做判断并发场景下会出现重复付款或者订单已取消却能发货的情况。所以接口设计上只传 newStatus旧状态永远以数据库为准。状态转移 Map 的设计越简单越好。有人会写成十几个 switch case把判断散落在 service 不同方法里最后改一个状态要在三个地方同步改。用 Map 集中管理后新增状态只需要改 transferMap 那一行排查时也只要看一个数据结构。4. 本地部署和联调从 SQL 脚本到小程序能交互动4.1 后端启动前必做的四步配置拿到源码第一件事不是点启动而是把环境配置统一好。JDK 版本、Maven 仓库、MySQL 版本这四项不一致会带来很多奇怪报错。最常见组合是 JDK 1.8 或 JDK 11Spring Boot 2.x 在这两个版本下兼容性最好。MySQL 用 5.7 或 8.0 都行但驱动配置写法略有区别。核心配置要改 application.yml下面这份是常见模板server: port: 8080 spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/farm_supply?useUnicodetruecharacterEncodingutf8useSSLfalseserverTimezoneAsia/Shanghai username: root password: 你的数据库密码 servlet: multipart: max-file-size: 10MB max-request-size: 20MB mybatis-plus: mapper-locations: classpath:mapper/*.xml configuration: map-underscore-to-camel-case: true log-impl: org.apache.ibatis.logging.stdout.StdOutImpl jwt: secret: farm-supply-jwt-secret-key expire-days: 7 wx: appid: 你的小程序AppID secret: 你的小程序AppSecret配置里有三个关注点。url 里的 serverTimezoneAsia/Shanghai 不能省略没有它 MySQL 8.0 连接时会报时区异常。characterEncoding 建议直接用 utf8mb4和前面建库时保持一致避免中文乱码。multipart 大小限制决定农产品图片能传多大后端不做图片压缩的话前端压完图后一般控制在 5MB 内就够。第二步是执行 SQL 脚本建库第三步在 IDEA 里用 Maven 刷新依赖第四步启动 Application 主类。启动时观察控制台日志看到 Tomcat started on port 8080 说明后端起来了。如果日志里报 Failed to configure a DataSource基本就是 yml 里的数据库地址或密码没对上。4.2 小程序端导入微信开发者工具的四个关键设置后端跑起来后前端才能发起请求。在微信开发者工具里导入项目时很多人卡在 AppID 上。个人开发者没有企业 AppID选择测试号是可行的测试号通过编译后调用的域名不受企业限制调本地后端接口没问题。进项目后第一个要改的是 config.js 里的 baseURL。代码里可能是 http://localhost:8080在开发者工具里能访问本机但部署到真机时要把 localhost 换成电脑的局域网 IP手机和电脑要在同一 WiFi 下。// config.js 环境配置文件 module.exports { // 开发环境微信开发者工具可访问 localhost // 真机调试时改成 http://192.168.x.x:8080 baseURL: http://localhost:8080, timeout: 10000, }第三个关键设置是“详情”面板里的“不校验合法域名、web-view业务域名、TLS 版本以及 HTTPS 证书”选项开发阶段允许小程序请求 HTTP 接口的开关不勾的话请求会被拦截。第四个设置是清缓存每次改完代码重新编译如果数据没有刷新清一下缓存再编译能排除旧代码的干扰。4.3 接口联调用 Apifox 先自测后端再跑通小程序端主链路开发阶段尽量不直接在小程序端调后端小程序端页面一跳转就很难区分问题是前端还是后端。我一般会先用 Apifox 把后端接口参数和返回结构调通再切到小程序端做端到端验证。# 用 curl 快速验证登录接口 curl -X POST http://localhost:8080/api/user/login \ -H Content-Type: application/json \ -d {code:test_code_001} # 查看商品列表接口 curl http://localhost:8080/api/product/list?page1size10如果登录接口返回的 code 是 0 且 data 里有 token说明后端正常。接下来用 Apifox 走一次下单主链路登录拿 token带 token 创建订单再模拟付款、发货、收货。这条链路能跑通说明数据库、拦截器、状态机三个关键模块没问题。同时再把 MyBatis 日志打开后端会打印每次执行的 SQL配合 Apifox 不用打开数据库就能看到数据写入情况联调效率会高不少。4.4 真机调试时的网络配置排查模拟器上跑通不算真的跑通真机演示才是毕业设计最容易翻车的一环。真机上最经典的问题就是在 config.js 里留了 localhost手机上的 localhost 指向手机自己自然连不上电脑的后端。改局域网 IP 还不行的话先试试电脑防火墙有没有拦掉 8080 端口Windows 上经常是防火墙默认拦了 Java 进程。判断请求到底有没有发出去常规做法是用 Charles 这类工具抓包看请求包能明显看到请求 URL、请求头、返回体。如果抓包看到请求已经到后端但返回 404检查一下 controller 的路径映射如果请求压根没发出去基本就是前端 baseURL 或域名校验的问题。5. 避坑指南小程序毕业设计最常见的五个坑5.1 登录提示 code 无效或 openid 为空现象小程序端点击登录后端返回“code 无效”日志里显示微信接口返回 errcode 40029。原因最常见是 AppID 和 AppSecret 配置错了写成了测试号的对应值其次是前端拿着同一个 code 重复换两次 openid微信的 code 只能使用一次。解决每次登录都在小程序端重新调用 wx.login 拿新 code后端只换一次 openid不要缓存 code。AppSecret 不要硬编码在源码里至少放到配置文件并从代码中排除提交答辩时被问到密钥泄露问题时也有话可讲。5.2 图片上传后小程序端显示不出来现象商品发布时图片上传提示成功但列表页图片破图或灰块。原因后端存储图片用的是本地磁盘路径比如 D:/uploads/a.jpg小程序端访问不到这个路径必须转成 http 地址才能加载。解决图片访问地址必须拼接成 http://ip:8080/upload/a.jpg 的形式并在后端配静态资源映射。Spring Boot 里加下面的配置类把本机 upload 目录映射成可通过 URL 访问的资源Configuration public class WebMvcConfig implements WebMvcConfigurer { Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler(/upload/**) .addResourceLocations(file: System.getProperty(user.dir) /upload/); } }这段配置里file:前缀是关键必须保留否则会被当成 classpath 路径处理。System.getProperty(user.dir)获取的是当前工作目录IDEA 中通常是工程根目录所以要保证上传目录建在工程根目录下。5.3 请求报跨域或被拦截现象在小程序端发请求控制台报 invalid url 或无法连接服务器。原因小程序端和浏览器跨域机制不一样小程序不存在严格意义上的浏览器跨域但开发者工具会模拟部分限制。最常出问题的是真机调试时 baseURL 写成了 localhost真机上的 localhost 指向手机自己根本到不了电脑后端。解决开发工具里勾选“不校验合法域名”真机调试时把 baseURL 改成电脑的局域网 IP并确保电脑防火墙放行 8080 端口。后端再配一层全局 CORS 是合理的保险动作方便以后万一要接 Web 端管理台时不用再改。5.4 订单状态卡在待发货现象买家付款后订单一直处于待发货状态卖家端点击发货没有任何变化。原因卖家端页面只改了本地缓存里的订单状态没有调后端接口或者调用了但传的参数 newStatus 不对。解决检查发货按钮的点击事件是否调用更新状态接口确认 newStatus 传的是代表已发货的状态值。给按钮加一个日志输出把当前状态值打出来看问题往往立刻定位到是前端缓存写入覆盖了接口返回值。5.5 启动报 Access denied for user现象启动后端第一次请求数据库时报 Access denied for user rootlocalhost。原因application.yml 里的密码写错了或 MySQL 用户权限不足或 MySQL 8 默认使用 caching_sha2_password 认证插件而数据库驱动版本不支持。解决先确认密码正确再更新 MySQL 连接驱动为最新版也可以在连接串中加 allowPublicKeyRetrievaltrue 和 useSSLfalse。JDK 版本、MySQL 认证插件、驱动版本这三个变量在环境不一致时特别容易组合出问题建议统一用一个 Docker 镜像里的 MySQL 来学习避免被宿主机环境干扰。6. 代码能跑只是及格请求封装与骨架屏两个加分项6.1 统一请求封装干掉每页重复的 wx.request很多毕设代码在每个页面直接写 wx.request一个问题重复散落在十几个页面里答辩时老师问“token 过期你在哪里处理”就答不上来。常见做法是封装两层基础 request 函数统一处理 token 注入、超时、错误码拦截基于它再生成业务 api 模块页面只关心业务数据。// utils/request.js 统一请求封装 const request (url, method, data) { return new Promise((resolve, reject) { const token wx.getStorageSync(token) wx.request({ url: getApp().globalData.baseURL url, method, data, header: { Content-Type: application/json, token: token || , }, success: (res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data) } else if (res.data.code 401) { // token 过期统一跳登录页 wx.removeStorageSync(token) wx.reLaunch({ url: /pages/login/login }) reject(new Error(登录状态已过期)) } else { // 统一弹错误提示页面不用重复写 toast wx.showToast({ title: res.data.message || 请求失败, icon: none }) reject(new Error(res.data.message)) } }, fail: (err) { wx.showToast({ title: 网络异常请检查网络, icon: none }) reject(err) }, }) }) } // api/product.js 页面实际调用的接口 const getProductList (params) request(/api/product/list, GET, params) const createOrder (data) request(/api/order/create, POST, data) module.exports { getProductList, createOrder }经过这样改造业务页面里不再出现 token 和错误提示逻辑以后新增接口只要在 api 文件里加一行。更重要的是答辩时能直接指着代码说“我在请求层统一做了登录态管理和异常拦截”这是工程化意识的体现。6.2 商品列表加骨架屏演示不露怯答辩现场最怕网速差时商品列表白屏改起来其实成本很低。在页面的 onLoad 阶段先用一个 loading 状态控制视图渲染八块灰色占位区域请求成功后换成真实列表。微信小程序原生的骨架屏也可以用 view 模拟用几行 wxml 和 wxss 就能实现。!-- 商品列表骨架屏示例 -- view wx:if{{loading}} classskeleton-list view classskeleton-item wx:for{{[1,2,3,4]}} wx:keyindex view classskeleton-img/view view classskeleton-line/view /view /view view wx:else !-- 真实商品列表 -- /view这样当数据没返回时用户看到的是稳定的界面框架而不是空白页体验上会舒服很多。从那以后我每次拿到新项目源码都会先做一件事把代码里所有 wx.request 搜出来看是否都被统一封装接管再检查登录链路里 token 过期有没有全局处理。两个问题都达标这份源码才敢直接拿去演示和答辩。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?