You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
8.8 KiB
276 lines
8.8 KiB
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 };
|
|
|