UNPKG

egg-born-front

Version:
755 lines (745 loc) 25.7 kB
import mparse from 'egg-born-mparse'; import moment from 'moment'; import * as uuid from 'uuid'; import cookies from 'js-cookie'; import queue from 'async/queue'; import debounce from '@zhennann/debounce'; import currency from '@zhennann/currency'; import extend from '@zhennann/extend'; import clipboardFn from './clipboard.js'; import sandboxFn from './sandbox.js'; import screenfull from './screenfull.jsx'; import visibilityChange from './visibilityChange.jsx'; import requirejsFn from './requirejs.js'; // eslint-disable-next-line import localeZhcn from 'moment/locale/zh-cn.js'; import _escape from './escape.js'; import hostUtil from './hostUtil.js'; const __ViewSizes = ['small', 'medium', 'large']; export default function (Vue) { const _ids = {}; const util = { overrideProperty({ obj, key, objBase, vueComponent, combinePath }) { Object.defineProperty(obj, key, { get() { return function () { const moduleInfo = vueComponent && vueComponent.$module && vueComponent.$module.info; const args = new Array(arguments.length); args[0] = combinePath(moduleInfo, arguments[0]); for (let i = 1; i < args.length; i++) { args[i] = arguments[i]; } return objBase[key].apply(objBase, args); }; }, }); }, removeAppLoading() { // eslint-disable-next-line const loading = window.document.getElementById('app-loading'); loading && loading.parentNode.removeChild(loading); }, clearRouterHistory() { Vue.prototype.$Framework7.history.state = null; history.replaceState(null, '', location.href.split('#')[0]); Object.keys(window.localStorage).forEach(key => { if (key.indexOf('f7router-') === 0) window.localStorage.removeItem(key); }); }, async createComponentOptionsUses(component) { return await this.useModules(component.meta && component.meta.uses); }, async preloadModules(modules, options) { options = options || {}; const delay = options.delay || Vue.prototype.$meta.config.preload.delay; window.setTimeout(() => { this.useModules(modules); }, delay); }, async useModules(modules) { if (!modules) return; if (!Array.isArray(modules)) modules = modules.split(','); modules = modules.filter(module => !Vue.prototype.$meta.module.get(module)); if (modules.length === 0) return; const promises = modules.map(module => Vue.prototype.$meta.module.use(module)); await Promise.all(promises); }, createComponentOptions(component) { // installFactory if (component.installFactory) { component = Vue.util.mergeOptions(component, component.installFactory(Vue)); component._Ctor = {}; delete component.installFactory; Vue.extend(component); this._setComponentGlobal(component); } return component; }, _setComponentGlobal(component) { // register if (component.meta && component.meta.global === true) { if (!Vue.options.components[component.name]) { Vue.component(component.name, component); } } return component; }, _setComponentModule(component, module) { if (component) { component.__ebModuleRelativeName = module.info.relativeName; } }, _locationFullPathName() { return location.origin + location.pathname; }, parseHash(url) { if (!url || url === '/') return '/'; // support external url if ( (url.indexOf('https://') === 0 || url.indexOf('http://') === 0) && url.indexOf(this._locationFullPathName()) === -1 ) { return url; } return url.split(Vue.prototype.$f7.router.params.pushStateSeparator)[1] || '/'; }, combineHash(hash) { hash = hash || ''; return `${location.origin}${location.pathname}${location.search}${Vue.prototype.$f7.router.params.pushStateSeparator}${hash}`; }, historyUrlEmpty(historyUrl) { if (!historyUrl || historyUrl === '/') return true; const router = Vue.prototype.$f7.router; if (!router.params.pushStateSeparator || historyUrl.indexOf(router.params.pushStateSeparator) < 0) return false; historyUrl = historyUrl.split(router.params.pushStateSeparator)[1]; return !historyUrl || historyUrl === '/'; }, isPromise(value) { return value && typeof value === 'object' && typeof value.then === 'function'; }, wrapPromise(promise) { if (!this.isPromise(promise)) { return Promise.resolve(promise); } return promise; }, sleep(ms) { return new Promise(reslove => { window.setTimeout(() => { reslove(); }, ms); }); }, nextId(scene) { scene = scene || 'default'; if (!_ids.scene) _ids.scene = 1; else _ids.scene++; return `${scene}_${_ids.scene}`; }, uuidv4() { return uuid.v4().replace(/-/g, ''); }, fromNow(date) { if (typeof date !== 'object') date = new Date(date); return moment(date).fromNow(); }, formatDateTime(date, fmt) { if (!fmt || fmt === true) { fmt = 'YYYY-MM-DD HH:mm:ss'; } else if (fmt === 'date') { fmt = 'YYYY-MM-DD'; } else if (fmt === 'time') { fmt = 'HH:mm:ss'; } date = date || new Date(); if (typeof date !== 'object') date = new Date(date); return moment(date).format(fmt); }, formatDate(date, sep) { if (sep === undefined) sep = '-'; const fmt = `YYYY${sep}MM${sep}DD`; return this.formatDateTime(date, fmt); }, formatTime(date, sep) { if (sep === undefined) sep = ':'; const fmt = `HH${sep}mm${sep}ss`; return this.formatDateTime(date, fmt); }, formatDateTimeRelative(date, fmt) { if (this.formatDate() === this.formatDate(date)) return this.formatTime(date); return this.formatDateTime(date, fmt); }, swipeoutClose(target) { Vue.prototype.$f7.swipeout.close(Vue.prototype.$$(target).closest('.swipeout')); }, swipeoutDelete(target) { Vue.prototype.$f7.swipeout.delete(Vue.prototype.$$(target).closest('.swipeout')); }, replaceTemplate(content, scope) { if (!content) return null; return content.toString().replace(/(\\)?{{ *([\w\.]+) *}}/g, (block, skip, key) => { if (skip) { return block.substring(skip.length); } const value = this.getProperty(scope, key); return value !== undefined ? value : ''; }); }, parseModuleInfo(moduleName) { return mparse.parseInfo(moduleName); }, getBaseURL() { return Vue.prototype.$meta.config.api.baseURL || window.location.origin; }, combineApiPath(moduleName, arg) { if (arg.charAt(0) === '/') return arg; const moduleInfo = typeof moduleName === 'string' ? mparse.parseInfo(moduleName) : moduleName; return `/${moduleInfo.url}/${arg}`; }, combineFetchStaticPath(arg) { return this.combineFetchPath(null, '/' + arg); }, combineFetchPath(moduleName, arg) { let url = this._combineFetchPath(moduleName, arg); url = `${this.getBaseURL()}${url}`; return url; }, _combineFetchPath(moduleName, arg) { if (arg.substr(0, 2) === '//') return arg.substr(1); if (arg.charAt(0) === '/') return `/api${arg}`; const moduleInfo = typeof moduleName === 'string' ? mparse.parseInfo(moduleName) : moduleName; return `/api/${moduleInfo.url}/${arg}`; }, combineStaticPath(moduleName, arg) { let url = this._combineStaticPath(moduleName, arg); url = `${this.getBaseURL()}${url}`; return url; }, _combineStaticPath(moduleName, arg) { if (arg.substr(0, 2) === '//') return arg.substr(1); if (arg.charAt(0) === '/') return `/api/static${arg}`; const moduleInfo = typeof moduleName === 'string' ? mparse.parseInfo(moduleName) : moduleName; return `/api/static/${moduleInfo.url}/${arg}`; }, combineStaticPathFront(arg) { return `${location.origin}${location.pathname}static/${arg}`; }, combineStorePath(moduleName, arg) { if (arg.substr(0) === '/') return arg.substr(1); const moduleInfo = typeof moduleName === 'string' ? mparse.parseInfo(moduleName) : moduleName; return `${moduleInfo.url}/${arg}`; }, combinePagePath(moduleName, arg) { if (!arg || typeof arg !== 'string') return arg; if (arg.indexOf('https://') === 0 || arg.indexOf('http://') === 0) return arg; const first = arg.charAt(0); if (first === '/' || first === '#') return arg; const moduleInfo = typeof moduleName === 'string' ? mparse.parseInfo(moduleName) : moduleName; return `/${moduleInfo.url}/${arg}`; }, createComponentInstance(component, options) { if (!component) throw new Error('component should not be null'); // const _component = Object.assign({}, component, options); let _component = this.extend({}, component); _component = Object.assign(_component, options); return new Vue(_component); }, _combineComponentsProps(parent, component) { if (component.mixins) { for (const mixin of component.mixins) { this._combineComponentsProps(parent, mixin); } } Object.assign(parent, component.props); }, async performAction(args) { const { ctx, action, item } = args; // actionPath if (!action.actionComponent) { const url = action.actionPath ? this.combinePagePath(action.actionModule, this.replaceTemplate(action.actionPath, item)) : null; const options = Object.assign({}, action.navigateOptions, { context: { params: { item, }, }, }); this.navigate({ ctx, url, options }); return; } // actionComponent const module = await Vue.prototype.$meta.module.use(action.actionModule); const component = module.options.components[action.actionComponent]; if (!component) throw new Error(`actionComponent not found: ${action.actionModule}:${action.actionComponent}`); // componentProps const componentProps = {}; this._combineComponentsProps(componentProps, component); // options const options = {}; if (componentProps) { options.propsData = {}; for (const key in componentProps) { options.propsData[key] = args[key]; } } // create instance const componentInstance = this.createComponentInstance(component, options); try { const res = await componentInstance.onAction(args); componentInstance.$destroy(); return res; } catch (err) { componentInstance.$destroy(); throw err; } }, performActionSync(args) { const { action } = args; // actionComponent // should load module before the call const module = Vue.prototype.$meta.module.get(action.actionModule); if (!module) throw new Error(`actionModule not found: ${action.actionModule}:${action.actionComponent}`); const component = module.options.components[action.actionComponent]; if (!component) throw new Error(`actionComponent not found: ${action.actionModule}:${action.actionComponent}`); // componentProps const componentProps = {}; this._combineComponentsProps(componentProps, component); // options const options = {}; if (componentProps) { options.propsData = {}; for (const key in componentProps) { options.propsData[key] = args[key]; } } // create instance const componentInstance = this.createComponentInstance(component, options); try { const res = componentInstance.onAction(args); componentInstance.$destroy(); return res; } catch (err) { componentInstance.$destroy(); throw err; } }, navigate({ ctx, url, options }) { const view = ctx.$view; if (view) { view.navigate(url, options); } else { ctx.$meta.vueLayout.navigate(url, options); } }, setProperty(obj, name, value) { const names = name.split('.'); if (names.length === 1) { obj[name] = value; } else { for (let i = 0; i < names.length - 1; i++) { const _obj = obj[names[i]]; if (_obj) { obj = _obj; } else { obj = obj[names[i]] = {}; } } obj[names[names.length - 1]] = value; } }, getPropertyDeprecate(obj, name, nameDeprecate, sep) { let value = this.getProperty(obj, name, sep); if (value !== undefined) return value; value = this.getProperty(obj, nameDeprecate, sep); if (value !== undefined) { console.warn(`DeprecationWarning: \`${nameDeprecate}\` is deprecated. Use \`${name}\` instead.`); return value; } return undefined; }, getProperty(obj, name, sep) { return this._getProperty(obj, name, sep, false); }, getPropertyObject(obj, name, sep) { return this._getProperty(obj, name, sep, true); }, _getProperty(obj, name, sep, forceObject) { if (!obj) return undefined; const names = name.split(sep || '.'); // loop for (const name of names) { if (obj[name] === undefined || obj[name] === null) { if (forceObject) { obj[name] = {}; } else { obj = obj[name]; break; } } obj = obj[name]; } return obj; }, combineStoreUrl(entityName) { const locale = this.getLocale(); return `https://store.cabloy.com/${locale === 'zh-cn' ? 'zh-cn/' : ''}articles/${entityName}.html`; }, combineAvatarUrl(url, width, height) { const media = url || Vue.prototype.$meta.config.modules['a-base'].user.avatar.default; return this.combineImageUrl(media, width, height); }, combineImageUrl(url, width, height) { if (!url) return url; if (url.indexOf('data:image/') === 0) return url; if (!width && !height) return url; const pixelRatio = Vue.prototype.$device.pixelRatio; let query = ''; if (width) query = `width=${parseInt(width) * pixelRatio}`; if (height) query = `${query ? query + '&' : ''}height=${parseInt(height) * pixelRatio}`; return `${url}${url.charAt(url.length - 1) === '?' ? '' : '?'}${query}`; }, combineQueries(url, queries) { // if (!queries) return url; // let str = ''; for (const key of Object.keys(queries)) { const value = queries[key]; if (value !== null && value !== undefined) { str += `${key}=${encodeURIComponent(value)}&`; } } if (str) { str = str.substr(0, str.length - 1); } if (!str) return url; // if (!url) return '?' + str; // const pos = url.indexOf('?'); if (pos === -1) return `${url}?${str}`; if (pos === url.length - 1) return `${url}${str}`; return `${url}&${str}`; }, loadScript(src, callback) { if (callback) { const check = document.querySelectorAll("script[src='" + src + "']"); if (check.length > 0) { check[0].addEventListener('load', function () { callback(); }); callback(); return; } const script = document.createElement('script'); const head = document.getElementsByTagName('head')[0]; script.type = 'text/javascript'; script.charset = 'UTF-8'; script.async = true; script.src = src; if (script.addEventListener) { script.addEventListener( 'load', function () { callback(); }, false ); } else if (script.attachEvent) { script.attachEvent('onreadystatechange', function () { const target = window.event.srcElement; if (target.readyState === 'loaded') { callback(); } }); } head.appendChild(script); } else { return new Promise(resolve => { this.loadScript(src, () => { resolve(); }); }); } }, loadLink(src, callback) { if (callback) { const check = document.querySelectorAll("link[href='" + src + "']"); if (check.length > 0) { callback(); return; } const link = document.createElement('link'); const head = document.getElementsByTagName('head')[0]; link.rel = 'stylesheet'; link.href = src; if (link.addEventListener) { link.addEventListener( 'load', function () { callback(); }, false ); } else if (link.attachEvent) { link.attachEvent('onreadystatechange', function () { const target = window.event.srcElement; if (target.readyState === 'loaded') { callback(); } }); } head.appendChild(link); } else { return new Promise(resolve => { this.loadLink(src, () => { resolve(); }); }); } }, removeClassLike($el, className) { const classes = className.split(' '); for (let i = 0; i < classes.length; i += 1) { for (let j = 0; j < $el.length; j += 1) { if (typeof $el[j] !== 'undefined' && typeof $el[j].classList !== 'undefined') { __removeClassLike($el[j].classList, classes[i]); } } } }, setLocale(locale) { if (Vue.prototype.$meta.config.base.jwt) { window.localStorage['eb-locale'] = locale; } else { this.cookies.set('locale', locale); } }, getLocale() { let locale; // localStorage / cookies if (Vue.prototype.$meta.config.base.jwt) { locale = window.localStorage['eb-locale']; } else { locale = this.cookies.get('locale'); } if (locale) return locale; // navigator.language locale = this.preferredLocale(window.navigator.language); if (locale) return locale; // config locale = Vue.prototype.$meta.config.base.locale; if (locale) return locale; // default return 'en-us'; }, getLocales() { // _locales const loginInfo = Vue.prototype.$meta.store.getState('auth/loginInfo'); const _locales = loginInfo && loginInfo.locales; // locales let locales; if (_locales) { locales = {}; for (const item of _locales) { locales[item.value] = item.title; } } else { locales = Vue.prototype.$meta.config.locales; } return locales; }, preferredLocale(locale) { locale = locale.toLowerCase().replace(/_/g, '-'); const locales = this.getLocales(); // match exactly if (locales[locale]) return locale; // match fuzzy const localeShort = locale.split('-')[0]; return Object.keys(locales).find(item => item.indexOf(localeShort) === 0); }, viewRouterDid(view) { const f7 = Vue.prototype.$vuef7; let viewRouter; f7.routers.views.forEach(data => { if (data.el && data.el === view.$el) { viewRouter = data; } }); const urlNew = viewRouter.component.$f7route.url; const urlCurrent = view.f7View.router.currentRoute.url; return urlNew === urlCurrent; }, fn2workerURL(fn) { const blob = new Blob(['(' + fn + ')()'], { type: 'text/javascript' }); return URL.createObjectURL(blob); }, async combineSearchClauseLoadModules({ schema }) { if (!schema) return; const properties = schema.schema.properties; for (const key in properties) { const property = properties[key]; const ebSearch = property.ebSearch; if (ebSearch === false) continue; if (ebSearch && ebSearch.combine) { const actionModule = ebSearch.combine.actionModule; const actionComponent = ebSearch.combine.actionComponent; if (!actionModule || !actionComponent) { throw new Error(`ebSearch.combine not set properly for property: ${key}`); } await Vue.prototype.$meta.module.use(actionModule); } } }, combineSearchClause({ ctx, schema, data, searchStates }) { if (!schema || !data) return null; const clause = {}; const properties = schema.schema.properties; for (const key in properties) { const property = properties[key]; const ebSearch = property.ebSearch; if (ebSearch === false) continue; // dataPath const dataPath = key; // value const value = data[key]; // operator const operator = this._combineSearchParseOperator({ property, operator: searchStates && searchStates[dataPath], }); // combine let res; if (ebSearch && ebSearch.combine) { res = this.performActionSync({ ctx, action: ebSearch.combine, item: { key, property, dataPath, value, operator, schema, data, searchStates }, }); } else { res = this._combineSearchClause({ key, property, value, operator }); } if (res) { Object.assign(clause, res); } } return clause; }, _combineSearchParseOperator({ property, operator }) { if (operator) return operator; const ebSearch = property.ebSearch; let operators = ebSearch && ebSearch.operators; let op; if (!operators) { op = property.type === 'string' ? 'like' : '='; } else { if (!Array.isArray(operators)) operators = operators.split(','); op = operators[0]; } return { op }; }, _combineSearchClause({ key, property, value, operator }) { const ebSearch = property.ebSearch; if (!property.type) return null; if (this.checkIfEmptyForSelect(value)) return null; if (ebSearch && ebSearch.ignoreValue === value) return null; let tableAlias = ebSearch && ebSearch.tableAlias; tableAlias = tableAlias === null ? null : tableAlias || 'f'; const fieldName = (ebSearch && ebSearch.fieldName) || key; const clauseName = tableAlias === null ? fieldName : `${tableAlias}.${fieldName}`; const clauseValue = { op: operator.op, val: value, }; return { [clauseName]: clauseValue }; }, checkIfEmptyForSelect(value) { return value === '' || value === undefined || value === null; }, _checkIfIconF7Default(iconF7) { if (!iconF7) return true; return iconF7.indexOf('/api/static/') === -1 && iconF7.split(':').length < 3; }, async combineIcon({ material, f7, color, size }) { return await Vue.prototype.$meta.store.dispatch('a/icon/combineIcon', { material, icon: f7, color, size }); }, getJwtAuthorization() { let oauth = window.localStorage['eb-jwt-oauth']; if (!oauth) return ''; oauth = JSON.parse(oauth); return oauth.expireTime - Date.now() > 120 * 1000 ? oauth.accessToken : oauth.refreshToken; }, combineLoginUrl({ providerModule, providerName, providerScene }) { const urlParamScene = providerScene ? `/${providerScene}` : ''; return `/api/a/auth/passport/${providerModule}/${providerName}${urlParamScene}`; }, normalizeResourceKey(key, module, sep = ':') { if (!key) return key; let _sep, _parts; for (let index = 0; index < sep.length; index++) { _sep = sep[index]; _parts = key.split(_sep); if (_parts.length > 1) break; } if (_parts.length === 1 && module) { _parts.unshift(module); } return _parts.join(_sep); }, compareViewSize(sizeA, sizeB) { return __ViewSizes.indexOf(sizeA) - __ViewSizes.indexOf(sizeB); }, get emptyIcon() { return '<i class="icon"></i>'; }, }; // moment window.moment = moment; // requirejs let requirejs; Object.defineProperty(util, 'requirejs', { get() { if (!requirejs) { requirejs = requirejsFn(Vue); } return requirejs; }, }); // mixin Object.assign(util, { clipboard: clipboardFn(Vue), sandbox: sandboxFn(Vue), moment, uuid, queue, cookies, debounce, currency, extend(...args) { return extend(true, ...args); }, escapeHtml: _escape.escapeHtml, escapeURL: _escape.escapeURL, hostUtil: hostUtil(Vue, util), }); // screenfull util.screenfull = util.createComponentInstance(screenfull); // visibilityChange util.visibilityChange = util.createComponentInstance(visibilityChange); // // test: // window.setTimeout(() => { // util.requirejs.require(['api/static/a/markdownblock/blocks/audio/audio'], function () { // console.log('audio.js loaded'); // }); // }, 0); // ok return util; } function __removeClassLike(classList, classNameLike) { for (let i = classList.length - 1; i >= 0; i--) { const item = classList.item(i); if (item.indexOf(classNameLike) > -1) classList.remove(item); } }