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

@ -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);
},
});
});
}