fix: 适配多平台url编码规则,修复双重编码问题

针对H5和小程序/App平台分别处理url参数编码:
1. H5平台需要手动encodeURIComponent避免参数解析错误
2. 小程序/App平台由uni内部自动编码,避免双重编码导致参数解码异常
同时重构了工具类中的编码逻辑,统一多场景下的平台适配规则
This commit is contained in:
2026-07-10 11:29:16 +08:00
parent 897a8157e7
commit a90245b201
2 changed files with 36 additions and 11 deletions

View File

@@ -282,7 +282,14 @@ export const resolvePageUrl = (page_key, params = {}) => {
Object.keys(params).forEach(function (key) { Object.keys(params).forEach(function (key) {
const value = params[key]; const value = params[key];
if (value === null || value === undefined || value === '') return; if (value === null || value === undefined || value === '') return;
// H5:浏览器不自动编码,需要手动 encodeURIComponent
// 小程序/App:uni.navigateTo 会自动编码,这里不能再 encode,否则双重编码
// #ifdef H5
parts.push(key + '=' + encodeURIComponent(value)); parts.push(key + '=' + encodeURIComponent(value));
// #endif
// #ifndef H5
parts.push(key + '=' + value);
// #endif
}); });
if (parts.length > 0) { if (parts.length > 0) {
url = url + (url.indexOf('?') !== -1 ? '&' : '?') + parts.join('&'); url = url + (url.indexOf('?') !== -1 ? '&' : '?') + parts.join('&');
@@ -643,18 +650,25 @@ export default {
// 2. 合并 param:同 key 时新值覆盖旧值,杜绝 ?name=A&name=B 这种重复 // 2. 合并 param:同 key 时新值覆盖旧值,杜绝 ?name=A&name=B 这种重复
const merged = Object.assign({}, existingParams, param || {}); const merged = Object.assign({}, existingParams, param || {});
// 3. 统一编码。decode -> encode 双向 roundtrip 兼具两种语义: // 3. 参数编码策略,按平台区分:
// - 调用方传原始值(/pages_tool/index/diy?name=XXX) -> encode 后 %2F%3F... // - 小程序/App: uni.navigateTo / router.pushUrl 内部会自动对 url 做一次编码,
// - 调用方已传编码值(%2F%3F...) -> decode 还原 -> encode 还原回去,不会被二次编码 // 若在 JS 侧再 encodeURIComponent 一次,会被平台再编码一层,目标页 onLoad(options)
// - 任何不能 decode 的非法序列(如裸 %) 走 catch 路径,直接 encode // 只 decode 一次后仍是编码形态(如 %25E6%25B2%2599%25E5%258F%2591 -> %E6%B2%99%E5%8F%91)
// => 这里必须保持原始值,完全交给平台去编码
// - H5: 浏览器不会自动编码 url,若参数含 & ? = 等特殊字符会被 queryString 错位解析
// => 这里仍需手动 encodeURIComponent,decode->encode roundtrip 兼容调用方已编码的入参
const safeEncode = (v) => { const safeEncode = (v) => {
const s = v == null ? '' : String(v); const s = v == null ? '' : String(v);
// try { // #ifdef H5
// return encodeURIComponent(decodeURIComponent(s)); try {
// } catch (e) { return encodeURIComponent(decodeURIComponent(s));
// return encodeURIComponent(s); } catch (e) {
// } return encodeURIComponent(s);
}
// #endif
// #ifndef H5
return s; return s;
// #endif
}; };
const queryStr = Object.keys(merged) const queryStr = Object.keys(merged)
.map(k => safeEncode(k) + '=' + safeEncode(merged[k])) .map(k => safeEncode(k) + '=' + safeEncode(merged[k]))
@@ -2020,10 +2034,12 @@ export default {
// 判断当前 URL 是否已经包含查询参数,决定使用 ? 还是 & // 判断当前 URL 是否已经包含查询参数,决定使用 ? 还是 &
const separator = targetUrl.includes('?') ? '&' : '?'; const separator = targetUrl.includes('?') ? '&' : '?';
// 遍历参数对象,手动进行 encodeURIComponent 编码并拼接 // 遍历参数对象,拼接参数
// 小程序/App 端:uni.navigateTo/router.pushUrl 内部会自动对 url 编码,
// 这里不能再 encodeURIComponent,否则会被平台再编码一层导致双重编码
const queryString = Object.entries(params) const queryString = Object.entries(params)
.filter(([_, value]) => value !== undefined && value !== null) .filter(([_, value]) => value !== undefined && value !== null)
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`) .map(([key, value]) => `${key}=${value}`)
.join('&'); .join('&');
// 重新拼接成完整的 URL // 重新拼接成完整的 URL

View File

@@ -50,9 +50,18 @@ export default {
helpDetail(e) { helpDetail(e) {
// #ifndef MP-WEIXIN // #ifndef MP-WEIXIN
if (e.link_address) { if (e.link_address) {
// H5:浏览器不自动编码,需要手动 encodeURIComponent
// 其他小程序平台(HarmonyOS 等):uni.redirectTo 内部会自动编码,不能再 encode,否则双重编码
// #ifdef H5
uni.redirectTo({ uni.redirectTo({
url: '/pages_tool/webview/webview?src=' + encodeURIComponent(e.link_address) url: '/pages_tool/webview/webview?src=' + encodeURIComponent(e.link_address)
}); });
// #endif
// #ifndef H5
uni.redirectTo({
url: '/pages_tool/webview/webview?src=' + e.link_address
});
// #endif
} else { } else {
this.$util.redirectTo('/pages_tool/help/detail', { this.$util.redirectTo('/pages_tool/help/detail', {
id: e.id id: e.id