This commit is contained in:
我是个攻城狮
2026-05-27 16:01:15 +08:00
parent dc43370953
commit c3f5f13bb3
953 changed files with 107635 additions and 11 deletions
@@ -0,0 +1,35 @@
export const H5 = function (fn) {
// #ifdef H5
fn();
// #endif
};
export const APP = function (fn) {
// #ifdef APP-PLUS
fn();
// #endif
};
export const applets = function (fn) {
// #ifdef MP
fn();
// #endif
};
export const notH5 = function (fn) {
// #ifndef H5
fn();
// #endif
};
export const baiduApple = function (fn) {
// #ifdef MP-BAIDU
fn();
// #endif
};
export const touTiao = function (fn) {
// #ifdef MP-TOUTIAO
fn();
// #endif
};
export const mp = function (fn) {
// #ifdef MP
fn();
// #endif
};
@@ -0,0 +1,93 @@
export const baseConfig = {
h5: {
rewriteFun: true, // 是否对uni-app reLaunch/navigateBack 两个方法重写 处理uni刷新直接返回到首页和触发路由守卫
paramsToQuery: false, // h5端上通过params传参时规则是vue-router 刷新会丢失 开启此开关将变成?连接的方式
loading: true, // 是否显示加载动画
hinderTab: false, // 是否拦截uni-app自带底部菜单 TODO
vueRouterDev: false, // 完全使用采用vue-router的开发模式
useUniConfig: true, // 是否采用在pages.json下的所有页面配置信息,false时需开发者自行设置页面
keepUniIntercept: false, // 保留uni-app使用vue-router的拦截器
vueNext: false, // 在next管道函数中是否获取vueRouter next的原本参数
replaceStyle: false, // 是否对resetStyle函数中返回的style节点进行全部替换,否则为追加
resetStyle: () => JSON.parse('{}'), // 自定义加载样式函数 可返回一个包涵 html、style、script 的对象来重置Router内置的加载动画
mode: 'hash',
base: '/',
linkActiveClass: 'router-link-active',
linkExactActiveClass: 'router-link-exact-active',
scrollBehavior: (to, from, savedPostion) => savedPostion,
fallback: true,
},
APP: {
holdTabbar: true, // 是否开启底部菜单拦截
loddingPageStyle: () => JSON.parse('{"backgroundColor":"#FFF"}'), // 当前等待页面的样式 必须返回一个json
loddingPageHook: (view) => { plus.navigator.closeSplashscreen(); view.show(); }, // 刚刚打开页面处于等待状态,会触发此事件
animation: { animationType: 'pop-in', animationDuration: 300 }, // 页面切换动画
switchPageOutTime: 1000, // 最高能忍耐的页面切换时间 达到此时间 不管切换有没有完成都会显示页面出来 这对启动页帮助很大
},
debugger: false, // 是否处于开发阶段 设置为true则打印日志
encodeURI: true, // 是否对url传递的参数进行编码
routerBeforeEach: () => {}, // router 前置路由函数 每次触发跳转前先会触发此函数
routerAfterEach: () => {}, // router 后置路由函数 每次触发跳转后会触发此函数
routes: [],
};
export const methods = {
push: 'navigateTo',
replace: 'redirectTo',
replaceAll: 'reLaunch',
pushTab: 'switchTab',
back: 'navigateBack',
};
export const H5FnTypeToggle = {
push: 'push',
replace: 'replace',
replaceAll: 'replace',
pushTab: 'replace',
};
export const lifeCycle = {
beforeHooks: [],
afterHooks: [],
routerHooks: [],
routerbeforeHooks: [], // 内部跳转前生命周期
routerAfterHooks: [], // 内部跳转后生命周期
};
export const Global = { // 缓存一些必要的对象,作为全局可以访问的参数
$parseQuery: null, // url query 帮助类实例
Router: {},
vueRouter: {},
addedRoutes: [], // 用于缓存用户动态添加的路由
RouterReadyPromise: () => {},
H5RouterReady: null, // 当前router是否就绪
backLayerC: 1, // 返回api调用时开发者传递的 delta
LockStatus: false, // 当前是否正在进行跳转 正在跳转调用api是不给跳转的
};
export const uniAppHook = {
indexVue: {}, // 首页 组件对象
toutiaoIndexThis: {}, // 头条小程序Index this对象
appVue: {}, // 同getApp()获取到的对象一毛一样的 其实就是app.vue组件
onLaunch: { fun: [], args: {}, isHijack: false }, // 这两个是app.vue
onShow: { fun: [], args: {}, isHijack: false },
variationFuns: ['onReady', 'onUnload'], // 一些uni-app的变异方法 需要处理下
waitHooks: {}, // 首页等待中的生命钩子 一些需要等待的Hooks,就是在页面没有进来之前一些提前触发的生命钩子 主要是用户已经声明好的
indexCallHooks: ['onLoad', 'onReady', 'created', 'onShow'], // 在首页首次启动时需要触发的生命周期
needHooks: ['onLoad', 'onReady', 'onShow', 'created', 'onHide', 'onUnload', 'onResize'], // 首页需要拦截的生命钩子
pageReady: false,
onLaunched: false, // 否触发onLaunch事件
};
export const appletsConfig = { // 小程序端的一些路由所需配置
onLaunchEd: false, // 当前小程序端是否触发onLaunch事件
};
export const route = function (object = {}) {
return {
...object,
params: {},
query: {},
};
};
@@ -0,0 +1,60 @@
import { uniAppHook } from './config';
import H5init from '../vueRouter/init';
import { appInit, removeBackPressEvent, pageIsHeadBack } from '../appRouter/init';
import appletsInit from '../appletsRouter/init';
import { appPlatform } from './util';
import { proxyIndexHook } from '../appRouter/hooks';
import { appletsProxyIndexHook } from '../appletsRouter/hooks';
/**
* 获取一些需要在各个平台混入的事件
* @param {Object} Router 当前原始路由对象
*/
const getMixins = function (Router) {
return {
H5: {
beforeCreate() {
if (this.$options.router) {
H5init(Router.$root, this.$options.router, this);
}
},
},
APP: {
onLaunch() {
uniAppHook.onLaunched = true; // 标志已经触发了 onLaunch 事件
appInit.call(this, Router.$root);
},
onLoad() {
// 第一个页面 拦截所有生命周期
if (uniAppHook.onLaunched && !uniAppHook.pageReady) {
uniAppHook.onLaunched = false;
proxyIndexHook.call(this, Router.$root);
}
removeBackPressEvent(this.$mp.page, this.$options); // 移除页面的onBackPress事件
},
onBackPress(...args) {
return pageIsHeadBack.call(Router.$root, this.$mp.page, this.$options, args);
},
},
APPLETS: {
onLaunch() {
uniAppHook.onLaunched = true; // 标志已经触发了 onLaunch 事件
appletsInit.call(this, Router.$root);
},
onLoad() {
if (uniAppHook.onLaunched && !uniAppHook.pageReady) { // 必须是第一个页面
uniAppHook.onLaunched = false;
appletsProxyIndexHook.call(this, Router.$root);
}
},
},
};
};
const initMixins = function (Vue, Router) {
Vue.mixin({
...getMixins(Router)[appPlatform(true)],
});
};
export default initMixins;
@@ -0,0 +1,103 @@
import { appPlatform } from './util';
import { methods, H5FnTypeToggle, Global } from './config';
import { transitionTo } from '../appRouter/hooks';
import { appletsTransitionTo, backCallHook } from '../appletsRouter/hooks';
import uniPushTo from '../appRouter/uniNav';
import appletsUniPushTo from '../appletsRouter/appletsNav';
import { err, warn } from './warn';
import H5PushTo from '../vueRouter/routerNav';
import * as compile from './compile';
/**
* 返回api 触发的公共函数
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
*
* this 为当前 Router 实例
*/
const isBcakNav = function ({
backLayer,
delta,
H5PATCH,
}) {
compile.H5(() => {
H5PATCH.on('historyBack', {
backLayer,
delta,
});
});
compile.APP(() => {
Global.backLayerC = backLayer; // 告诉路由需要返回几层
uni.navigateBack({
delta: backLayer,
complete: () => {
Global.LockStatus = false; // 跳转完成解锁状态
},
});
});
compile.mp(() => {
backCallHook.call(this, backLayer, () => {
uni.navigateBack({
delta: backLayer,
});
});
});
};
/**
* 非 返回api 触发的公共函数
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
*
* this 为当前 Router 实例
*/
const notBackNav = function (rule, fnType) {
if (rule == null) {
return err('跳转规则为空,不允许这样操作');
}
if (rule.constructor === String) { // 单独 path 的情况 允许?拼接参数
const ruleArray = rule.split('?');
if (ruleArray.length > 1) {
rule = {
path: ruleArray[0],
query: Global.$parseQuery.parse(ruleArray[1]),
};
}
}
switch (appPlatform(true)) {
case 'H5':
return H5PushTo.call(this, H5FnTypeToggle[fnType], rule, methods[fnType]);
case 'APP':
Global.LockStatus = true; // 设置为锁住状态
return transitionTo.call(this, rule, fnType, uniPushTo);
case 'APPLETS':
Global.LockStatus = true; // 设置为锁住状态
return appletsTransitionTo.call(this, rule, fnType, appletsUniPushTo);
default:
err('糟糕!!!还有其他的执行环境???没听说过啊。一脸懵逼???加QQ群问问:769241495');
break;
}
};
/**
* 处理正在跳转的公共api
* @param {Object/String} rule 当前跳转规则
* @param {String} fnType 跳转页面的类型方法
* @param {Boolean} isBack 是否通过 back() api 调用的 默认为false
* @param {Boolean} enforce 是否强制越过跳转加锁检查 默认false 目前只有back() api 传递了
*
* this 为当前 Router 实例
*/
const navjump = function (rule, fnType, isBack = false, enforce = false) {
if (Global.LockStatus && !enforce) { // 正在跳转的状态下 给出提示正在跳转
return warn('当前页面正在处于跳转状态,请稍后再进行跳转....');
}
if (isBack) { // 是返回api触发的
return isBcakNav.call(this, rule, fnType);
}
return notBackNav.call(this, rule, fnType);
};
export default navjump;
@@ -0,0 +1,157 @@
import { Global } from './config';
import { warn, err } from './warn';
const nodeURL = require('query-string');
class ParseQuery {
get queryName() {
return nodeURL;
}
/**
* 判断当前这个对象是否为深度对象
* @param {Object} obj
*/
isDepthObject(obj) {
const str = JSON.stringify(obj);
return str.match(/}/g).length > 1;
}
/**
* 从URL中提取查询字符串
* @param {String} url
*/
extract(url) {
return nodeURL.extract(url);
}
/**
* 把一个 key=value&key1=value 的字符串转成对象
* @param {string} strQuery key=value&key1=value 类型的字符串
*/
parse(strQuery) {
return nodeURL.parse(strQuery);
}
/**
* 把一个对象转成 key=value&key1=value 类型的字符串
* @param {Object} ObjQuery 符合js标注的对象
* @param {Boolean} intact 是否在转成的字符串前添加?号
*/
stringify(ObjQuery, intact = true) {
const strQuery = nodeURL.stringify(ObjQuery);
if (intact) {
return `?${strQuery}`;
}
return strQuery;
}
/**
* 把一个对象或者 key=value&key1=value 类型的数据加密成 query=encodeURIComponent(value)
* @param {Object|String} query 符合js标注的对象 或者 key=value&key1=value 字符串
* @param {Boolean} intact 是否在转成的字符串前添加?号
*/
encode(query, intact = true) {
let [strQuery, formatQuery] = ['', ''];
if (query == null) {
warn('加密参数没有传递,你知道?', true);
return '';
}
if (query.constructor === String) { // 字符串 尝试 转成 对象
strQuery = JSON.stringify(this.parse(query));
} else if (query.constructor === Object) { // 直接转成字符串对象即可
if (Object.keys(query).length === 0) {
warn('当前参数不满足加密规范!');
return '';
}
strQuery = JSON.stringify(query);
}
if (intact) {
formatQuery = '?';
}
formatQuery += `query=${encodeURIComponent(strQuery)}`;
return formatQuery;
}
/**
* 把一个已经加密好的字符串 query=encodeURIComponent(value) 解密成 对象
* @param {string} strQuery 已经加密好的字符串 query=encodeURIComponent(value)
*/
decode(strQuery) {
if (strQuery == null) {
warn('解密参数没有传递,你知道?', true);
return {};
}
let jsonQuery = strQuery;
if (strQuery.constructor === Object) { // 如果是对象 看能不能满足要求
jsonQuery = strQuery.query;
if (jsonQuery == null) {
warn('当前解密参数不满足编码规则');
return {};
}
jsonQuery = `query=${jsonQuery}`;
}
let decode = {};
// query 长这个样 query=encodeURIComponent(value)
const decodeStr = decodeURIComponent(jsonQuery);
const { query } = this.parse(decodeStr); // 转成 json 获取到正真的json字符串
if (query == null) {
warn('当前解密参数不满足编码规则');
} else {
try {
decode = JSON.parse(query);
} catch (error) {
warn('当前解密参数不满足编码规则');
}
}
return decode;
}
queryGet(query) {
const { encodeURI } = Global.Router.CONFIG; // 获取到路由配置
let [decode, historyObj, strQuery] = [query, query, ''];
switch (encodeURI) {
case true: { // 加密模式
decode = this.decode(query);
strQuery = this.encode(decode);
historyObj = {
query: encodeURIComponent(JSON.stringify(decode)),
};
break;
}
case false: { // 不加密模式
strQuery = this.stringify(query);
break;
}
default: {
err('未知参数模式,请检查 \'encodeURI\'', true);
}
}
return { strQuery, historyObj, decode };
}
/**
* 对需要传递的参数进行加密解密
* @param {Object|String} query get为false 必须为 Object 类型
* @param {String} get 是取值 还是通过api传值
*/
transfer(query = {}) {
const { encodeURI } = Global.Router.CONFIG; // 获取到路由配置
switch (encodeURI) {
case true: {
// 加密模式
return this.encode(query, false);
}
case false: {
// 不加密模式
return this.stringify(query);
}
default: {
err('未知参数模式,请检查 \'encodeURI\' ', true);
}
}
}
}
export default ParseQuery;
@@ -0,0 +1,255 @@
import { route, baseConfig, Global } from './config';
import { builtIn } from '../vueRouter/base';
import { err, log, warn } from './warn';
/**
* 当前是不是H5运行环境
*/
export const isH5 = function () {
return typeof window !== 'undefined' && typeof document !== 'undefined';
};
/**
* 判断当前变量是否为Object
* @param {Object} strObj
*/
export const isObject = function (strObj) {
return strObj.toString() === '[object Object]' && strObj.constructor === Object;
};
/**
* 获取当前运行平台
* @param {Boolean} applets 默认false true时所有小程序平台统一返回 APPLETS
*/
export const appPlatform = function (applets = false) {
let platform = '';
// #ifdef APP-PLUS-NVUE
platform = 'APPNVUE';
// #endif
// #ifdef APP-PLUS
platform = 'APP';
// #endif
// #ifdef H5
platform = 'H5';
// #endif
// #ifdef MP-ALIPAY
platform = 'ALIPAY';
// #endif
// #ifdef MP-BAIDU
platform = 'BAIDU';
// #endif
// #ifdef MP-QQ
platform = 'QQ';
// #endif
// #ifdef MP-WEIXIN
platform = 'WEIXIN';
// #endif
// #ifdef MP-TOUTIAO
platform = 'TOUTIAO';
// #endif
if (applets) {
// #ifdef MP
platform = 'APPLETS';
// #endif
}
return platform;
};
/**
* 定义一个空方法 如果最后一个参数为true则打印所有参数
* @param {...any} args
*/
export const noop = function (...args) {
if (args[args.length - 1] === true) {
log(args);
}
};
/**
* 格式化基础配置信息 通过new Router传递过来的参数
*/
export const formatConfig = function (userConfig) {
if (!userConfig.routes || userConfig.routes.constructor !== Array) {
return err(`路由参数 'routes' 必须传递 \r\n\r\n${JSON.stringify(userConfig)}`);
}
if (userConfig.h5 != null && userConfig.h5.constructor !== Object) {
return err(`h5参数传递错误,应该是一个 'Object' 类型 示例:\r\n\r\n${JSON.stringify(baseConfig.h5)}`);
}
const config = Object.create(null);
const baseConfigKeys = Object.keys(baseConfig);
for (let i = 0; i < baseConfigKeys.length; i += 1) {
const key = baseConfigKeys[i];
if (userConfig[key] != null) {
if (userConfig[key].constructor === Object) {
config[key] = {
...baseConfig[key],
...userConfig[key],
};
} else if (key == 'routes') { // 需要加入已知的白名单
config[key] = [...baseConfig[key], ...userConfig[key], ...builtIn];
} else {
config[key] = userConfig[key];
}
} else {
config[key] = baseConfig[key];
}
}
return config;
};
export const filter = function (str) {
str += '';
str = str.replace(/%/g, '%25');
str = str.replace(/\+/g, '%2B');
str = str.replace(/ /g, '%20');
str = str.replace(/\//g, '%2F');
str = str.replace(/\?/g, '%3F');
str = str.replace(/&/g, '%26');
str = str.replace(/=/g, '%3D');
str = str.replace(/#/g, '%23');
return str;
};
/**
* 使用encodeURI:true的情况 需要进行编码后再传递,解码等等 可以传递深度对象并会在路径后面加入一个query=
*
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQueryN = function (routerName, query, Encode) {
if (Encode) {
return {
url: routerName,
query: JSON.parse(decodeURIComponent(query.replace(/^query=/, ''))),
};
}
return {
url: routerName,
query: `query=${encodeURIComponent(JSON.stringify(query))}`,
};
};
/**
* 使用encodeURI:false的情况 直接格式化为普通的queryURl参数形式传递即可 扁平深度对象
*
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQueryD = function (routerName, query, Encode) {
if (Encode) {
const obj = {};
const reg = /([^=&\s]+)[=\s]*([^&\s]*)/g;
while (reg.exec(query)) {
obj[RegExp.$1] = RegExp.$2;
}
return {
url: routerName,
query: obj,
};
}
const encodeArr = [];
const queryKeys = Object.keys(query);
for (let i = 0; i < queryKeys.length; i += 1) {
const attr = queryKeys[i];
let encodeStr = '';
if (query[attr].constructor == Object) {
encodeStr = parseQueryD(routerName, query[attr], Encode).query;
encodeArr.push(encodeStr);
} else {
encodeStr = filter(query[attr]);
encodeArr.push(`${attr}=${encodeStr}`);
}
}
return {
url: routerName,
query: encodeArr.join('&'),
};
};
/**
* @param {String} routerName //路径名称
* @param {JSON} query //需要格式化参数
* @param {Boolean} Encode //是获取还是编码后传递
*/
export const parseQuery = function (routerName, query, Encode = false) {
if (Global.Router.CONFIG.encodeURI) {
return parseQueryN(routerName, query, Encode);
}
return parseQueryD(routerName, query, Encode);
};
export const exactRule = function (cloneRule, routes, ruleKey, getRule = false) {
const params = {};
let i = 0;
// eslint-disable-next-line
while (true) {
const item = routes[i];
if (item == null) {
if (!getRule) {
err(`路由表中未查找到 '${ruleKey}' 为 '${cloneRule[ruleKey]}'`);
}
return {
path: '',
name: '',
};
}
if (item[ruleKey] != null && item[ruleKey] === cloneRule[ruleKey]) {
if (!getRule) {
params.url = item.path;
params.rule = item;
if (isH5()) { // 如果是h5 则使用优先使用自定义路径名称
params.url = item.aliasPath || item.path;
}
return params;
}
return item;
}
i += 1;
}
};
export const resolveRule = function (router, rule, query = {}, ruleKey = 'path') {
const ruleInfo = route(
exactRule({
...rule,
},
router.CONFIG.routes,
ruleKey,
router),
);
return {
...ruleInfo,
query,
};
};
/**
* 把一些不必要的参数进行格式化掉,完成url的美观
* @param {String} URLQuery URL中传递的参数
*/
export const formatURLQuery = function (URLQuery) {
switch (URLQuery.trim()) {
case 'query=%7B%7D':
case '%7B%7D':
case '?query=%7B%7D':
case '?':
case '?[object Object]':
case '?query={}':
URLQuery = '';
break;
default:
warn('url已经很完美啦,不需要格式化!');
break;
}
return URLQuery;
};
/**
* 拷贝对象
* @param {Object} object
*/
export const copyObject = function (object) {
return JSON.parse(JSON.stringify(object));
};
@@ -0,0 +1,34 @@
import { Global } from './config';
const isLog = function (type, errText, enforce) {
if (!enforce) {
const dev = Global.Router.CONFIG.debugger;
const isObject = dev.toString() === '[object Object]';
if (dev === false) {
return false;
} if (dev === false) {
return false;
} if (isObject) {
if (dev[type] === false) {
return false;
}
}
}
/* eslint no-console:"off" */
console[type](errText);
};
export const err = function (errInfo, enforce = false) {
isLog('error', errInfo, enforce);
};
export const warn = function (errInfo, enforce = false) {
isLog('warn', errInfo, enforce);
};
export const log = function (errInfo, enforce = false) {
isLog('log', errInfo, enforce);
};
export const warnLock = function (errInfo) {
console.warn(errInfo);
};