@ams-team/ams
Version:
Admin Materials System.
294 lines (274 loc) • 10.7 kB
JavaScript
/* eslint-disable complexity,no-new,max-depth */
import Vue from 'vue';
import initRequest from './request';
import initConfig from './config';
import { filter } from '../utils/tpl';
let amsRootId = 0;
const ams = {
// 特殊参数、保存上一次action的返回值
$prevReturn: '',
/**
* 属性
*/
// 全局默认actios
actions: {},
// 扁平的blocks结构
blocks: {},
// blocks的vue对象引用、会根据vue生命周期自动注册卸载
$blocks: {},
resources: {},
bus: null,
$router: null,
/**
* 方法
*/
_setBlocks(block) {
let blocks = block.blocks;
const arr = [];
Object.keys(blocks).forEach(blockId => {
let subBlock = blocks[blockId];
this.block(blockId, subBlock);
// slot 插槽,无slot block为默认子blocks
if (subBlock.slot) {
if (!block.slotBlocks[subBlock.slot]) {
block.slotBlocks[subBlock.slot] = [];
}
block.slotBlocks[subBlock.slot].push(blockId);
} else {
arr.push(blockId);
}
});
block.blocks = arr;
},
action(name, action) {
if (this.actions[name]) {
return console.warn('重复注册action:', name);
}
this.actions[name] = action;
},
resource(name, resource) {
// 合并BASE简化配置
if (resource) {
resource = this.deepCloneConfig(resource);
// console.log(name, resource);
resource.api = resource.api || {};
resource.key = resource.key || 'id';
if (name) {
this.resources[name] = resource;
}
}
return resource;
},
// 封装同步和异步block
async getBlock(name) {
let block = ams.blocks[name];
if (ams.configs.consoleWarn && !block) {
console.warn(`ams Err: can not find block: ${name}, please run ams.block() first`);
}
if (typeof block === 'function') {
block = await block();
if (block) {
delete ams.blocks[name];
ams.block(name, block);
}
}
return ams.blocks[name];
},
block(name, block) {
if (this.blocks[name]) {
console.warn('重复注册block:', name);
}
// 异步函数通过函数包装,延迟初始化
if (typeof block === 'function') {
this.blocks[name] = block;
return;
}
// 合并BASE简化配置
const defaultBlockConfig = ams.configs.defaultBlockConfig && ams.configs.defaultBlockConfig[block.type];
block = this.deepCloneConfig(block, defaultBlockConfig);
// 初始化props
block.props = {
...ams.configs.defaultBlockProps[block.type],
...block.props
};
// 通用配置
block.options = block.options || {};
// 注册resources
if (block.resources) {
Object.keys(block.resources).forEach(key =>
this.resource(key, block.resources[key])
);
}
// 合并全局config
if (block.config) {
this.config(block.config);
delete block.config;
}
block.slotBlocks = block.slotBlocks || {};
// 拉平block结构,换成名字引用
if (block.blocks && !Array.isArray(block.blocks)) {
this._setBlocks(block);
}
// 初始化data
block.data = {
...ams.configs.defaultBlockDatas[block.type],
...block.data
};
// 初始化events
block.events = block.events || {};
// 初始化actions
block.actions = block.actions || {};
// 初始化列表参数
if (this.configs.baseBlockType[block.type] === 'list') {
block.data.pageSize = block.data.pageSize || block.pageSize || 20;
block.data.pageSizes = block.data.pageSizes || [10, 20, 30, 40, 50, 100];
block.data.layout = block.data.layout || 'total, prev, sizes, pager, next, jumper';
block.data.page = block.data.page || 1;
block.data.total = block.data.total || 0;
block.data.list = block.data.list || [];
// 列表默认值
block.sorts = block.sorts || {};
block.filters = block.filters || {};
if (block.searchs || block.searchsOptions) {
// !!!!! 搜索配置兼容旧版本及废弃提示 !!!!!
console.warn('列表的searchs和searchsOptions已在ams@0.6.0+废弃,请使用 slot operations 替代,如: \nsearchs:{testText:true} 修改为\noperations: { testText: {type: "field", field: "testText", slot: "searchs"} }');
}
} else if (block.type === 'dialog' || block.type === 'drawer') {
// 初始化dialog
block.data.visible = !!block.data.visible;
}
this.blocks[name] = block;
// render
if (block.render) {
this.render(name, block.render);
}
},
render(name, el) {
if (el === true || !el || (typeof el === 'string' && !document.querySelector(el))) {
el = document.createElement('div');
el.id = `ams-root-${++amsRootId}`;
document.body.appendChild(el);
}
const block = ams.blocks[name];
if (block) {
new Vue({
el,
template: `<ams-block name="${name}" />`
});
} else {
console.warn('未注册block:', name);
}
},
/**
* Register a listener on Nova's built-in event bus
*/
$on(...args) {
this.bus.$on(...args);
},
/**
* Register a one-time listener on the event bus
*/
$once(...args) {
this.bus.$once(...args);
},
/**
* De-register a listener on the event bus
*/
$off(...args) {
this.bus.$off(...args);
},
/**
* Emit an event on the event bus
*/
$emit(...args) {
this.bus.$emit(...args);
},
/**
* 调用action
*
* @param {String} maybeMultipleActionStr '@addDialog.show @clear @hide'
* @param {Object} args
*/
async callAction(maybeMultipleActionStr = '', args = {}) {
if (!maybeMultipleActionStr.trim()) return ams.$prevReturn;
// 用来解析:routePush:/detail-page/id=<%=data.ctx.id%> 之类的lodash.template
// 感觉这里放在,比如 routePush 这个函数里面去做更合适,但是这里面的正则无法兼容,因此不得不将其提前到callAction中对tpl进行处理
maybeMultipleActionStr = filter(maybeMultipleActionStr, { ctx: ams.$prevReturn });
/**
*
* actionName: '@list' -> ['@list']
* \s是指空白,包括空格、换行、tab缩进等所有的空白
* split(/\s+/) 简单理解:split('空白字符<主要是空格>'):
*
* eg:
* 'a b c'.split(/\s+/) -> ['a', 'b', 'c']
* @addDialog.show @clear' -> ['@adminDialog.show', '@clear']
*
*/
const maybeMultipleActionStrArr = maybeMultipleActionStr.split(/\s+/);
for (let i = 0; i < maybeMultipleActionStrArr.length; i++) {
let actionStr = maybeMultipleActionStrArr[i];
if (actionStr) {
/**
* 举例几种场景的处理:
*
* #1
* actionStr: "@list"
* actionDetail: ["@list", "@", undefined, "list", undefined, index: 0, input: "@list", groups: undefined]
*
* #2
* actionStr: "addDialogForm.submit"
* -> actionDetail: ["addDialogForm.submit", undefined, "addDialogForm", "submit", undefined, index: 0, input: "addDialogForm.submit", groups: undefined]
*
* #3
* actionStr: "@addDialogForm.submit"
* -> actionDetail: ["addDialogForm.submit", "@", "addDialogForm", "submit", undefined, index: 0, input: "addDialogForm.submit", groups: undefined]
*
* 理解正则
* https://regexper.com/#%2F%5E%28%40%29%3F%28%3F%3A%28.*%3F%29%5C.%29%3F%28.*%3F%29%28%3F%3A%3A%28.*%29%29%3F%24%2F
*/
// actionDetail action字符串的组成
let actionDetail = /^(@)?(?:(.*?)\.)?(.*?)(?::(.*))?$/.exec(actionStr);
// eslint-disable-next-line no-unused-vars
const [rawString, at, blockName, actionName, argument] = actionDetail;
if (actionDetail) {
const target = blockName ? ams.$blocks[blockName] : this;
// args标准参数,如event配置为 @block.action:arg1,arg2,参数为args1,arg2的字符串
// 先取argument, argument为手动输入优先级应该高于从上一个action传递下来的args.$arg
args.$arg = argument || args.$arg || '';
args.$context = this;
args.$prevReturn = ams.$prevReturn;
// 事件 event
if (!at) {
if (target && target.emitEvent) {
// /ams/src/ams/mixins/block-mixin.js
await target.emitEvent(actionName, args);
}
// action
} else {
if (target) {
const action = (target.block && target.block.actions && target.block.actions[actionName]) || ams.actions[actionName];
if (action) {
let result = await action.call(
target,
args
);
// 保存当前结果,下一个action的args.$prevReturn用来取值
if (result) {
// 只有需要显式return数据的场景才记录$prevReturn,方便控制需要关心的$prevReturn参数传递
ams.$prevReturn = result;
}
}
}
}
}
}
}
return ams.$prevReturn;
}
};
// 初始化request相关
initRequest(ams);
// 初始化config相关
initConfig(ams);
export default ams;