baimacms/scratch/url-query.js

347 lines
9.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* ============================================================
* URL 参数解析工具 - url-query.js
* 支持 uni-app 全平台H5、微信小程序、支付宝小程序、
* 百度小程序、抖音小程序、App、快应用
* ============================================================
*
* 【使用方式】
* import UrlQuery from '@/scratch/url-query.js';
*
* // 获取单个参数(自动类型转换)
* const id = UrlQuery.get('id'); // "123" → 123 (number)
* const name = UrlQuery.get('name'); // "张三" (string)
* const flag = UrlQuery.get('flag'); // "true" → true (boolean)
* const rid = UrlQuery.get('rid', '-1'); // 不存在返回默认值 '-1'
*
* // 获取原始字符串(不自动类型转换)
* const raw = UrlQuery.get('id', '', false); // "123" (string)
*
* // 获取所有参数
* const params = UrlQuery.getAll(); // { id: 123, name: "张三" }
*
* // 判断参数是否存在
* const hasId = UrlQuery.has('id'); // true / false
*
* // 解析指定 URL 字符串
* const parsed = UrlQuery.parseUrl('https://example.com?id=1&name=a');
*
* // 解析查询字符串
* const obj = UrlQuery.parse('?a=1&b=2&b=3'); // { a: "1", b: ["2", "3"] }
*
* 【特性】
* 1. 多平台自适应H5 通过 location 获取,小程序/App 通过页面栈获取)
* 2. 自动类型转换数字、布尔值、null
* 3. 安全解码(异常编码不会报错)
* 4. 支持数组参数(同名参数自动转为数组)
* 5. 支持 history 路由和 hash 路由模式
* 6. 容错处理空值、undefined、异常 URL 等)
* ============================================================
*/
const UrlQuery = {
/**
* 解析 URL 查询字符串为对象
* @param {string} queryString - 查询字符串,如 "?a=1&b=2" 或 "a=1&b=2"
* @returns {Object} 解析后的参数对象,同名参数自动转为数组
*/
parse(queryString) {
const result = {};
if (typeof queryString !== 'string') {
return result;
}
// 统一去掉开头的 ? 和 #,并去除首尾空白
queryString = queryString.replace(/^[?#]/, '').trim();
if (!queryString) {
return result;
}
const pairs = queryString.split('&');
for (let i = 0; i < pairs.length; i++) {
const pair = pairs[i];
if (!pair) continue;
const eqIndex = pair.indexOf('=');
let key, value;
if (eqIndex === -1) {
// 无等号的情况,如 "?foo&bar"
key = pair;
value = '';
} else {
key = pair.substring(0, eqIndex);
value = pair.substring(eqIndex + 1);
}
// 安全解码(异常编码不抛错)
try {
key = decodeURIComponent(key);
} catch (e) {
/* 保留原始值 */
}
try {
value = decodeURIComponent(value);
} catch (e) {
/* 保留原始值 */
}
// 处理数组参数(如 id=1&id=2 → { id: ['1', '2'] }
if (Object.prototype.hasOwnProperty.call(result, key)) {
if (!Array.isArray(result[key])) {
result[key] = [result[key]];
}
result[key].push(value);
} else {
result[key] = value;
}
}
return result;
},
/**
* 获取当前页面的 URL 参数对象
* 多平台自适应H5 从 window.location 获取,其他平台从页面栈获取
* @returns {Object} 当前页面所有参数
*/
getCurrent() {
let params = {};
// #ifdef H5
// H5 环境下优先从 window.location 解析(兼容 history 和 hash 路由)
try {
if (typeof window !== 'undefined' && window.location) {
const searchParams = this.parse(window.location.search);
const hashParts = window.location.hash.split('?');
const hashParams = hashParts.length > 1 ? this.parse(hashParts[1]) : {};
params = { ...searchParams, ...hashParams };
}
} catch (e) {
console.error('[UrlQuery] H5 解析 location 失败:', e);
}
// #endif
// 尝试从页面栈获取uni-app 所有平台通用)
try {
const pages = getCurrentPages();
if (pages && pages.length > 0) {
const currentPage = pages[pages.length - 1];
if (currentPage && currentPage.options && typeof currentPage.options === 'object') {
// H5 下合并 location 与 options其他平台以 options 为准
// #ifdef H5
params = { ...params, ...currentPage.options };
// #endif
// #ifndef H5
params = { ...currentPage.options };
// #endif
}
}
} catch (e) {
// getCurrentPages 在部分环境下可能不可用,静默处理
}
return params;
},
/**
* 解析指定 URL 字符串中的参数
* @param {string} url - 完整 URL如 "https://example.com/path?id=1&name=a#hash?b=2"
* @returns {Object} 解析后的参数对象search 参数优先hash 参数补充)
*/
parseUrl(url) {
if (typeof url !== 'string' || !url.trim()) {
return {};
}
url = url.trim();
// 优先使用浏览器 URL APIH5 / 现代环境)
try {
if (typeof URL !== 'undefined') {
const urlObj = new URL(url);
return this.parse(urlObj.search);
}
} catch (e) {
// URL 构造函数不支持或解析失败,降级到手动提取
}
// 降级处理:手动提取 search 和 hash 中的参数
let search = '';
let hashQuery = '';
const searchIndex = url.indexOf('?');
const hashIndex = url.indexOf('#');
if (searchIndex !== -1) {
const endIndex = (hashIndex !== -1 && hashIndex > searchIndex) ? hashIndex : url.length;
search = url.substring(searchIndex, endIndex);
}
if (hashIndex !== -1) {
const hashPart = url.substring(hashIndex + 1);
const hashQIndex = hashPart.indexOf('?');
if (hashQIndex !== -1) {
hashQuery = hashPart.substring(hashQIndex);
}
}
const searchParams = this.parse(search);
const hashParams = this.parse(hashQuery);
return { ...searchParams, ...hashParams };
},
/**
* 获取单个参数值
* @param {string} key - 参数名(必填)
* @param {*} defaultValue - 参数不存在时返回的默认值(默认空字符串)
* @param {boolean} autoType - 是否自动类型转换(默认 true
* true : "123"→123, "true"→true, "false"→false, "null"→null
* false : 始终返回原始字符串
* @returns {*} 参数值
*/
get(key, defaultValue = '', autoType = true) {
if (typeof key !== 'string' || !key) {
return defaultValue;
}
const params = this.getCurrent();
if (!Object.prototype.hasOwnProperty.call(params, key)) {
return defaultValue;
}
let value = params[key];
// 值为 undefined / null 时返回默认值
if (value === undefined || value === null) {
return defaultValue;
}
// 不自动类型转换时直接返回字符串形式
if (!autoType) {
return String(value);
}
// 已经是非字符串类型(如数字、布尔)直接返回
if (typeof value !== 'string') {
return value;
}
// 空字符串处理
if (value === '') {
return defaultValue !== '' ? defaultValue : '';
}
// 布尔值转换
if (value === 'true') return true;
if (value === 'false') return false;
// null / undefined 字符串转换
if (value === 'null') return null;
if (value === 'undefined') return undefined;
// 整数转换(严格匹配,避免 "0123" 被转成 123 后丢失前导零)
if (/^-?(0|[1-9]\d*)$/.test(value)) {
const intVal = parseInt(value, 10);
if (!isNaN(intVal) && String(intVal) === value) {
return intVal;
}
}
// 浮点数转换
if (/^-?(0|[1-9]\d*)\.\d+$/.test(value)) {
const floatVal = parseFloat(value);
if (!isNaN(floatVal) && String(floatVal) === value) {
return floatVal;
}
}
return value;
},
/**
* 获取所有参数(原始字符串形式,不做类型转换)
* @returns {Object} 所有参数的字符串键值对
*/
getAll() {
return this.getCurrent();
},
/**
* 获取所有参数并进行自动类型转换
* @returns {Object} 所有参数(数字、布尔值已转换)
*/
getAllTyped() {
const params = this.getCurrent();
const result = {};
for (const key in params) {
if (Object.prototype.hasOwnProperty.call(params, key)) {
result[key] = this.get(key, '', true);
}
}
return result;
},
/**
* 判断参数是否存在于当前 URL 中
* @param {string} key - 参数名
* @returns {boolean}
*/
has(key) {
if (typeof key !== 'string' || !key) {
return false;
}
return Object.prototype.hasOwnProperty.call(this.getCurrent(), key);
},
/**
* 获取数组类型的参数值
* 若参数为单个值则包装为数组返回,不存在返回默认值
* @param {string} key - 参数名
* @param {Array} defaultValue - 默认值(默认空数组)
* @returns {Array} 数组形式的参数值
*/
getArray(key, defaultValue = []) {
if (typeof key !== 'string' || !key) {
return defaultValue;
}
const params = this.getCurrent();
if (!Object.prototype.hasOwnProperty.call(params, key)) {
return defaultValue;
}
const value = params[key];
if (Array.isArray(value)) {
return value;
}
if (value === undefined || value === null) {
return defaultValue;
}
return [value];
}
};
// 兼容 CommonJS / ES Module / 全局变量
// #ifdef VUE3
export default UrlQuery;
// #endif
// #ifndef VUE3
if (typeof module !== 'undefined' && module.exports) {
module.exports = UrlQuery;
} else if (typeof define === 'function' && define.amd) {
define(function () { return UrlQuery; });
} else {
try {
window.UrlQuery = UrlQuery;
} catch (e) {
// 非浏览器环境静默处理
}
}
// #endif