
简介这是一套基于Android技术的音乐论坛APP完整源码后端采用Java/SSM框架前端包含Vue管理后台、H5页面与微信小程序端特别适合计算机专业学生用于毕业设计或课程设计实践。压缩包共2000个文件整体约85.83MB主要涵盖430个Vue页面组件、345个JavaScript逻辑脚本、208个Java后端代码及307个PNG图标素材另附SQL数据库脚本与项目配置文件目录按前端、后台、小程序等模块划分便于快速定位。目前已有70人浏览学习。源码经严格测试可直接运行代码注释完善并配有运行环境说明可轻松搭建开发环境深入理解音乐论坛从用户登录、音乐展示、论坛交互到后台管理的完整业务链路。对课程作业、毕业答辩或求职项目积累而言这套三端完整的实战源码都是扎实的参考素材。1. 这套基于 Java 与 Android 技术的音乐论坛 App拆开到底值不值得跑先说结论:这套源码不是那种只丢给你一个 Android 空壳的 Demo,而是一套「Java 后端接口 uni-app 前端工程」的完整前后端分离项目,前端打包后跑在 Android 上,后端用 Java 提供帖子、回复、用户、音乐资源等接口。打开压缩包你会看到两类目录:一类是 Java 后端源码,一类是 front 前端工程,front 里包含了 pages、components、colorui、assets 等标准 uni-app 目录结构。适合谁?正在做音乐类社区、校园论坛类课程设计的人,以及想把「发帖、回帖、扫码分享、列表分页」这一套闭环逻辑一次性学明白的人。它不是玩具项目,页面、组件、接口全都齐,自己改改就能当毕设主体,也能当练手项目把前后端联调走一遍。2. 拆包看结构:Java 后端与 uni-app 前端的分工边界在哪2.1 压缩包里到底是什么:前端工程与后端工程的辨识方法解压后不要急着找 .apk,这套资源给的是源码而不是安装包。顶层会看到一个 front 目录,这是 uni-app 项目;另一个是 Java 工程目录,通常以 pom.xml 或 build.gradle 作为识别标志。很多人第一次打开会蒙:为什么没有 Activity 和 xml?因为 Android 端不是用 Android Studio 原生写的,而是用 uni-app 跨端框架写的,最后通过 HBuilderX 打包成 Android 安装包。这不是缺陷,反而让同一套代码能同时输出 Android、H5 等多端。front 目录内部,components 下能看到几个关键组件:forum-reply 负责帖子回复,mescroll-uni 负责上拉加载更多,uni-popup 负责弹窗,tki-qrcode 负责生成二维码。这些组件名直接告诉你这个 App 的核心玩法:看帖子、回帖子、滚动分页、生成分享二维码。colorui 是 UI 样式库,assets/css 是全局样式, pages 目录才是页面主体。看懂了这几个目录,你就知道这套 App 的表层功能大概长什么样。后端部分主要是 Java 写的接口服务,常见的分层是 controller、service、mapper,配合 MySQL 存储数据。前端的 pages 里的请求地址,默认指向本地后端服务。所以整个项目的运行链路是:uni-app 前端发请求 → Java 后端接口 → 返回 JSON → 前端渲染列表和详情。想让它跑起来,前后端必须一起启动,只开前端只会看到白屏或接口报错。2.2 运行前必须搞定的环境三件套:数据库、后端、前端打包工具先把环境对齐,不然后面每一步都在踩坑。数据库用 MySQL,版本建议 5.7 或 8.0,需要把后端目录里附带的 .sql 文件导入;后端需要 JDK 与 Maven 环境,IDE 用 IntelliJ IDEA 或 Eclipse 都行;前端工程用 HBuilderX 打开,因为 uni-app 工程最省事的运行方式就是 HBuilderX 直接运行到 Android App 基座。常见做法是先导入数据库,再改后端配置,启动后端,最后用 HBuilderX 运行前端。后端配置文件的重点有两个:数据库连接地址、端口号。端口默认常见用的是 8080,如果被占用,要同时改后端 application.yml 和前端 utils/request.js 或 api 目录里的 baseURL。# 导入数据库(在命令行或 MySQL 客户端执行) mysql -u root -p database/forum.sql # 检查后端是否启动成功(端口按实际配置) curl http://localhost:8080/api/health上面第一条命令把数据库结构连同种子数据一次性导入;第二条是验证后端接口服务有没有起来。如果 curl 返回 JSON 或 HTTP 200,说明后端已经就绪;如果连接被拒绝,先去查后端控制台日志,绝大多数情况是端口配置不一致导致的。参数说明:forum.sql 文件名以实际压缩包为准,如果找不到,检查后端 resources 目录下的 db 子目录。// front/utils/request.js 中修改后端地址的常见位置 const BASE_URL http://localhost:8080/api这段代码是前端所有请求的公共前缀。如果你的后端跑在局域网另一台机器上,要把 localhost 换成那台机器的局域网 IP,比如 http://192.168.1.100:8080/api。注意 Android 真机上不能写 localhost,因为那指向手机自己;也不能直接写花括号表达式,要写死的地址。换了地址之后,页面里所有和帖子、用户相关的接口都会自动指向新后端。2.3 选型角度:为什么用 uni-app 而不是原生 Java 写 Android这个问题很多人会问。从课程设计角度看,uni-app 的优势在于:一套代码能同时出 Android、H5、小程序,做出来的东西演示场景多;UI 层用 vue 语法,页面的布局和交互调整比原生快很多;对于以 Java 后端为重点的课题,前端用 uni-app 可以显著降低界面开发工作量,把精力放在后端接口设计上。代价也有:部分原生能力调用时要依赖打包配置,比如扫码和相册权限,需要到 manifest.json 里做权限声明。这套代码里用到了 tki-qrcode 组件,它是前端生成二维码的常见方案,不是调用摄像头扫码,而是把链接生成二维码让别人扫——这种场景用前端 QR 库就够了,不需要原生 SDK。3. 前端页面逐页拆解:论坛核心功能是怎么串起来的3.1 首页信息流与 mescroll-uni 的下拉刷新和上拉加载首页是典型的列表页,展示音乐相关的帖子流。列表数据来自后端的分页接口,前端用 mescroll-uni 完成下拉刷新和上拉加载。这个组件是 uni-app 里常用的滚动加载方案,之所以选它,是因为它自己管理分页状态,不用你手写 onReachBottom 再去拼接页码。先看页面结构关键代码:template view classpage mescroll-uni refmescrollRef initmescrollInit :downdownOption :upupOption downdownCallback upupCallback view v-foritem in postList :keyitem.id classpost-card text classpost-title{{ item.title }}/text text classpost-author{{ item.nickname }}/text text classpost-time{{ item.createTime }}/text /view /mescroll-uni /view /templatemescroll-uni 组件必须通过 ref 和 init 初始化,父组件要接收 mescroll 实例并调用它的方法。downOption 控制下拉刷新的文案与样式,upOption 控制上拉加载的页码参数。注意: up 回调里要自己拼接 pageNum,通常第一页从 1 开始,下拉刷新时重置为 1,上拉加载时 pageNum 加 1。忘记重置页码是这个组件最常见的翻车点,表现是下拉刷新后列表数据从第 2 页开始接。// 上拉加载的回调 upCallback(page) { const params { pageNum: page.num, pageSize: page.size } this.$http.get(/post/list, { params }).then(res { if (page.num 1) { this.postList res.data.records } else { this.postList this.postList.concat(res.data.records) } this.mescroll.endSuccess(res.data.records.length) }) }page.num 和 page.size 是 mescroll 组件自动传进来的分页参数,你可以直接拿它请求后端通用的分页接口结构。endSuccess(count) 告诉组件本次加载成功并且这次加载了多少条,组件根据这个值判断还有没有下一页。后端接口返回的 records 字段是 MyBatis-Plus 分页的默认结构,这也反向说明后端分页用的就是常见的 Page 对象。3.2 发帖与图片:多行文本、标签与图片上传的交互实现发帖页是论坛类 App 的核心输入入口,代码里包含标题输入框、正文多行文本域、可选的封面图片上传。多行文本域在 textarea 上要设置 auto-height 或定高,否则在 Android 上会出现输入几行后视图不滚动的问题。view classpublish-card input v-modelform.title placeholder输入标题 maxlength30 / textarea v-modelform.content placeholder分享你的音乐故事 maxlength5000 / button clickchooseImage选择封面/button button clickpublish发布/button /viewtextarea 的 maxlength 要和后端字段长度保持一致,否则后端抛异常。图片选择流程是前端先调 uni.chooseImage 拿到临时路径,再上传到后端,后端返回图片 URL,最后提交表单时把 URL 放进 form.cover。不要试图把图片 base64 塞进 JSON 请求体,那样请求体会非常大,后端接口容易超时。正确做法是先把图片传到专门的 upload 接口,再拿返回的 URL 参与表单提交。// 上传图片并拿回 URL uni.chooseImage({ count: 1, success(res) { const tempFilePath res.tempFilePaths[0] uni.uploadFile({ url: BASE_URL /file/upload, filePath: tempFilePath, name: file, success: uploadRes { const data JSON.parse(uploadRes.data) form.cover data.url } }) } })这里的 name 字段是后端接口指定的表单字段名,必须叫 file,和后端 RequestParam(file) 对应。如果后端的参数名是 image,这里就要改成 image,前后端不一致会返回 400。真实项目里我一般会先看后端 controller 的 RequestParam 注解再定前端字段名,不要想当然。3.3 帖子详情、回复与二维码:一套完整的内容互动闭环详情页是另一个重点。进入帖子详情后,页面要展示正文内容,下方展示回复列表,底部是输入框和回复按钮。forum-reply 组件封装了回复输入、回复列表展示和回复提交逻辑。用的是组件化思路,把回复区从详情页里拆出去,避免详情页代码过长。forum-reply :post-idpostId :reply-listreplyList submitsubmitReply /这个组件的 props 有两个:postId 用于提交回复时告诉后端回复哪条帖子,replyList 用于列表展示。submit 事件在用户点击发送时触发,父组件拿到回复内容后调接口。很多课程设计项目会把回复逻辑直接写在详情页里,这套代码用组件拆开,有一个实际好处:如果你要在多个页面复用一个回复功能,比如用户主页的留言,直接引组件就行,不用复制粘贴页面代码。二维码组件 tki-qrcode 放在详情页底部,用途生成当前帖子的分享链接。它的核心参数是 content,传入链接字符串,组件内部自动把它绘制成二维码。tki-qrcode :valshareLink :size200 resultqrResult /shareLink 可以是 https 链接,也可以是自定义协议串。生成结果只是一个图片,不影响业务逻辑。注意 :size 控制二维码尺寸,不宜过大,200 到 260 显示效果最好,再大会糊。result 回调能拿到生成的 base64 图片路径,你可以把它保存到相册实现分享图片带二维码。3.4 个人中心与登录态:token 是怎么存的个人中心展示当前用户头像、昵称、发布的帖子数、回复数。要拿到这些数据,前端必须带登录凭证请求接口。这套项目用的是常见的 token 方案:用户登录后,后端返回一个 token 字符串,前端把它存在本地,之后每个请求的请求头里都带 Authorization。// 登录成功后的处理 const res await this.$http.post(/user/login, { username, password }) uni.setStorageSync(token, res.data.token) uni.setStorageSync(userInfo, res.data.userInfo) // 公共服务里统一注入 token request.interceptors.request.use(config { const token uni.getStorageSync(token) if (token) { config.header.Authorization token } return config })token 存储在 uni.setStorageSync 里,底层对应 Android 的 SharedPreferences 或本地文件存储。登出时直接 uni.removeStorageSync(token) 即可。这里有一个容易忽略的细节:存储 userInfo 时,因为对象里有 createTime 等字段,直接存储会丢失类型信息,取出来时要用 JSON.parse 包一层,否则渲染时间字段时会显示 undefined。3.5 消息列表与搜索页:tab 页之间的职责划分底部 tab 一般分为首页、发现、发布、消息、我的几个入口。搜索功能在发现页里,支持按帖子标题模糊搜索。这里的设计要点是:搜索接口和后端列表接口共用同一套分页结构,差别只在于多传一个 keyword 参数。这个思路值得沿用,你后期加按标签筛选时,只需要再加一个 tagId 参数,后端 mapper 里加一个条件判断,不用重新开发接口。消息页展示的是被回复通知。这套项目的通知机制比较简单:不是实时推送,而是进入消息页时拉取未读消息列表。如果你打算把它做成实时聊天,那要引入 WebSocket 或轮询,复杂度会上去不少,课程设计阶段建议保持现有方案。4. Java 后端接口设计与前端联调的关键约定4.1 接口风格:统一返回结构与分页参数的约定后端接口给前端返回的 JSON 有一个固定包裹结构,通常长这样——这是前后端能否顺畅联调的地基。{ code: 200, message: 操作成功, data: { records: [], total: 12, pageNum: 1, pageSize: 10 } }code 是业务状态码,前端全局拦截器先判断code,不是 200 就直接弹错误提示,不再进页面逻辑。message 是给用户看的文案。data 里 records 是数据列表,total 是总条数,pageNum 和 pageSize 是当前页码与每页条数。这套结构约定好之后,前端所有列表页面都用同一套解析逻辑,这是项目能快速铺开页面的原因。// 后端 Controller 返回统一结构 public class ResultT { private Integer code; private String message; private T data; public static T ResultT ok(T data) { ResultT r new Result(); r.code 200; r.message 操作成功; r.data data; return r; } }Result 类是整个后端接口的公共出口,所有 controller 方法都要 return Result.ok(xxx)。如果某个接口直接返回裸对象,前端的公共拦截器就解析不到 code 字段,会误判为错误。团队协作时通常把 Result 类放在 common 包下面,不允许各模块自己定义返回结构。4.2 帖子接口的完整链路:从 Controller 到数据库帖子的新增、分页查询、详情查看可以看作一条完整链路。要么只看 Controller 层,会漏掉关键逻辑;要么只盯 Mapper,又不清楚前端参数怎么进来。把整条链拆开看,才知道改哪里。RestController RequestMapping(/post) public class PostController { PostMapping(/add) public Result add(RequestBody Post post) { post.setUserId(CurrentUser.getId()); post.setCreateTime(new Date()); postService.save(post); return Result.ok(null); } GetMapping(/list) public Result list(RequestParam(defaultValue 1) Integer pageNum, RequestParam(defaultValue 10) Integer pageSize, RequestParam(required false) String keyword) { PagePost page postService.pagePosts(pageNum, pageSize, keyword); return Result.ok(page); } }add 接口的 RequestBody 要求前端提交 JSON,前端表单里收集的 title、content、cover 都要控制在同一个对象里。注意 post.setUserId 是从 CurrentUser 取当前登录用户,而不是信任前端传的 userId——这是安全设计,如果前端能随便传 userId,就存在越权风险。list 接口的 keyword 用 required false 修饰,允许搜索时传空。帖子详情接口负责把帖子正文和作者信息拼在一起返回。它和 list 接口的差别在于:list 只要列表字段,detail 要包含正文全文和作者信息。不少课程设计项目会把 list 和 detail 混成一个接口,前端拉到列表时把 content 全文也带回来,列表接口压力会变大。拆开是更规范的做法。4.3 登录鉴权的雏形:拦截器与 token 校验的取舍后端对需要登录的接口统一做 token 校验,实现方式是拦截器或 Spring 拦截器加自定义注解。登录接口本身不校验,注册接口不校验,其余接口统一校验。这种做法的好处是拦截规则集中在配置里,而不是在每个 Controller 开头手动判断。// 拦截器核心逻辑 Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { String token request.getHeader(Authorization); if (token null || !tokenService.verify(token)) { response.setStatus(401); return false; } CurrentUser.set(tokenService.getUserId(token)); return true; }这段拦截器从请求头取 token,调用 verify 校验有效性。校验方式是解析出用户信息后放入当前线程副本。注意 verify 方法内部是查 Redis 还是查数据库,取决于项目用没用缓存中间件。如果后端项目比较简单、没有引入 Redis,那 token 一般存数据库表,verify 方法就是一条带过期时间判断的查询,这种方式也够课程设计用。token 的生成逻辑一般是用用户 id 加盐做哈希,生成一串不重复的字符串。不要用明文密码当 token,也不要让 token 携带密码信息,否则拦截器日志打印时会泄露。这里比较推荐 UUID 或 JDK 自带 UUID 生成,简单且不冲突。4.4 上传接口:图片落地与 URL 回传上传接口是一个被多个页面复用的公共能力。它的流程是:接收 multipart 文件,存到服务器本地目录,返回可访问的 URL 给前端。开发环境常见存到后端项目的某个静态目录下,用一个映射路径让前端能通过 HTTP 访问到图片。PostMapping(/file/upload) public Result upload(RequestParam(file) MultipartFile file) { String originalFilename file.getOriginalFilename(); String suffix originalFilename.substring(originalFilename.lastIndexOf(.)); String newFilename System.currentTimeMillis() suffix; String savePath uploadDir newFilename; file.transferTo(new File(savePath)); return Result.ok(/upload/ newFilename); }suffix 取的是文件扩展名,比如 .jpg。uploads 目录默认放项目根目录下,如果你的项目部署到 Linux,务必确认运行账号对上传目录有写权限,否则会报 FileNotFoundException。newFilename 用时间戳拼后缀是为了防止重名,同时避免中文文件名在 URL 传递时乱码。在实际运行中,我建议在拼接文件名时毫秒基础上加一个三位随机后缀,防止同一毫秒上传多个文件相互覆盖。4.5 数据库表设计:帖子表、回复表、用户表的字段关系数据库是整个项目的地基。看懂了表关系,你就会明白为什么后端接口能连表查询。表结构一般有 user、post、reply 三张主表,post 表通过 user_id 关联 user 表,reply 表通过 post_id 关联 post 表、通过 user_id 关联 user 表。用户表字段通常有 id、username、password、nickname、avatar、create_time。password 字段存的是加密后的密文,不是明文。帖子表字段为 id、user_id、title、content、cover、create_time、like_count、view_count。回复表字段是 id、post_id、user_id、content、create_time、parent_id,parent_id 用于支持回复某条回复的楼层嵌套,如果你的项目不需要嵌套回复,这个字段可以留空。关联查询的典型 SQL 是帖子列表需要带出用户昵称和头像,接口层用 join 一次查出来,而不是先查帖子再循环查用户。很多课程设计源码犯的毛病是循环查库,列表 20 条就查 20 次用户表。看到这条 SQL 时你要确认项目用的是连表 join 还是 MyBatis-Plus 的实体关联。连表是更稳的。SELECT p.*, u.nickname, u.avatar FROM post p LEFT JOIN user u ON p.user_id u.id ORDER BY p.create_time DESC这条 SQL 里的 LEFT JOIN 保证查出来的帖子即使作者信息异常也不会丢。ORDER BY create_time DESC 让新帖排在前面。如果帖子量大了,可以加索引在 create_time 上,不过课程设计阶段表的规模不大,不加也跑得动。5. 三个必备避坑指南:换数据、换接口、换包名时最容易翻车的地方5.1 数据库导入失败:编码与版本语法不兼容现象:导入 .sql 文件时提示 Unknown collation 或乱码,表建出来了但中文全是问号。原因:两个坑叠加。第一,SQL 文件头部的字符集声明与本地 MySQL 不一致;第二,MySQL 8.0 的默认排序规则和 5.7 不一样,某些旧 SQL 里写的 utf8mb4_general_ci 在部分环境中不被识别。解决:导入之前先把 SQL 文件的编码统一为 utf8mb4,并且把 collate 改成 utf8mb4_0900_ai_ci;如果坚持用 5.7,就在 MySQL 配置文件里把 character_set_server 和 collation_server 都显式指定。导入时加上 --default-character-setutf8mb4 参数。我一般会用文本编辑器把 SQL 另存为 UTF-8 编码再导,这一步能解决八成乱码问题。5.2 前端能编译但列表空白:接口地址错了三年没发现现象:项目编译通过,页面也能打开,但所有列表都是空的,打开控制台看到请求报 404 或 net::ERR_CONNECTION_REFUSED。原因:前端 baseURL 指向的地址和后端实际监听地址不一致。最常见的是别人发的源码里 baseURL 写的是他那台机器的 IP,你拿过来没改。第二个常见原因是后端端口不是 8080,改成了 8888,前端却还是 8080。解决:打开 front/utils/request.js 或 config.js,把 baseURL 改成你本机后端的真实地址。改完先不要启动前端,直接在浏览器地址栏访问 baseURL 下的某个 GET 接口,能返回 JSON 再启动前端。这能帮你把问题限定在纯前端链路还是纯后端链路。做一个检查习惯:启动顺序固定为数据库 → 后端 → 接口自测 → 前端。5.3 图片上传失败但接口没报错:目录权限与 URL 映射缺失现象:选择图片后提示上传失败,但后端日志里没有异常;或者上传成功但页面上图片显示 404。原因:upload 目录没有写权限,transferTo 报错但被上层吞掉;或者上传成功但静态资源映射没有把 upload 目录暴露成 URL,前端拿到的地址访问不到文件。解决:先手工在 upload 目录下面创建一个文件,确认操作系统层面可写;然后在后端配置文件里检查静态资源映射,确保上传目录被映射为 /upload/** 的 URL 前缀。这个映射代码一般写在 WebMvcConfigurer 实现类里,把磁盘路径映射到 URL 路径。排查时直接访问返回的 URL 地址,浏览器能显示图片就说明映射没问题,不能显示就看后端映射配置。另外要养成习惯:上传成功后在日志里打印 savePath,方便定位文件到底落在哪。5.4 分页数据重复:下拉刷新后列表从第二页开始现象:首页下拉刷新,数据没有回到第一页,而是接着旧列表后面追加,出现前几条重复。原因:mescroll 的 downCallback 里没有重置 pageNum,upCallback 用的是组件自带的 page.num。如果你在 downCallback 里没有把 page.num 初始化,组件继续沿用之前的页码。解决:在 downCallback 里手动调用 this.mescroll.resetUpScroll(),它会自动把 up 的页码重置为 1,再触发 upCallback。如果你的组件版本对 resetUpScroll 支持不好,就在 onLoad 里把页码初始化为 1,每次下拉刷新直接把列表清空再重新赋值。记住一个原则:刷新和分页的页码状态只交给组件管,不要自己在 data 里维护一份独立的 pageNum 副本,两处状态不一致必出问题。5.5 Android 真机连不上后端:localhost 的认知陷阱现象:网页端跑得好好的,装到 Android 手机上就请求失败,所有接口都提示超时或无法连接。原因:手机上的 localhost 指向手机自己,不是你的开发电脑。模拟器里可能用 10.0.2.2 指代宿主机,但真机必须写电脑的局域网 IP。解决:保证手机和电脑连同一个路由器,把后端启动的电脑的局域网 IP 填入前端 baseURL,例如 http://192.168.1.5:8080/api。这里有几个连带坑:后端要监听 0.0.0.0 而不是默认回环地址;电脑防火墙要放行后端端口。验证方法是在手机浏览器里直接访问 http://192.168.1.5:8080/api 下的接口,能打开 JSON 再跑 App。如果手机浏览器能访问而 App 不能,去看 manifest.json 里的网络权限配置,uni-app 项目要确保不限制明文流量。Android 9 以上系统默认禁止明文 HTTP 请求,如果后端是 HTTP,需要在 manifest 里打开明文传输开关或者改用 HTTPS,这个配置在打包时经常被忽略,怕麻烦就直接用 HTTPS 证书或保持后端调试时不打包。6. 三个值得立即动手的改造技巧6.1 把固定写死的 IP 改成动态可配置拿到源码后第一件事不是跑通流程,而是把 baseURL 从硬编码改成可配置。这个动作我现在每次拿到新项目都强制做一遍,因为硬编码地址会在换电脑、换网络、上真机测试时反复咬你一口。// 新增 config.js 统一管理环境变量 const ENV { development: { baseURL: http://192.168.1.5:8080/api }, production: { baseURL: https://api.example.com/api } } export default ENV[process.env.NODE_ENV || development]process.env.NODE_ENV 在 uni-app 里由 HBuilderX 的运行模式自动注入,运行到浏览器时是 development,打包发布时可以手动改。把 baseURL 集中在一个文件里,改起来不用全文搜索替换。不要相信记性,不要觉得就一个地址而已,项目一旦加了分享二维码、上传图片,涉及拼接地址的地方至少有 5 处。6.2 全局拦截器里统一处理登录失效源码里的接口拦截通常只做到了 token 校验,前端页面没有统一的登录失效处理。改造方法是:在 request 工具拦截器里,遇到 code 为 401 的响应,直接清掉本地 token 并跳转到登录页。这一招能避免你在多个页面里重复写三段相同的请先登录逻辑。response.interceptors.response.use(res { if (res.data.code 401) { uni.removeStorageSync(token) uni.navigateTo({ url: /pages/login/login }) uni.showToast({ title: 登录已过期, icon: none }) } return res.data })判断逻辑放在公共拦截器里,改一处全端生效。从这以后,我每次接手一个带登录态的项目,第一天就检查拦截器里有没有处理 401,没有就先补上。拦截器处理好了,后面改权限、加会员、加角色控制都轻松很多。6.3 真机调试时把所有报错信息留在控制台Android 真机跑 uni-app 时,有时页面白屏,但 HBuilderX 控制台没有报错。这个时候不要慌,先在代码里加统一错误捕获,把错误打印到控制台,再配合 vConsole 在手机上看日志。vConsole 是一个前端调试面板,在小程序里嵌页面很管用,放到 uni-app 的 App 端也能用。它会把 console 日志显示在页面上,不用每次连数据线看电脑日志。// main.js 里挂全局错误捕获 Vue.config.errorHandler err { console.error([AppError], err) }这一行代码在真机调试阶段的价值非常大。白屏到底是因为接口跨域,还是组件渲染异常,还是某个字段 undefined,console 里全都能看出来。我在本地跑通第一个页面后,接下来的每一轮真机调试都会刻意操作一遍登录、发帖、回复、上传图片这四个主流程,每跑一个就在列表里标记一轮。从那以后我每次做联调,都强制走一遍全流程回归,不再只测改动的那一个页面。希望帮到你,跑通了之后,再往里加自己的需求就会顺畅很多。本文还有配套的精品资源点击获取