refactor: 环境地址收口到 utils/config.js,请求层新增 silent 静默模式

config.js(新增)作为后端域名/接口地址的唯一来源。此前 request.js 与 App.vue
各硬编码一份生产域名,改域名要改两处,漏一处就会出现「接口通了但 OTA 检查
更新接不上」这类极难排查的偏差。

request.js 改动:
- PROD_URL / LOCAL_URL 改为从 ./config 引入,不再就地硬编码
- 新增 options.silent:调用方自行兜底提示时跳过通用错误 toast。
  用于 feedback 的 404 回退 mock —— 否则用户会先看到「请求失败 (404)」
  再看到「感谢反馈」,两句自相矛盾。HTTP 错误与 fail 两个分支都受其控制
- fail 回调保留原始 errMsg 并打上 isNetworkError / isTimeout 标记:
  fail 表示压根没拿到后端响应(超时/断网),与 4xx/5xx 有本质区别 ——
  后端可能已执行成功只是响应没回来,上层需要据此打断用户的连点重试
This commit is contained in:
2026-09-15 15:51:30 +08:00
parent 93e4714019
commit 242a6d5463
2 changed files with 52 additions and 10 deletions

View File

@ -0,0 +1,24 @@
/**
* 全局环境配置 — 后端域名/接口地址的唯一来源。
*
* 此前 request.js 与 App.vue 各自硬编码了一份生产域名,改域名要改两处,
* 漏一处就会出现「接口通了但 OTA 检查更新接不上」这类极难排查的偏差,
* 故统一收口到本文件。新增环境地址一律加在这里,不要在业务代码里写死。
*/
// 生产环境 API 地址
//
// ⚠️ HTTPS 说明2026-09 实测):生产域名 track_back.iris-rs.cn 的 443 端口
// 是通的、也确实配有证书,但证书【已过期】—— 现在切 https 会让 App 的
// 全部请求直接失败。待运维完成证书续期后,只需把下面 PROD_URL 与
// LOCAL_URL 的协议头改成 https:// 即可,业务代码无需任何改动。
// 续期前请勿改动,保持 http:// 以保证可用性。
export const PROD_URL = "http://track_back.iris-rs.cn/api/v1";
// 本地开发地址 — 真机调试时指向开发机局域网 IP
export const LOCAL_URL = "http://192.168.9.80:8011/api/v1";
// 生产基础地址:供只认「生产环境」的场景直接引用
export const BASE_URL = PROD_URL;
export default { PROD_URL, LOCAL_URL, BASE_URL };

View File

@ -7,8 +7,8 @@
* 3. 生产默认 PROD_URL
*/
const PROD_URL = "http://track_back.iris-rs.cn/api/v1";
const LOCAL_URL = "http://192.168.9.80:8011/api/v1";
// 域名/环境地址统一来自 utils/config.js勿在此处硬编码
import { PROD_URL, LOCAL_URL } from "./config";
// ============================================================
// 环境 URL自动感知缓存配置 > NODE_ENV > 生产默认)
@ -170,17 +170,35 @@ export default function request(options) {
return;
}
if (code === 403) uni.showToast({ title: extractErrorDetail(res.data, "无权操作"), icon: "none", duration: 3000 });
else if (code === 400) uni.showToast({ title: extractErrorDetail(res.data, "请求参数有误"), icon: "none", duration: 2500 });
else if (code === 409) uni.showToast({ title: extractErrorDetail(res.data, "操作冲突"), icon: "none", duration: 3000 });
else if (code === 422) {
// 表单/参数校验失败Pydantic—— detail 是校验项数组,必须解析出 msg 再提示
uni.showToast({ title: extractErrorDetail(res.data, "提交内容不符合要求"), icon: "none", duration: 3000 });
// silent调用方自行兜底提示时如 feedback 的 404 回退 mock跳过通用 toast
// 否则用户会先看到一句「请求失败 (404)」,再看到「感谢反馈」,自相矛盾。
if (!options.silent) {
if (code === 403) uni.showToast({ title: extractErrorDetail(res.data, "无权操作"), icon: "none", duration: 3000 });
else if (code === 400) uni.showToast({ title: extractErrorDetail(res.data, "请求参数有误"), icon: "none", duration: 2500 });
else if (code === 409) uni.showToast({ title: extractErrorDetail(res.data, "操作冲突"), icon: "none", duration: 3000 });
else if (code === 422) {
// 表单/参数校验失败Pydantic—— detail 是校验项数组,必须解析出 msg 再提示
uni.showToast({ title: extractErrorDetail(res.data, "提交内容不符合要求"), icon: "none", duration: 3000 });
}
else uni.showToast({ title: extractErrorDetail(res.data, `请求失败 (${code})`), icon: "none", duration: 3000 });
}
else uni.showToast({ title: extractErrorDetail(res.data, `请求失败 (${code})`), icon: "none", duration: 3000 });
reject(res);
},
fail() { uni.showToast({ title: "网络连接失败", icon: "none" }); reject(new Error("network")); },
fail(err) {
// ⚠️ fail = 压根没拿到后端响应(超时/断网),与 4xx/5xx 有本质区别:
// 后端可能已经执行成功,只是响应没回来。故保留原始 errMsg 并打上标记,
// 让上层能识别出「网络级失败」从而打断用户的连点重试。
const errMsg = (err && (err.errMsg || err.message)) || "";
const isTimeout = /timeout/i.test(errMsg);
if (!options.silent) {
uni.showToast({ title: isTimeout ? "网络超时,请稍后重试" : "网络连接失败", icon: "none" });
}
const e = new Error(errMsg || "network");
e.errMsg = errMsg;
e.isNetworkError = true;
e.isTimeout = isTimeout;
reject(e);
},
});
});
}