277 lines
8.8 KiB
JavaScript
277 lines
8.8 KiB
JavaScript
|
import { invokeArrayFns, isUniLifecycleHook, ON_LOAD, ON_SHOW, LINEFEED, RENDERJS_MODULES, formatLog, WXS_PROTOCOL, WXS_MODULES, UniLifecycleHooks, ON_ERROR, invokeCreateErrorHandler, invokeCreateVueAppHook } from '@dcloudio/uni-shared';
|
|||
|
import { isString, isArray, isFunction } from '@vue/shared';
|
|||
|
import { injectHook } from 'vue';
|
|||
|
|
|||
|
function getCurrentPage() {
|
|||
|
const pages = getCurrentPages();
|
|||
|
const len = pages.length;
|
|||
|
if (len) {
|
|||
|
return pages[len - 1];
|
|||
|
}
|
|||
|
}
|
|||
|
function getCurrentPageVm() {
|
|||
|
const page = getCurrentPage();
|
|||
|
if (page) {
|
|||
|
return page.$vm;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function invokeHook(vm, name, args) {
|
|||
|
if (isString(vm)) {
|
|||
|
args = name;
|
|||
|
name = vm;
|
|||
|
vm = getCurrentPageVm();
|
|||
|
}
|
|||
|
else if (typeof vm === 'number') {
|
|||
|
const page = getCurrentPages().find((page) => page.$page.id === vm);
|
|||
|
if (page) {
|
|||
|
vm = page.$vm;
|
|||
|
}
|
|||
|
else {
|
|||
|
vm = getCurrentPageVm();
|
|||
|
}
|
|||
|
}
|
|||
|
if (!vm) {
|
|||
|
return;
|
|||
|
}
|
|||
|
// 兼容 nvue
|
|||
|
{
|
|||
|
if (vm.__call_hook) {
|
|||
|
return vm.__call_hook(name, args);
|
|||
|
}
|
|||
|
}
|
|||
|
const hooks = vm.$[name];
|
|||
|
return hooks && invokeArrayFns(hooks, args);
|
|||
|
}
|
|||
|
|
|||
|
function injectLifecycleHook(name, hook, publicThis, instance) {
|
|||
|
if (isFunction(hook)) {
|
|||
|
injectHook(name, hook.bind(publicThis), instance);
|
|||
|
}
|
|||
|
}
|
|||
|
function initHooks(options, instance, publicThis) {
|
|||
|
var _a;
|
|||
|
const mpType = options.mpType || publicThis.$mpType;
|
|||
|
if (!mpType || mpType === 'component') {
|
|||
|
// 仅 App,Page 类型支持在 options 中配置 on 生命周期,组件可以使用组合式 API 定义页面生命周期
|
|||
|
return;
|
|||
|
}
|
|||
|
Object.keys(options).forEach((name) => {
|
|||
|
if (isUniLifecycleHook(name, options[name], false)) {
|
|||
|
const hooks = options[name];
|
|||
|
if (isArray(hooks)) {
|
|||
|
hooks.forEach((hook) => injectLifecycleHook(name, hook, publicThis, instance));
|
|||
|
}
|
|||
|
else {
|
|||
|
injectLifecycleHook(name, hooks, publicThis, instance);
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
if (mpType === 'page') {
|
|||
|
instance.__isVisible = true;
|
|||
|
// 直接触发页面 onLoad、onShow 组件内的 onLoad 和 onShow 在注册时,直接触发一次
|
|||
|
try {
|
|||
|
invokeHook(publicThis, ON_LOAD, instance.attrs.__pageQuery);
|
|||
|
delete instance.attrs.__pageQuery;
|
|||
|
if (((_a = publicThis.$page) === null || _a === void 0 ? void 0 : _a.openType) !== 'preloadPage') {
|
|||
|
invokeHook(publicThis, ON_SHOW);
|
|||
|
}
|
|||
|
}
|
|||
|
catch (e) {
|
|||
|
console.error(e.message + LINEFEED + e.stack);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
function initRenderjs(options, instance) {
|
|||
|
initModules(instance, options.$renderjs, options['$' + RENDERJS_MODULES]);
|
|||
|
}
|
|||
|
function initModules(instance, modules, moduleIds = {}) {
|
|||
|
if (!isArray(modules)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
const ownerId = instance.uid;
|
|||
|
// 在vue的定制内核中,通过$wxsModules来判断事件函数源码中是否包含该模块调用
|
|||
|
// !$wxsModules.find(module => invokerSourceCode.indexOf('.' + module + '.') > -1)
|
|||
|
const $wxsModules = (instance.$wxsModules ||
|
|||
|
(instance.$wxsModules = []));
|
|||
|
const ctx = instance.ctx;
|
|||
|
modules.forEach((module) => {
|
|||
|
if (moduleIds[module]) {
|
|||
|
ctx[module] = proxyModule(ownerId, moduleIds[module], module);
|
|||
|
$wxsModules.push(module);
|
|||
|
}
|
|||
|
else {
|
|||
|
if ((process.env.NODE_ENV !== 'production')) {
|
|||
|
console.error(formatLog('initModules', modules, moduleIds));
|
|||
|
}
|
|||
|
}
|
|||
|
});
|
|||
|
}
|
|||
|
function proxyModule(ownerId, moduleId, module) {
|
|||
|
const target = {};
|
|||
|
return new Proxy(target, {
|
|||
|
get(_, p) {
|
|||
|
return (target[p] ||
|
|||
|
(target[p] = createModuleFunction(ownerId, moduleId, module, p)));
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
function createModuleFunction(ownerId, moduleId, module, name) {
|
|||
|
const target = () => { };
|
|||
|
const toJSON = () => WXS_PROTOCOL + JSON.stringify([ownerId, moduleId, module + '.' + name]);
|
|||
|
return new Proxy(target, {
|
|||
|
get(_, p) {
|
|||
|
if (p === 'toJSON') {
|
|||
|
return toJSON;
|
|||
|
}
|
|||
|
return (target[p] ||
|
|||
|
(target[p] = createModuleFunction(ownerId, moduleId, module + '.' + name, p)));
|
|||
|
},
|
|||
|
apply(_target, _thisArg, args) {
|
|||
|
return (WXS_PROTOCOL +
|
|||
|
JSON.stringify([ownerId, moduleId, module + '.' + name, [...args]]));
|
|||
|
},
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
function initWxs(options, instance) {
|
|||
|
initModules(instance, options.$wxs, options['$' + WXS_MODULES]);
|
|||
|
}
|
|||
|
|
|||
|
function applyOptions(options, instance, publicThis) {
|
|||
|
{
|
|||
|
initWxs(options, instance);
|
|||
|
initRenderjs(options, instance);
|
|||
|
}
|
|||
|
initHooks(options, instance, publicThis);
|
|||
|
}
|
|||
|
|
|||
|
function set(target, key, val) {
|
|||
|
return (target[key] = val);
|
|||
|
}
|
|||
|
|
|||
|
function createErrorHandler(app) {
|
|||
|
return function errorHandler(err, instance, _info) {
|
|||
|
if (!instance) {
|
|||
|
throw err;
|
|||
|
}
|
|||
|
const appInstance = app._instance;
|
|||
|
if (!appInstance || !appInstance.proxy) {
|
|||
|
throw err;
|
|||
|
}
|
|||
|
{
|
|||
|
invokeHook(appInstance.proxy, ON_ERROR, err);
|
|||
|
}
|
|||
|
};
|
|||
|
}
|
|||
|
function mergeAsArray(to, from) {
|
|||
|
return to ? [...new Set([].concat(to, from))] : from;
|
|||
|
}
|
|||
|
function initOptionMergeStrategies(optionMergeStrategies) {
|
|||
|
UniLifecycleHooks.forEach((name) => {
|
|||
|
optionMergeStrategies[name] = mergeAsArray;
|
|||
|
});
|
|||
|
}
|
|||
|
|
|||
|
let realAtob;
|
|||
|
const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
|
|||
|
const b64re = /^(?:[A-Za-z\d+/]{4})*?(?:[A-Za-z\d+/]{2}(?:==)?|[A-Za-z\d+/]{3}=?)?$/;
|
|||
|
if (typeof atob !== 'function') {
|
|||
|
realAtob = function (str) {
|
|||
|
str = String(str).replace(/[\t\n\f\r ]+/g, '');
|
|||
|
if (!b64re.test(str)) {
|
|||
|
throw new Error("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
|
|||
|
}
|
|||
|
// Adding the padding if missing, for semplicity
|
|||
|
str += '=='.slice(2 - (str.length & 3));
|
|||
|
var bitmap;
|
|||
|
var result = '';
|
|||
|
var r1;
|
|||
|
var r2;
|
|||
|
var i = 0;
|
|||
|
for (; i < str.length;) {
|
|||
|
bitmap =
|
|||
|
(b64.indexOf(str.charAt(i++)) << 18) |
|
|||
|
(b64.indexOf(str.charAt(i++)) << 12) |
|
|||
|
((r1 = b64.indexOf(str.charAt(i++))) << 6) |
|
|||
|
(r2 = b64.indexOf(str.charAt(i++)));
|
|||
|
result +=
|
|||
|
r1 === 64
|
|||
|
? String.fromCharCode((bitmap >> 16) & 255)
|
|||
|
: r2 === 64
|
|||
|
? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
|
|||
|
: String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
|
|||
|
}
|
|||
|
return result;
|
|||
|
};
|
|||
|
}
|
|||
|
else {
|
|||
|
// 注意atob只能在全局对象上调用,例如:`const Base64 = {atob};Base64.atob('xxxx')`是错误的用法
|
|||
|
realAtob = atob;
|
|||
|
}
|
|||
|
function b64DecodeUnicode(str) {
|
|||
|
return decodeURIComponent(realAtob(str)
|
|||
|
.split('')
|
|||
|
.map(function (c) {
|
|||
|
return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
|
|||
|
})
|
|||
|
.join(''));
|
|||
|
}
|
|||
|
function getCurrentUserInfo() {
|
|||
|
const token = uni.getStorageSync('uni_id_token') || '';
|
|||
|
const tokenArr = token.split('.');
|
|||
|
if (!token || tokenArr.length !== 3) {
|
|||
|
return {
|
|||
|
uid: null,
|
|||
|
role: [],
|
|||
|
permission: [],
|
|||
|
tokenExpired: 0,
|
|||
|
};
|
|||
|
}
|
|||
|
let userInfo;
|
|||
|
try {
|
|||
|
userInfo = JSON.parse(b64DecodeUnicode(tokenArr[1]));
|
|||
|
}
|
|||
|
catch (error) {
|
|||
|
throw new Error('获取当前用户信息出错,详细错误信息为:' + error.message);
|
|||
|
}
|
|||
|
userInfo.tokenExpired = userInfo.exp * 1000;
|
|||
|
delete userInfo.exp;
|
|||
|
delete userInfo.iat;
|
|||
|
return userInfo;
|
|||
|
}
|
|||
|
function uniIdMixin(globalProperties) {
|
|||
|
globalProperties.uniIDHasRole = function (roleId) {
|
|||
|
const { role } = getCurrentUserInfo();
|
|||
|
return role.indexOf(roleId) > -1;
|
|||
|
};
|
|||
|
globalProperties.uniIDHasPermission = function (permissionId) {
|
|||
|
const { permission } = getCurrentUserInfo();
|
|||
|
return this.uniIDHasRole('admin') || permission.indexOf(permissionId) > -1;
|
|||
|
};
|
|||
|
globalProperties.uniIDTokenValid = function () {
|
|||
|
const { tokenExpired } = getCurrentUserInfo();
|
|||
|
return tokenExpired > Date.now();
|
|||
|
};
|
|||
|
}
|
|||
|
|
|||
|
function initApp(app) {
|
|||
|
const appConfig = app._context.config;
|
|||
|
appConfig.errorHandler = invokeCreateErrorHandler(app, createErrorHandler);
|
|||
|
initOptionMergeStrategies(appConfig.optionMergeStrategies);
|
|||
|
const globalProperties = appConfig.globalProperties;
|
|||
|
{
|
|||
|
uniIdMixin(globalProperties);
|
|||
|
}
|
|||
|
{
|
|||
|
globalProperties.$set = set;
|
|||
|
globalProperties.$applyOptions = applyOptions;
|
|||
|
}
|
|||
|
{
|
|||
|
invokeCreateVueAppHook(app);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
export { initApp };
|