Comparing version 0.1.4 to 0.2.2
{ | ||
"name": "bat-ria", | ||
"version": "0.1.4", | ||
"description": "RIA extension for Brand Ads Team", | ||
"main": "main.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/ecomfe/bat-ria.git" | ||
}, | ||
"keywords": [ | ||
"RIA", | ||
"esui", | ||
"er" | ||
], | ||
"dependencies": { | ||
"er": "3.1.x", | ||
"esui": ">=3.1.0-alpha.5", | ||
"ef": "3.1.x", | ||
"underscore": "1.4.x", | ||
"moment": "2.x", | ||
"etpl": ">=2.0.8", | ||
"urijs": ">=1.12.0" | ||
}, | ||
"author": "Justineo, chestnutchen", | ||
"license": "BSD-2-Clause", | ||
"bugs": { | ||
"url": "https://github.com/ecomfe/bat-ria/issues" | ||
}, | ||
"homepage": "https://github.com/ecomfe/bat-ria" | ||
"name": "bat-ria", | ||
"version": "0.2.2", | ||
"description": "RIA extension for Brand Ads Team", | ||
"main": "main.js", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/ecomfe/bat-ria.git" | ||
}, | ||
"keywords": [ | ||
"RIA", | ||
"esui", | ||
"er" | ||
], | ||
"edp": { | ||
"dependencies": { | ||
"er": ">=3.1.0-beta.3", | ||
"esui": ">=3.1.0-beta.3", | ||
"ef": "3.1.x", | ||
"er-track": "~0.9.1", | ||
"underscore": ">=1.6.0", | ||
"moment": "2.x", | ||
"etpl": ">=2.0.8", | ||
"urijs": ">=1.12.0", | ||
"eoo": "~0.0.9", | ||
"mini-event": "~1.0.2" | ||
} | ||
}, | ||
"author": "Justineo, chestnutchen, leeight", | ||
"license": "BSD-2-Clause", | ||
"bugs": { | ||
"url": "https://github.com/ecomfe/bat-ria/issues" | ||
}, | ||
"homepage": "https://github.com/ecomfe/bat-ria" | ||
} |
@@ -11,5 +11,5 @@ # BAT-RIA 扩展 | ||
在 `common/main` 中如此通过 MA-RIA 来启动系统: | ||
在 `common/main`(或其他入口模块)中如此通过 BAT-RIA 来启动系统: | ||
```javascript | ||
require('bat-ria/main').start().then(function () { | ||
require('bat-ria/main').start(config).then(function () { | ||
// custom system initialization | ||
@@ -16,0 +16,0 @@ }); |
@@ -8,3 +8,3 @@ /** | ||
var u = require('underscore'); | ||
var URI = require('urijs'); | ||
var uri = require('urijs'); | ||
var loading = require('../ui/loading'); | ||
@@ -14,33 +14,77 @@ | ||
var user = require('../system/user'); | ||
var aderId = user.ader && user.ader.id | ||
|| URI.parseQuery(document.location.search).aderId | ||
|| user.visitor && user.visitor.id; | ||
return aderId ? { aderId: aderId } : {}; | ||
var aderId = user.getAderId(); | ||
return u.purify({ | ||
aderId: aderId | ||
}); | ||
} | ||
function activate() { | ||
/** | ||
* 可用的钩子名称如下: | ||
* - SHOW_LOADING | ||
* - ADD_ADER_ID | ||
* - ADD_ER_REQUEST_HEADER | ||
* | ||
* 默认全部启用 | ||
*/ | ||
var hooks = { | ||
SHOW_LOADING: true, | ||
ADD_ADER_ID: true, | ||
ADD_ER_REQUEST_HEADER: true | ||
}; | ||
/** | ||
* 激活扩展 | ||
* | ||
* @param {Object} options 需要启用的钩子扩展,默认为都启用,键名为钩子名称,键值为falsy值时禁用 | ||
*/ | ||
function activate(options) { | ||
// 设定默认值 | ||
u.extend(hooks, options); | ||
var io = require('../io/serverIO'); | ||
io.hooks.filterIndexUrl = function(url) { | ||
return URI(url).addQuery(getAderArgMap()).toString(); | ||
}; | ||
if (hooks.ADD_ADER_ID) { | ||
io.hooks.filterIndexUrl = function(url) { | ||
return uri(url).addQuery(getAderArgMap()).toString(); | ||
}; | ||
var Uploader = require('../ui/Uploader'); | ||
Uploader.prototype.filterAction = function (action) { | ||
var argMap = getAderArgMap(); | ||
if (argMap) { | ||
action = uri(action).addQuery(argMap).toString(); | ||
} | ||
return action; | ||
}; | ||
} | ||
io.hooks.beforeRequest = function(options) { | ||
var url = options.url; | ||
var argMap = getAderArgMap(); | ||
if (argMap) { | ||
options.url = URI(url).addQuery(argMap).toString(); | ||
if (hooks.ADD_ADER_ID) { | ||
var url = options.url; | ||
var argMap = getAderArgMap(); | ||
if (argMap) { | ||
options.url = uri(url).addQuery(argMap).toString(); | ||
} | ||
} | ||
loading.show(); | ||
}; | ||
if (options.showLoading !== false && hooks.SHOW_LOADING) { | ||
loading.show(); | ||
} | ||
io.hooks.afterComplete = function() { | ||
loading.hide(); | ||
return options; | ||
}; | ||
var ajax = require('er/ajax'); | ||
ajax.hooks.beforeSend = function(xhr) { | ||
xhr.setRequestHeader('X-Request-By', 'ERApplication'); | ||
}; | ||
if (hooks.SHOW_LOADING) { | ||
io.hooks.afterComplete = function() { | ||
loading.hide(); | ||
}; | ||
} | ||
if (hooks.ADD_ER_REQUEST_HEADER) { | ||
var ajax = require('er/ajax'); | ||
ajax.hooks.beforeSend = function(xhr) { | ||
xhr.setRequestHeader('X-Request-By', 'ERApplication'); | ||
}; | ||
} | ||
} | ||
@@ -47,0 +91,0 @@ |
@@ -13,2 +13,3 @@ /** | ||
var lib = require('esui/lib'); | ||
var Control = require('esui/Control'); | ||
@@ -22,4 +23,6 @@ /** | ||
// 加载所有验证规则 | ||
var MaxLengthRule = require('esui/validator/MaxLengthRule'); | ||
var MinLengthRule = require('esui/validator/MinLengthRule'); | ||
require('esui/validator/MaxByteLengthRule'); | ||
require('esui/validator/MinByteLengthRule'); | ||
require('esui/validator/MaxLengthRule'); | ||
require('esui/validator/MinLengthRule'); | ||
var RequiredRule = require('esui/validator/RequiredRule'); | ||
@@ -72,7 +75,7 @@ var PatternRule = require('esui/validator/PatternRule'); | ||
var Rule = require('esui/validator/Rule'); | ||
var defaultGetErrorMessage = Rule.prototype.getErrorMessage; | ||
MaxLengthRule.prototype.getErrorMessage = function (control) { | ||
if (control.get('maxErrorMessage')) { | ||
var getErrorMessage = Rule.prototype.getErrorMessage; | ||
getErrorMessage.apply(this, arguments); | ||
function getErrorMessage(control) { | ||
if (control.get(this.type + 'ErrorMessage')) { | ||
return defaultGetErrorMessage.apply(this, arguments); | ||
} | ||
@@ -83,48 +86,12 @@ var rangeErrorMessage = getRangeErrorMessage(control); | ||
} | ||
return Rule.prototype.getErrorMessage.apply(this, arguments); | ||
}; | ||
MinLengthRule.prototype.getErrorMessage = function (control) { | ||
if (control.get('maxErrorMessage')) { | ||
var getErrorMessage = Rule.prototype.getErrorMessage; | ||
getErrorMessage.apply(this, arguments); | ||
} | ||
var rangeErrorMessage = getRangeErrorMessage(control); | ||
if (rangeErrorMessage) { | ||
return rangeErrorMessage; | ||
} | ||
return Rule.prototype.getErrorMessage.apply(this, arguments); | ||
}; | ||
MaxRule.prototype.getErrorMessage = function (control) { | ||
if (control.get('maxErrorMessage')) { | ||
var getErrorMessage = Rule.prototype.getErrorMessage; | ||
getErrorMessage.apply(this, arguments); | ||
} | ||
var rangeErrorMessage = getRangeErrorMessage(control); | ||
if (rangeErrorMessage) { | ||
return rangeErrorMessage; | ||
} | ||
return Rule.prototype.getErrorMessage.apply(this, arguments); | ||
}; | ||
return defaultGetErrorMessage.apply(this, arguments); | ||
} | ||
MinRule.prototype.getErrorMessage = function (control) { | ||
if (control.get('maxErrorMessage')) { | ||
var getErrorMessage = Rule.prototype.getErrorMessage; | ||
getErrorMessage.apply(this, arguments); | ||
} | ||
var rangeErrorMessage = getRangeErrorMessage(control); | ||
if (rangeErrorMessage) { | ||
return rangeErrorMessage; | ||
} | ||
return Rule.prototype.getErrorMessage.apply(this, arguments); | ||
}; | ||
MaxRule.prototype.getErrorMessage = getErrorMessage; | ||
MinRule.prototype.getErrorMessage = getErrorMessage; | ||
PatternRule.prototype.getErrorMessage = function (control) { | ||
var pattern = control.get('pattern') + ''; | ||
if (control.get('patternErrorMessage') | ||
|| !NUMBER_REGEX.hasOwnProperty(pattern) | ||
) { | ||
var getErrorMessage = Rule.prototype.getErrorMessage; | ||
getErrorMessage.apply(this, arguments); | ||
if (control.get('patternErrorMessage') || !NUMBER_REGEX.hasOwnProperty(pattern)) { | ||
return defaultGetErrorMessage.apply(this, arguments); | ||
} | ||
@@ -135,3 +102,3 @@ var rangeErrorMessage = getRangeErrorMessage(control); | ||
} | ||
return Rule.prototype.getErrorMessage.apply(this, arguments); | ||
return defaultGetErrorMessage.apply(this, arguments); | ||
}; | ||
@@ -179,5 +146,232 @@ } | ||
/** | ||
* 激活全局ESUI扩展 | ||
* | ||
* @ignore | ||
*/ | ||
function initializeGlobalExtensions() { | ||
var ui = require('esui'); | ||
var globalExtensions = [ | ||
// { type: 'CustomData', options: {} } | ||
]; | ||
u.each(globalExtensions, function (extension) { | ||
ui.attachExtension(extension.type, extension.options); | ||
}); | ||
} | ||
/** | ||
* esui升级前region的过渡扩展 | ||
* 增加获取最大地域个数的方法 | ||
* 增加多选状态下是否处于全选状态的判断 | ||
* 增加获取选中文本的方法 | ||
* 增加获取rawValue的时候即使省下边没有全选也把省份id返回的方法(adrc地区格式需求) | ||
* 增加设置rawValue时去掉非子节点的省份,并返回选中文本的方法(adrc地区格式需求) | ||
* | ||
* @ignore | ||
*/ | ||
function addRegionExtension() { | ||
var Region = require('esui/Region'); | ||
Region.prototype.getMaxRegionSize = function () { | ||
if (!this.maxRegionSize) { | ||
this.maxRegionSize = u.size(this.regionDataIndex); | ||
} | ||
return this.maxRegionSize; | ||
}; | ||
Region.prototype.isAllSelected = function () { | ||
if (this.mode === 'multi') { | ||
return this.getMaxRegionSize() === this.getRawValue().length; | ||
} | ||
// 不是多选就直接返回false吧 | ||
return false; | ||
}; | ||
/* | ||
* 获取地区的文本 | ||
* | ||
* @param isFilterParentNode {boolean} 是不是要把有子节点省份的文本过滤掉 | ||
* @param region {array} 指定地区的id范围,默认使用已选地区 | ||
* @return {string} 选中的地区的文本 | ||
*/ | ||
Region.prototype.getRegionText = function (isFilterParentNode, region) { | ||
var me = this; | ||
var rawValue = region || this.getRawValue(); | ||
var regionTextArr = []; | ||
if (isFilterParentNode) { | ||
u.each(rawValue, function (id) { | ||
var item = me.regionDataIndex[id]; | ||
if (!item.children) { | ||
var tmpText = item.text; | ||
if (tmpText) { | ||
regionTextArr.push(tmpText); | ||
} | ||
} | ||
}); | ||
} | ||
else { | ||
u.each(rawValue, function (id) { | ||
var tmpText = me.regionDataIndex[id] && me.regionDataIndex[id].text; | ||
if (tmpText) { | ||
regionTextArr.push(tmpText); | ||
} | ||
}); | ||
} | ||
return regionTextArr.join(','); | ||
}; | ||
/* | ||
* 主要用于adrc地域获取要发送到后端的值 | ||
* | ||
* @return {array} 不论省是不是全选都得带上的一个地区id数组 | ||
*/ | ||
Region.prototype.getRawValueWithProv = function () { | ||
var me = this; | ||
var rawValue = this.getRawValue(); | ||
var returnRawValue = []; | ||
u.each(rawValue, function (id) { | ||
var node = me.regionDataIndex[id]; | ||
// 检查叶子节点 | ||
if (node && !node.children) { | ||
// 如果不是最深叶节点,那就是我们要的特殊的省 | ||
if (node.level !== 4) { | ||
returnRawValue.push(id); | ||
} | ||
else { | ||
// 深度为4就是最深的叶节点,是个市,把它的省也搞进来 | ||
returnRawValue.push(id); | ||
returnRawValue.push(node.parent.id); | ||
} | ||
} | ||
}); | ||
return u.uniq(returnRawValue); | ||
}; | ||
/* | ||
* 主要用于adrc设置控件值 | ||
* | ||
* @param rawValue {array} 地区的id数组 | ||
* @return {string} 返回这些地区的展示文本,不包含省份以上的文本 | ||
*/ | ||
Region.prototype.setRawValueWithoutProv = function (rawValue) { | ||
if (typeof rawValue === 'string' && rawValue.length) { | ||
rawValue = rawValue.split(','); | ||
} | ||
var me = this; | ||
var regionTextArr = []; | ||
var rawValueToBeSet = []; | ||
u.each(rawValue, function (id) { | ||
var node = me.regionDataIndex[id]; | ||
if (node && !node.children) { | ||
regionTextArr.push(node.text); | ||
rawValueToBeSet.push(node.id); | ||
} | ||
}); | ||
this.setRawValue(rawValueToBeSet); | ||
return regionTextArr.join(','); | ||
}; | ||
} | ||
/** | ||
* esui升级前Crumb的过渡扩展,增加global-redirect的功能 | ||
* | ||
* @ignore | ||
*/ | ||
function addCrumbGlobalRedirect() { | ||
var Crumb = require('esui/Crumb'); | ||
/** | ||
* 链接节点的内容HTML模板 | ||
* | ||
* 模板中可以使用以下占位符: | ||
* | ||
* - `{string} text`:文本内容,经过HTML转义 | ||
* - `{string} href`:链接地址,经过HTML转义 | ||
* - `{string} scope`:当Crumb在一个子action中时是否global跳转,经过HTML转义 | ||
* 值为`global`时全局跳转,其他值或空在子action中跳转 | ||
* | ||
* @type {string} | ||
* @override | ||
*/ | ||
Crumb.prototype.linkNodeTemplate = | ||
'<a class="${classes}" href="${href}" data-redirect="${scope}">${text}</a>'; | ||
/** | ||
* 获取节点的HTML内容 | ||
* | ||
* @param {meta.CrumbItem} node 节点数据项 | ||
* @param {number} index 节点索引序号 | ||
* @return {string} | ||
* | ||
* @override | ||
*/ | ||
Crumb.prototype.getNodeHTML = function (node, index) { | ||
var classes = this.helper.getPartClasses('node'); | ||
if (index === 0) { | ||
classes.push.apply( | ||
classes, | ||
this.helper.getPartClasses('node-first') | ||
); | ||
} | ||
if (index === this.path.length - 1) { | ||
classes.push.apply( | ||
classes, | ||
this.helper.getPartClasses('node-last') | ||
); | ||
} | ||
var template = node.href | ||
? this.linkNodeTemplate | ||
: this.textNodeTemplate; | ||
var data = { | ||
href: u.escape(node.href), | ||
scope: u.escape(node.scope), | ||
text: u.escape(node.text), | ||
classes: classes.join(' ') | ||
}; | ||
return lib.format(template, data); | ||
}; | ||
} | ||
function addTreeNodeTitle() { | ||
var Tree = require('esui/Tree'); | ||
Tree.prototype.itemTemplate = '<span title="${text}">${text}</span>'; | ||
} | ||
function fixSidebarHide() { | ||
var Sidebar = require('esui/Sidebar'); | ||
/** | ||
* 隐藏控件 | ||
*/ | ||
Sidebar.prototype.hide = function () { | ||
Control.prototype.hide.call(this); | ||
var mat = lib.g(this.helper.getId('mat')); | ||
if (mat) { | ||
mat.style.display = 'none'; | ||
} | ||
// 隐藏主区域 | ||
this.main.style.display = 'none'; | ||
// minibar | ||
var miniBar = lib.g(this.helper.getId('minibar')); | ||
if (miniBar) { | ||
miniBar.style.display = 'none'; | ||
} | ||
}; | ||
} | ||
function activate() { | ||
initializeValidationRules(); | ||
addControlLinkMode(); | ||
initializeGlobalExtensions(); | ||
addRegionExtension(); | ||
addCrumbGlobalRedirect(); | ||
addTreeNodeTitle(); | ||
fixSidebarHide(); | ||
} | ||
@@ -184,0 +378,0 @@ |
@@ -6,5 +6,10 @@ /** | ||
define(function(require) { | ||
define(function (require) { | ||
var u = require('underscore'); | ||
/** | ||
* underscore扩展模块 | ||
* | ||
* @singleton | ||
*/ | ||
var util = {}; | ||
@@ -30,9 +35,9 @@ | ||
function (value, key) { | ||
var isDefaultNull = | ||
var isDefaultNull = | ||
value == null || value === ''; | ||
var isInDefaults = | ||
var isInDefaults = | ||
defaults.hasOwnProperty(key) && defaults[key] === value; | ||
if (!isDefaultNull && !isInDefaults) { | ||
if (deep && typeof value === 'object') { | ||
purifiedObject[key] = | ||
purifiedObject[key] = | ||
purify(value, defaults[key], deep); | ||
@@ -49,7 +54,27 @@ } | ||
util.filterObject = function(obj, predicate, context) { | ||
/** | ||
* `filterObject`用来判断是否过滤的方法 | ||
* @callback underscore~filterCallback | ||
* @param {Object} obj 需要过滤的对象 | ||
* @param {string} key 对应的键 | ||
* @return {boolean} 是否过滤,如为falsy值则过滤 | ||
*/ | ||
/** | ||
* 根据指定条件过滤对象中的键值对 | ||
* | ||
* @param {Object} obj 输入的对象 | ||
* @param {filterCallback} predicate 判断是否要保留某键值对,返回falsy value则过滤 | ||
* @param {*} [context] 判断函数的`this` | ||
* @return {Object} 过滤的结果 | ||
*/ | ||
util.filterObject = function (obj, predicate, context) { | ||
var result = {}; | ||
if (obj == null) { return results; } | ||
u.each(obj, function(value, key) { | ||
if (predicate.call(context, value, key, obj)) { result[key] = value; } | ||
if (obj == null) { | ||
return result; | ||
} | ||
u.each(obj, function (value, key) { | ||
if (predicate.call(context, value, key, obj)) { | ||
result[key] = value; | ||
} | ||
}); | ||
@@ -59,7 +84,24 @@ return result; | ||
util.mapKey = function(obj, map) { | ||
/** | ||
* `mapObject`用来处理映射逻辑的方法 | ||
* @callback underscore~mapObjectCallback | ||
* @param {*} original 处理前值 | ||
* @return {*} 处理后结果 | ||
*/ | ||
/** | ||
* 根据指定的映射关系修改对象的键值 | ||
* | ||
* @param {Object} obj 输入的对象 | ||
* @param {mapObjectCallback} iterator 每个键值的映射函数 | ||
* @param {*} [context] 判断函数的`this` | ||
* @return {Object} context 映射函数的this | ||
*/ | ||
util.mapObject = function (obj, iterator, context) { | ||
var result = {}; | ||
if (obj == null) { return results; } | ||
u.each(obj, function(value, key) { | ||
result[map[key]] = value; | ||
if (obj == null) { | ||
return result; | ||
} | ||
u.each(obj, function (value, key) { | ||
result[key] = iterator.call(context, value); | ||
}); | ||
@@ -70,2 +112,27 @@ return result; | ||
/** | ||
* 根据指定的映射关系修改对象的键名 | ||
* | ||
* 如果键名不在给定的映射关系中,保留原名 | ||
* | ||
* @param {Object} obj 输入的对象 | ||
* @param {Object.<string, string>} map 键名的映射关系 | ||
* @return {Object} 转换过的新对象 | ||
*/ | ||
util.mapKey = function (obj, map) { | ||
var result = {}; | ||
if (obj == null) { | ||
return result; | ||
} | ||
u.each(obj, function (value, key) { | ||
if (map[key]) { | ||
result[map[key]] = value; | ||
} | ||
else { | ||
result[key] = value; | ||
} | ||
}); | ||
return result; | ||
}; | ||
/** | ||
* 去除字符串首尾空格 | ||
@@ -76,3 +143,3 @@ * | ||
*/ | ||
util.trim = function(s) { | ||
util.trim = function (s) { | ||
return s.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); | ||
@@ -89,3 +156,3 @@ }; | ||
*/ | ||
util.pascalize = function(s) { | ||
util.pascalize = function (s) { | ||
s = s + ''; | ||
@@ -97,3 +164,3 @@ if (/^[A-Z\-_]+$/.test(s)) { | ||
/[\s-_]+(.)/g, | ||
function(w, c) { | ||
function (w, c) { | ||
return c.toUpperCase(); | ||
@@ -114,3 +181,3 @@ } | ||
*/ | ||
util.camelize = function(s) { | ||
util.camelize = function (s) { | ||
s = util.pascalize(s); | ||
@@ -133,13 +200,21 @@ return s.charAt(0).toLowerCase() + s.slice(1); | ||
*/ | ||
util.dasherize = function(s) { | ||
util.dasherize = function (s) { | ||
s = util.pascalize(s); | ||
// 这里把ABCD这种连续的大写,转成AbcD这种形式。 | ||
// 这里把xABCDx这种连续的大写,转成xAbcDx这种形式。 | ||
// 如果`encodeURIComponent`,会变成`encodeUriComponent`, | ||
// 然后加横线后就是`encode-uri-component`得到正确的结果 | ||
var keepLast = false; | ||
if (/[A-Z]{2,}$/.test(s)) { | ||
// 如果连续的大写出现在最后,则不单独处理最后的字母 | ||
keepLast = true; | ||
} | ||
s = s.replace( | ||
/[A-Z]{2,}/g, | ||
function(match) { | ||
function (match) { | ||
return match.charAt(0) | ||
+ match.slice(1, -1).toLowerCase() | ||
+ match.charAt(match.length - 1); | ||
+ (keepLast | ||
? match.slice(1).toLowerCase() | ||
: (match.slice(1, -1).toLowerCase() | ||
+ match.charAt(match.length - 1)) | ||
); | ||
} | ||
@@ -149,8 +224,13 @@ ); | ||
s = s.replace( | ||
/[A-Z]/g, | ||
function(match) { return '-' + match.toLowerCase(); } | ||
/[A-Z]/g, | ||
function (match) { | ||
return '-' + match.toLowerCase(); | ||
} | ||
); | ||
if (s.charAt(0) === '-') { | ||
s = s.substring(1); | ||
s = s.slice(1); | ||
} | ||
if (s.charAt(s.length - 1) === '-') { | ||
s = s.slice(0, -1); | ||
} | ||
return s; | ||
@@ -167,4 +247,5 @@ }; | ||
*/ | ||
util.constanize = function(s) { | ||
s = util.pascalize(s); | ||
util.constanize = function (s) { | ||
s = util.dasherize(s); | ||
s = s.replace(/-/g, '_'); | ||
return s.toUpperCase(); | ||
@@ -181,3 +262,3 @@ }; | ||
*/ | ||
util.pluralize = function(s) { | ||
util.pluralize = function (s) { | ||
return s.replace(/y$/, 'ie') + 's'; | ||
@@ -241,5 +322,5 @@ }; | ||
*/ | ||
util.pad = function(s, padding, length) { | ||
util.pad = function (s, padding, length) { | ||
s = s + ''; | ||
var padLength = s.length - length; | ||
var padLength = length - s.length; | ||
if (padLength > 0) { | ||
@@ -262,5 +343,5 @@ var left = new Array(padLength + 1).join(padding); | ||
*/ | ||
util.padRight = function() { | ||
util.padRight = function (s, padding, length) { | ||
s = s + ''; | ||
var padLength = s.length - length; | ||
var padLength = length - s.length; | ||
if (padLength > 0) { | ||
@@ -281,3 +362,3 @@ var right = new Array(padLength + 1).join(padding); | ||
*/ | ||
util.deepClone = function(obj) { | ||
util.deepClone = function (obj) { | ||
// 非对象以及函数就直接返回 | ||
@@ -295,3 +376,3 @@ if (!u.isObject(obj) || u.isFunction(obj)) { | ||
obj, | ||
function(value, key) { | ||
function (value, key) { | ||
clone[key] = util.deepClone(value); | ||
@@ -303,2 +384,14 @@ } | ||
/** | ||
* 获取指定参数的类型 | ||
* | ||
* 取自`Object.prototype.toString`的结果,比isXX方法更为准确 | ||
* | ||
* @param {Mixed} value 需要判断的参数 | ||
* @return {string} 参数类型(Object, Array, Function, ...) | ||
*/ | ||
util.typeOf = function (value) { | ||
return Object.prototype.toString.call(value).slice(8, -1); | ||
}; | ||
function activate() { | ||
@@ -305,0 +398,0 @@ u.mixin(util); |
@@ -11,14 +11,124 @@ /** | ||
var u = require('underscore'); | ||
var loc = require('../location'); | ||
var io = {}; | ||
/** | ||
* 常规请求流程中的hook如下: | ||
* | ||
* io.request(url, data, options) | ||
* │ | ||
* ├───── io.hooks.beforeRequest(data) ───┐ | ||
* │ │ | ||
* │<────────────── data ─────────────────┘ | ||
* │ | ||
* ├───── io.hooks.afterResponse(data) ───┐ | ||
* │ │ | ||
* │<────────────── data ─────────────────┘ | ||
* │ | ||
* └─────────────────┐ | ||
* ┌──── success ────♦──── failure ────┐ | ||
* │ │ | ||
* ├─ io.hooks.afterSuccess(data) ─┐ ├─ io.hooks.afterFailure(message) ─┐ | ||
* │ │ │ │ | ||
* │<──────────── data ────────────┘ │<───────────── message ───────────┘ | ||
* │ │ | ||
* ├───────────────────────────────────┘ | ||
* ● | ||
*/ | ||
io.hooks = {}; | ||
var DEFAULT_SERVER_ERROR = { | ||
'success': 'false', | ||
'message': { | ||
'global': '服务器错误' | ||
/** | ||
* 后端返回的结果代码对应的类型 | ||
* | ||
* @enum {string} | ||
*/ | ||
var CodeType = { | ||
0: 'SUCCESS', | ||
1: 'GLOBAL', | ||
2: 'FIELD', | ||
3: 'REDIRECT', | ||
4: 'NO_SESSION' | ||
}; | ||
/** | ||
* 最小的自定义错误代码 | ||
* 小于此代码的均保留为预定义类型,大于等于此代码作为自定义处理 | ||
* | ||
* @type {number} | ||
*/ | ||
var MINIMAL_CUSTOM_FAIL_CODE = 100; | ||
var SERVER_ERROR = getGlobalError('服务器错误'); | ||
var PARSE_ERROR = getGlobalError('数据解析失败'); | ||
var SCHEMA_ERROR = getGlobalError('数据格式错误'); | ||
var UNKNOWN_ERROR = getGlobalError('未知错误'); | ||
/** | ||
* 生成全局错误对象 | ||
* | ||
* @param {string} message 错误提示信息 | ||
* @return {Object} 全局错误对象 | ||
*/ | ||
function getGlobalError(message) { | ||
return { | ||
success: false, | ||
message: { | ||
global: message | ||
} | ||
}; | ||
} | ||
/** | ||
* 适配新NMP接口返回的结果 | ||
* | ||
* @param {Object} data 后端返回的数据对象 | ||
* @return {Object} 转换过后符合前端逻辑的对象 | ||
*/ | ||
io.prepareResponse = function (data) { | ||
if (typeof data.code !== 'undefined') { // 有code时认为是新版接口 | ||
var status = CodeType[data.code]; | ||
if (!status) { | ||
if (data.code < MINIMAL_CUSTOM_FAIL_CODE) { // 非预定义类型,未知错误 | ||
return UNKNOWN_ERROR; | ||
} | ||
else { // 自定义类型错误 | ||
var message = data.message || {}; | ||
message.code = data.code; | ||
return { | ||
success: false, | ||
message: message | ||
}; | ||
} | ||
} | ||
else { | ||
if (status === 'SUCCESS') { | ||
var result = { | ||
success: true, | ||
message: data.message, | ||
result: data.result || data.page | ||
}; | ||
return u.purify(result); | ||
} | ||
else { | ||
return { | ||
success: false, | ||
message: data.message | ||
}; | ||
} | ||
} | ||
} | ||
else if (typeof data.success !== 'undefined') { | ||
return data; | ||
} | ||
else { | ||
return SCHEMA_ERROR; | ||
} | ||
}; | ||
/** | ||
* 跳转到主页 | ||
*/ | ||
function gotoIndex() { | ||
@@ -28,10 +138,22 @@ var url = '/index.html'; | ||
if (typeof io.hooks.filterIndexUrl === 'function') { | ||
url = io.hooks.filterIndexUrl(url); | ||
url = io.hooks.filterIndexUrl(url) || url; | ||
} | ||
document.location.href = url; | ||
loc.assign(url); | ||
} | ||
function requestSuccessHandler(data) { | ||
if (data.success !== 'true') { | ||
/** | ||
* 处理服务端响应成功的情况 | ||
* | ||
* @param {Object} rawData 转换后的后端响应对象 | ||
* @return {meta.Promise} 处理后的Promise | ||
*/ | ||
function requestSuccessHandler(rawData) { | ||
var data = io.prepareResponse(rawData); | ||
if (typeof io.hooks.afterResponse === 'function') { | ||
data = io.hooks.afterResponse(data) || data; | ||
} | ||
if ((data.success + '') !== 'true') { | ||
var message = data.message; | ||
@@ -43,7 +165,7 @@ var title; | ||
if (message.global) { | ||
if (typeof message.global !== 'undefined') { | ||
title = '系统提示'; | ||
content = message.global; | ||
} | ||
else if (message.noSession) { | ||
else if (typeof message.noSession !== 'undefined') { | ||
title = '系统超时'; | ||
@@ -58,39 +180,42 @@ content = message.noSession; | ||
onok = function() { | ||
window.location.reload(true); | ||
loc.reload(true); | ||
}; | ||
} | ||
else { | ||
window.location.href = message.redirect; | ||
loc.assign(message.redirect); | ||
return; | ||
} | ||
} | ||
else if (!message.field) { | ||
else if (typeof message.field !== 'undefined' || typeof message.code !== 'undefined') { | ||
// 字段错误不需要弹窗提示,直接在表单中处理 | ||
// 自定义错误也在后面的过程中自行处理 | ||
needAlert = false; | ||
} | ||
else { // last resort | ||
title = '系统提示'; | ||
content = '请求失败(未知错误)'; | ||
content = '未知错误'; | ||
} | ||
// field error | ||
else { | ||
needAlert = false; | ||
} | ||
if (needAlert) { | ||
Dialog.alert({ | ||
Dialog.alert(u.purify({ | ||
title: title, | ||
content: content, | ||
onok: onok | ||
}); | ||
})); | ||
} | ||
if (typeof io.hooks.afterFailure === 'function') { | ||
io.hooks.afterFailure(message); | ||
message = io.hooks.afterFailure(message) || message; | ||
} | ||
requestCompleteHandler(message); | ||
message = requestCompleteHandler(message) || message; | ||
return Deferred.rejected(message); | ||
} | ||
// success | ||
else { | ||
else { // 成功状态 | ||
if (typeof io.hooks.afterSuccess === 'function') { | ||
io.hooks.afterSuccess(data); | ||
data = io.hooks.afterSuccess(data) || data; | ||
} | ||
var result = data.page || data.result; | ||
requestCompleteHandler(result); | ||
result = requestCompleteHandler(result) || result; | ||
return Deferred.resolved(result); | ||
@@ -100,10 +225,33 @@ } | ||
function requestFailureHandler(data) { | ||
requestCompleteHandler(data); | ||
return requestSuccessHandler(DEFAULT_SERVER_ERROR); | ||
/** | ||
* 处理服务端响应失败的情况 | ||
* 转换为成功响应,返回错误提示处理 | ||
* | ||
* @param {meta.Promise} fakeXHR 请求的Promise | ||
* @return {meta.Promise} 处理后的Promise | ||
*/ | ||
function requestFailureHandler(fakeXHR) { | ||
var status = fakeXHR.status; | ||
var error; | ||
if (status < 200 || (status >= 300 && status !== 304)) { // 服务器没有正常返回 | ||
error = SERVER_ERROR; | ||
} | ||
else { | ||
error = PARSE_ERROR; | ||
} | ||
return requestSuccessHandler(error); | ||
} | ||
/** | ||
* 处理服务端响应完成的情况 | ||
* 不管成功失败均执行 | ||
* | ||
* @param {Object|meta.Promise} data 成功时为返回的数据对象,失败时为请求Promise | ||
* @return {Mixed} 处理后的输入参数 | ||
*/ | ||
function requestCompleteHandler(data) { | ||
if (typeof io.hooks.afterComplete === 'function') { | ||
io.hooks.afterComplete(data); | ||
data = io.hooks.afterComplete(data) || data; | ||
} | ||
@@ -113,2 +261,10 @@ return data; | ||
/** | ||
* 向服务端发起请求 | ||
* | ||
* @param {string} url 请求URL | ||
* @param {Object} data 请求参数 | ||
* @param {Object} options 请求选项 | ||
* @return {meta.Promise} 请求Promise | ||
*/ | ||
io.request = function(url, data, options) { | ||
@@ -118,3 +274,4 @@ var defaults = { | ||
data: data, | ||
dataType: 'json' | ||
dataType: 'json', | ||
charset: 'utf-8' | ||
}; | ||
@@ -126,4 +283,6 @@ | ||
options.data = u.extend(options.data, data); | ||
if (typeof io.hooks.beforeRequest === 'function') { | ||
io.hooks.beforeRequest(options); | ||
options = io.hooks.beforeRequest(options) || options; | ||
} | ||
@@ -138,12 +297,30 @@ | ||
/** | ||
* 以GET方式向服务端发起请求 | ||
* | ||
* @param {string} url 请求URL | ||
* @param {Object} data 请求参数 | ||
* @param {Object} options 请求选项 | ||
* @return {meta.Promise} 请求Promise | ||
*/ | ||
io.get = function(url, data, options) { | ||
return this.request(url, data, { | ||
u.extend(options, { | ||
method: 'GET' | ||
}); | ||
return this.request(url, data, options); | ||
}; | ||
/** | ||
* 以POST方式向服务端发起请求 | ||
* | ||
* @param {string} url 请求URL | ||
* @param {Object} data 请求参数 | ||
* @param {Object} options 请求选项 | ||
* @return {meta.Promise} 请求Promise | ||
*/ | ||
io.post = function(url, data, options) { | ||
return this.request(url, data, { | ||
u.extend(options, { | ||
method: 'POST' | ||
}); | ||
return this.request(url, data, options); | ||
}; | ||
@@ -150,0 +327,0 @@ |
111
src/main.js
@@ -9,24 +9,25 @@ /** | ||
var config; | ||
var config = {}; | ||
var u = require('underscore'); | ||
var util = require('./util'); | ||
var Deferred = require('er/Deferred'); | ||
require('./extension/hooks').activate(); | ||
require('./extension/underscore').activate(); | ||
/** | ||
* 初始化API请求器 | ||
* | ||
* @ignore | ||
*/ | ||
function initApiConfig() { | ||
// init api requesters | ||
var requesters = u.filterObject(config.api, function (path) { | ||
// 跳过以`/download`结尾的路径 | ||
return !/\/download$/.test(path); | ||
}); | ||
config.api = u.extend( | ||
config.api, | ||
util.genRequesters(requesters) | ||
); | ||
config.api = util.genRequesters(config.api); | ||
} | ||
/** | ||
* 初始化ER 配置 | ||
* | ||
* @ignore | ||
*/ | ||
function initErConfigs() { | ||
var erConfig = require('er/config'); | ||
erConfig.indexURL = config.index; | ||
erConfig.systemName = config.systemName; | ||
} | ||
@@ -37,25 +38,60 @@ | ||
* | ||
* @inner | ||
* @param {Array} [extra] 额外的请求发送器 | ||
* @return {er.Promise} 处理完毕的Promise | ||
* @ignore | ||
*/ | ||
function loadData() { | ||
var Deferred = require('er/Deferred'); | ||
function loadData(extra) { | ||
extra = extra ? u.map(extra, function (api) { | ||
if (typeof api === 'string') { | ||
return util.genRequesters(api); | ||
} | ||
else { | ||
return api; | ||
} | ||
}) : []; | ||
return Deferred.all( | ||
config.api.user(), | ||
config.api.constants() | ||
var requests = [config.api.user, config.api.constants].concat(extra || []); | ||
return Deferred.all.apply( | ||
Deferred, | ||
u.map(requests, function (requester) { | ||
return Deferred.when(requester()); | ||
}) | ||
); | ||
} | ||
/** | ||
* 默认读取用户信息和系统常量后初始化对应模块 | ||
* | ||
* @param {Object} session 用户信息 | ||
* @param {Object} constants 服务器端常量 | ||
* @return {er.Promise} 处理完毕的Promise | ||
*/ | ||
function initData(session, constants) { | ||
// init user | ||
// 初始化用户信息 | ||
var user = require('./system/user'); | ||
user.init(session); | ||
// init constants | ||
// 初始化系统常量 | ||
var consts = require('./system/constants'); | ||
var localConstants = require('common/constants'); | ||
consts.init(u.extend(localConstants, constants)); | ||
// 返回其余请求结果 | ||
var extra = [].slice.call(arguments).slice(2); | ||
return Deferred.all.apply( | ||
Deferred, | ||
u.map(extra, function (result) { | ||
return Deferred.resolved(result); | ||
}) | ||
); | ||
} | ||
function erStart() { | ||
/** | ||
* 启动ER | ||
* | ||
* @ignore | ||
*/ | ||
function init() { | ||
initErConfigs(); | ||
@@ -67,12 +103,37 @@ | ||
function start(riaConfig) { | ||
/** | ||
* RIA启动入口 | ||
* | ||
* @param {Object} riaConfig RIA配置 | ||
* @param {Array} requesters 初始化数据需要的请求发送器 | ||
* @param {Function} callback 初始化请求返回后的回调函数 | ||
* @return {er.Promise} 处理完毕的Promise | ||
* @ignore | ||
*/ | ||
function start(riaConfig, requesters, callback) { | ||
config = riaConfig; | ||
require('./extension/underscore').activate(); | ||
require('./extension/hooks').activate(config.hooks); | ||
require('./extension/ui').activate(); | ||
if (!(riaConfig.ext && riaConfig.ext.track === false)) { | ||
// 默认开启,如果想要禁用的话,可以在调用bat-ria的时候关闭 | ||
// require('bat-ria').start({ | ||
// ext: { | ||
// track: false | ||
// } | ||
// }); | ||
require('./extension/track').activate(); | ||
} | ||
// 对API配置进行一下封装 | ||
initApiConfig(); | ||
return loadData() | ||
// 读取必要信息后初始化系统 | ||
return loadData(requesters) | ||
.then(initData) | ||
.then(erStart); | ||
.then(callback) | ||
.then(init); | ||
} | ||
@@ -79,0 +140,0 @@ |
/** | ||
* UB RIA Base | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* @ignore | ||
* @file Action基类 | ||
* @author otakustay | ||
* @date $DATE$ | ||
* @file 业务`Action`基类 | ||
* @author Justineo(justice360@gmail.com) | ||
*/ | ||
define( | ||
function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var Action = require('er/Action'); | ||
/** | ||
* Action基类,提供实体名称和实体描述的维护 | ||
* | ||
* @extends er.Action | ||
* @constructor | ||
*/ | ||
function BaseAction() { | ||
Action.apply(this, arguments); | ||
} | ||
define(function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var Action = require('er/Action'); | ||
util.inherits(BaseAction, Action); | ||
/** | ||
* `Action`基类 | ||
* | ||
* @extends er.Action | ||
* @constructor | ||
*/ | ||
function BaseAction() { | ||
Action.apply(this, arguments); | ||
} | ||
/** | ||
* 创建数据模型对象 | ||
* | ||
* 此方法会在返回的`Model`中加上`entityDescription`属性 | ||
* | ||
* @param {Object} args 模型的初始化数据 | ||
* @return {BaseModel} | ||
* @protected | ||
* @override | ||
*/ | ||
BaseAction.prototype.createModel = function (args) { | ||
var model = Action.prototype.createModel.apply(this, arguments); | ||
util.inherits(BaseAction, Action); | ||
// Action基类的默认返回值是一个空对象`{}`, | ||
// 但是普通的`Model`对象因为方法和属性全在`prototype`上,也会被判断为空 | ||
var Model = require('er/Model'); | ||
if (!(model instanceof Model) && u.isEmpty(model)) { | ||
var BaseModel = require('./BaseModel'); | ||
model = new BaseModel(args); | ||
} | ||
/** | ||
* @override | ||
*/ | ||
BaseAction.prototype.createModel = function (args) { | ||
var model = Action.prototype.createModel.apply(this, arguments); | ||
return model; | ||
}; | ||
// `Action`基类的默认返回值是一个空对象`{}`, | ||
// 但是普通的`Model`对象因为方法和属性全在`prototype`上,也会被判断为空 | ||
var Model = require('er/Model'); | ||
if (!(model instanceof Model) && u.isEmpty(model)) { | ||
var BaseModel = require('./BaseModel'); | ||
model = new BaseModel(args); | ||
} | ||
/** | ||
* 返回来源URL,无来源URL时可指定一个默认地址 | ||
* | ||
* @param {string | URL} [defaultURL](optional) 无来源URL时的跳转地址 | ||
* @param {boolean} [isForce] 强制跳转至历史记录 | ||
* @protected | ||
* @override | ||
*/ | ||
BaseAction.prototype.back = function (defaultURL, isForce) { | ||
if (typeof arguments[0] === 'boolean') { | ||
isForce = defaultURL; | ||
defaultURL = null; | ||
} | ||
else { | ||
defaultURL = defaultURL || ''; | ||
} | ||
var referrer = this.context && this.context.referrer; | ||
var url = referrer || defaultURL; | ||
if (url) { | ||
this.redirect(url); | ||
} | ||
else if (isForce) { | ||
window.history.back(); | ||
} | ||
}; | ||
return BaseAction; | ||
} | ||
); | ||
return model; | ||
}; | ||
/** | ||
* 返回来源URL,无来源URL时可指定一个默认地址 | ||
* | ||
* @param {string|URL} [defaultURL] 无来源URL时的跳转地址 | ||
* @param {boolean} [isForce] 强制跳转至历史记录 | ||
* @protected | ||
* @override | ||
*/ | ||
BaseAction.prototype.back = function (defaultURL, isForce) { | ||
if (typeof arguments[0] === 'boolean') { | ||
isForce = defaultURL; | ||
defaultURL = null; | ||
} | ||
else { | ||
defaultURL = defaultURL || ''; | ||
} | ||
var referrer = this.context && this.context.referrer; | ||
var url = referrer || defaultURL; | ||
if (url) { | ||
this.redirect(url); | ||
} | ||
else if (isForce) { | ||
require('../location').back(); | ||
} | ||
}; | ||
return BaseAction; | ||
}); |
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* @file Model基类 | ||
* @file 业务`Model`基类 | ||
* @author Justineo(justice360@gmail.com) | ||
* @date $DATE$ | ||
*/ | ||
define( | ||
function (require) { | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var UIModel = require('ef/UIModel'); | ||
define(function (require) { | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var UIModel = require('ef/UIModel'); | ||
/** | ||
* 业务`Model`基类 | ||
* | ||
* @param {Object=} context 初始化时的数据 | ||
* | ||
* @constructor | ||
* @extends ef/UIModel | ||
*/ | ||
function BaseModel(context) { | ||
UIModel.call(this, context); | ||
/** | ||
* 业务`Model`基类 | ||
* | ||
* @param {Object} [context] 初始化时的数据 | ||
* | ||
* @constructor | ||
* @extends ef.UIModel | ||
*/ | ||
function BaseModel(context) { | ||
UIModel.call(this, context); | ||
} | ||
util.inherits(BaseModel, UIModel); | ||
/** | ||
* 合并默认数据源 | ||
*/ | ||
BaseModel.prototype.mergeDefaultDatasource = function() { | ||
if (!this.datasource) { | ||
this.datasource = this.defaultDatasource; | ||
return; | ||
} | ||
util.inherits(BaseModel, UIModel); | ||
/** | ||
* 合并默认数据源 | ||
*/ | ||
BaseModel.prototype.mergeDefaultDatasource = function() { | ||
if (!this.datasource) { | ||
this.datasource = this.defaultDatasource; | ||
return; | ||
} | ||
// 管它有没有必要,先深复制一份,这样下面就不会为各种情况纠结, | ||
// `datasource`大不到哪里去,深复制不影响性能 | ||
var datasource = u.deepClone(this.datasource) || {}; | ||
var defaultDatasource = u.deepClone(this.defaultDatasource); | ||
// 管它有没有必要,先深复制一份,这样下面就不会为各种情况纠结, | ||
// `datasource`大不到哪里去,深复制不影响性能 | ||
var datasource = u.deepClone(this.datasource) || {}; | ||
var defaultDatasource = u.deepClone(this.defaultDatasource); | ||
// 默认数据源可能是对象或者数组,当前的数据源也可能是对象或数组,按以下规则: | ||
// | ||
// - 默认数组 + 当前数组:将当前数组连接到默认的最后 | ||
// - 默认数组 + 当前对象:将当前对象加到默认的最后 | ||
// - 默认对象 + 当前数组:将默认放在数组第1个 | ||
// - 默认对象 + 当前对象:做对象的合并 | ||
if (u.isArray(defaultDatasource)) { | ||
// 默认数组 + 当前数组 | ||
if (u.isArray(datasource)) { | ||
datasource = defaultDatasource.concat(datasource); | ||
} | ||
// 默认数组 + 当前对象 | ||
else { | ||
datasource = defaultDatasource.push(datasource); | ||
} | ||
// 默认数据源可能是对象或者数组,当前的数据源也可能是对象或数组,按以下规则: | ||
// | ||
// - 默认数组 + 当前数组:将当前数组连接到默认的最后 | ||
// - 默认数组 + 当前对象:将当前对象加到默认的最后 | ||
// - 默认对象 + 当前数组:将默认放在数组第1个 | ||
// - 默认对象 + 当前对象:做对象的合并 | ||
if (u.isArray(defaultDatasource)) { | ||
// 默认数组 + 当前数组 | ||
if (u.isArray(datasource)) { | ||
datasource = defaultDatasource.concat(datasource); | ||
} | ||
// 默认数组 + 当前对象 | ||
else { | ||
// 默认对象 + 当前数组 | ||
if (u.isArray(datasource)) { | ||
if (!u.contains(datasource, defaultDatasource)) { | ||
// 其它的数据项有可能会依赖这个属性,因此需要放在最前面 | ||
datasource.unshift(defaultDatasource); | ||
} | ||
datasource = defaultDatasource.push(datasource); | ||
} | ||
} | ||
else { | ||
// 默认对象 + 当前数组 | ||
if (u.isArray(datasource)) { | ||
if (!u.contains(datasource, defaultDatasource)) { | ||
// 其它的数据项有可能会依赖这个属性,因此需要放在最前面 | ||
datasource.unshift(defaultDatasource); | ||
} | ||
// 默认对象 + 当前对象 | ||
else { | ||
u.defaults(datasource, defaultDatasource); | ||
} | ||
} | ||
// 默认对象 + 当前对象 | ||
else { | ||
u.defaults(datasource, defaultDatasource); | ||
} | ||
} | ||
this.datasource = datasource; | ||
}; | ||
this.datasource = datasource; | ||
}; | ||
/** | ||
* 加载数据 | ||
* | ||
* @return {er/Promise} | ||
*/ | ||
BaseModel.prototype.load = function() { | ||
// TODO: 移到`getDatasource`方法中 | ||
this.mergeDefaultDatasource(); | ||
/** | ||
* 加载数据 | ||
* | ||
* @return {er/Promise} | ||
*/ | ||
BaseModel.prototype.load = function() { | ||
// TODO: 移到`getDatasource`方法中 | ||
this.mergeDefaultDatasource(); | ||
return UIModel.prototype.load.apply(this, arguments); | ||
}; | ||
return UIModel.prototype.load.apply(this, arguments); | ||
}; | ||
return BaseModel; | ||
} | ||
); | ||
return BaseModel; | ||
}); |
/** | ||
* UB RIA Base | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* @ignore | ||
* @file 视图基类 | ||
* @author otakustay | ||
* @date $DATE$ | ||
* @file 业务`View`基类 | ||
* @author Justineo(justice360@gmail.com) | ||
*/ | ||
define( | ||
function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var UIView = require('ef/UIView'); | ||
/** | ||
* 视图基类 | ||
* | ||
* @extends ef.UIView | ||
* @constructor | ||
*/ | ||
function BaseView() { | ||
UIView.apply(this, arguments); | ||
} | ||
define(function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var UIView = require('ef/UIView'); | ||
var Deferred = require('er/Deferred'); | ||
var Dialog = require('esui/Dialog'); | ||
util.inherits(BaseView, UIView); | ||
/** | ||
* 业务`View`基类 | ||
* | ||
* @extends ef.UIView | ||
* @constructor | ||
*/ | ||
function BaseView() { | ||
UIView.apply(this, arguments); | ||
} | ||
/** | ||
* 获取对应模板名称 | ||
* | ||
* 当一个视图被作为子Action使用时,需要在其视图模板名后加上`"Main"`以进行区分, | ||
* 根据此设计,可以将视图切分为“完整页面”和“仅用于嵌套”两部分,根据约定命名 | ||
* | ||
* @return {string} | ||
* @override | ||
*/ | ||
BaseView.prototype.getTemplateName = function () { | ||
var templateName = | ||
UIView.prototype.getTemplateName.apply(this, arguments); | ||
util.inherits(BaseView, UIView); | ||
// 作为子Action嵌入页面时,模板使用`xxxMain`这个target | ||
if (this.model && this.model.get('isChildAction')) { | ||
templateName += '_child'; | ||
} | ||
/** | ||
* 获取对应模板名称 | ||
* | ||
* 当一个视图被作为子Action使用时,需要在其视图模板名后加上`_child`以进行区分, | ||
* 根据此设计,可以将视图切分为“完整页面”和“仅用于嵌套”两部分,根据约定命名 | ||
* | ||
* @return {string} | ||
* @override | ||
*/ | ||
BaseView.prototype.getTemplateName = function () { | ||
var templateName = | ||
UIView.prototype.getTemplateName.apply(this, arguments); | ||
return templateName; | ||
}; | ||
// 作为子Action嵌入页面时,模板使用`xxxMain`这个target | ||
if (this.model && this.model.get('isChildAction')) { | ||
templateName += '_child'; | ||
} | ||
var globalToast; | ||
return templateName; | ||
}; | ||
/** | ||
* 显示toast提示信息,这个方法会控制一个单例,以免信息叠在一起 | ||
* | ||
* @parma {string} content 显示的内容 | ||
* @param {Object} [options] 配置 | ||
* @return {esui.Toast} | ||
*/ | ||
BaseView.prototype.showToast = function (content, options) { | ||
if (!content) { | ||
return; | ||
} | ||
var globalToast; | ||
if (!globalToast) { | ||
// 此处直接new控件出来, | ||
// 因为这个控件不能属于任何一个业务模块的ViewContext, | ||
// 不然会随着跳转被销毁,造成下次用不了 | ||
var Toast = require('../ui/Toast'); | ||
var toastOptions = { disposeOnHide: false, autoShow: false }; | ||
globalToast = new Toast(toastOptions); | ||
globalToast.on( | ||
'hide', | ||
u.bind(globalToast.detach, globalToast) | ||
); | ||
globalToast.render(); | ||
} | ||
/** | ||
* 显示toast提示信息,这个方法会控制一个单例,以免信息叠在一起 | ||
* | ||
* @param {string} content 显示的内容 | ||
* @param {Object} [options] 配置 | ||
* @return {esui.Toast} | ||
*/ | ||
BaseView.prototype.showToast = function (content, options) { | ||
if (!content) { | ||
return; | ||
} | ||
// 如果这个信息无比素正好显示着内容,又有新内容要显示, | ||
// 那么新内容也应该有个动画效果,以吸引用户眼球, | ||
// 所以要先`detach`一次,让`animation`生效 | ||
globalToast.detach(); | ||
var properties = { | ||
content: content, | ||
status: undefined | ||
if (!globalToast) { | ||
// 此处直接new控件出来, | ||
// 因为这个控件不能属于任何一个业务模块的ViewContext, | ||
// 不然会随着跳转被销毁,造成下次用不了 | ||
var Toast = require('esui/Toast'); | ||
var toastOptions = { | ||
disposeOnHide: false, | ||
autoShow: false | ||
}; | ||
properties = u.extend(properties, options); | ||
globalToast.setProperties(properties); | ||
globalToast.show(); | ||
return globalToast; | ||
globalToast = new Toast(toastOptions); | ||
globalToast.render(); | ||
} | ||
var properties = { | ||
content: content | ||
}; | ||
properties = u.extend(properties, options); | ||
globalToast.setProperties(properties); | ||
globalToast.show(); | ||
return globalToast; | ||
}; | ||
BaseView.prototype.popDialog = function (options) { | ||
//创建main | ||
var main = document.createElement('div'); | ||
document.body.appendChild(main); | ||
/** | ||
* 显示Dialog | ||
* | ||
* @param {Object} options 参数 | ||
* @return {esui/Dialog} | ||
* @protected | ||
*/ | ||
BaseView.prototype.popDialog = function (options) { | ||
// 创建main | ||
var main = document.createElement('div'); | ||
document.body.appendChild(main); | ||
var defaults = { | ||
width: 600, | ||
needFoot: true, | ||
draggable: true, | ||
closeOnHide: false, | ||
autoClose: true, | ||
main: main, | ||
viewContext: this.viewContext | ||
}; | ||
options = u.defaults({}, options, defaults); | ||
var defaults = { | ||
width: 600, | ||
needFoot: true, | ||
draggable: true, | ||
closeOnHide: false, | ||
autoClose: true, | ||
main: main, | ||
viewContext: this.viewContext | ||
}; | ||
options = u.defaults({}, options, defaults); | ||
var ui = require('esui'); | ||
var dialog = ui.create('Dialog', options); | ||
var ui = require('esui'); | ||
var dialog = ui.create('Dialog', options); | ||
dialog.render(); | ||
//使用默认foot时,改变显示文字 | ||
if (options.needFoot) { | ||
var okBtn = dialog.getFoot().getChild('btnOk'); | ||
var cancelBtn = dialog.getFoot().getChild('btnCancel'); | ||
var okText = u.escape(options.okText || ''); | ||
var cancelText = u.escape(options.cancelText || ''); | ||
okBtn.setContent(okText || Dialog.OK_TEXT); | ||
cancelBtn.setContent(cancelText || Dialog.CANCEL_TEXT); | ||
} | ||
// 使用默认foot时,改变显示文字 | ||
if (options.needFoot) { | ||
var okBtn = dialog.getFoot().getChild('btnOk'); | ||
var cancelBtn = dialog.getFoot().getChild('btnCancel'); | ||
var okText = u.escape(options.okText || ''); | ||
var cancelText = u.escape(options.cancelText || ''); | ||
okBtn.setContent(okText || Dialog.OK_TEXT); | ||
cancelBtn.setContent(cancelText || Dialog.CANCEL_TEXT); | ||
} | ||
dialog.show(); | ||
return dialog; | ||
}; | ||
/** | ||
* 等待一个`Dialog`触发`ok`或`cancel`事件,触发后一定会自动关闭 | ||
* | ||
* @param {esui.Dialog} [dialog] 指定的对话框控件,未指定则通过`popDialog`创建新对话框 | ||
* @param {Object} options 参数 | ||
* @return {er.Promise} 一个`Promise`对象, | ||
* 默认为点击确定按钮时进入`resolved`状态, | ||
* 点击取消按钮则进入`rejected`状态 | ||
* | ||
* 有两种重载: | ||
* 1. waitDialog(options) | ||
* 2. waitDialog(dialog, options) | ||
*/ | ||
BaseView.prototype.waitDialog = function (dialog, options) { | ||
if (!(dialog instanceof Dialog)) { | ||
options = dialog; | ||
dialog = this.popDialog.call(this, options); | ||
} | ||
else if (!dialog.isShow) { | ||
dialog.show(); | ||
return dialog; | ||
}; | ||
} | ||
BaseView.prototype.waitDialog = function (dialog, options) { | ||
if (!dialog instanceof require('esui/Dialog')) { | ||
options = dialog; | ||
dialog = this.popDialog.apply(this, options); | ||
} | ||
else if (!dialog.isShow) { | ||
dialog.show(); | ||
} | ||
function btnClickHandler(dialog, type, args) { | ||
// 如果在参数里设置了处理函数,会在fire时执行 | ||
dialog.fire(type); | ||
dialog.hide(); | ||
} | ||
function btnClickHandler(dialog, type, args) { | ||
// 如果在参数里设置了处理函数,会在fire时执行 | ||
dialog.fire(type, args); | ||
dialog.hide(); | ||
} | ||
// 使用默认foot时,改变显示文字 | ||
if (options.needFoot || dialog.getFoot()) { | ||
var okBtn = dialog.getFoot().getChild('btnOk'); | ||
var cancelBtn = dialog.getFoot().getChild('btnCancel'); | ||
var okText = u.escape(options.okText || ''); | ||
var cancelText = u.escape(options.cancelText || ''); | ||
okBtn.setContent(okText || Dialog.OK_TEXT); | ||
cancelBtn.setContent(cancelText || Dialog.CANCEL_TEXT); | ||
//使用默认foot时,改变显示文字 | ||
if (options.needFoot || dialog.getFoot()) { | ||
var okBtn = dialog.getFoot().getChild('btnOk'); | ||
var cancelBtn = dialog.getFoot().getChild('btnCancel'); | ||
var okText = u.escape(options.okText || ''); | ||
var cancelText = u.escape(options.cancelText || ''); | ||
okBtn.setContent(okText || Dialog.OK_TEXT); | ||
cancelBtn.setContent(cancelText || Dialog.CANCEL_TEXT); | ||
okBtn.un('click'); | ||
cancelBtn.un('click'); | ||
okBtn.on('click', u.partial(btnClickHandler, dialog, 'ok')); | ||
cancelBtn.on('click', u.partial(btnClickHandler, dialog, 'cancel')); | ||
} | ||
okBtn.on('click', lib.curry(btnClickHandler, dialog, 'ok')); | ||
cancelBtn.on('click', lib.curry(btnClickHandler, dialog, 'cancel')); | ||
var deferred = new Deferred(); | ||
dialog.un('ok'); | ||
dialog.un('cancel'); | ||
dialog.on('ok', deferred.resolver.resolve); | ||
dialog.on('cancel', deferred.resolver.reject); | ||
return deferred.promise; | ||
}; | ||
/** | ||
* 显示一个`Dialog`,并指定触发`ok`与`cancel`事件(默认状态下为点击确定、取消按钮后触发) | ||
* 后的处理函数,可以手动指定阻止自动关闭 | ||
* | ||
* @param {esui.Dialog} [dialog] 指定的对话框控件,未指定则通过`popDialog`创建新对话框 | ||
* @param {Object} options 参数 | ||
* @param {function} [options.onOk] `ok`事件处理函数,`this`指向对应的`Dialog`对象 | ||
* @param {function} [options.onCancel] `cancel`事件处理函数,`this`指向对应的`Dialog`对象 | ||
* @return {esui.Dialog} 显示的`Dialog`对象 | ||
* | ||
* `onOk`或`onCancel`的返回值如果为`false`,则不执行默认的关闭动作; | ||
* 如果返回值是一个`Event`对象,则在调用过`preventDefault()`后不执行默认动作; | ||
* 如果返回一个`Promise`对象,则在`resolve`时执行默认关闭动作,在`reject`时不执行 | ||
* | ||
* 有两种重载: | ||
* 1. showDialog(options) | ||
* 2. showDialog(dialog, options) | ||
*/ | ||
BaseView.prototype.showDialog = function (dialog, options) { | ||
if (!(dialog instanceof Dialog)) { | ||
options = dialog; | ||
dialog = this.popDialog.call(this, options); | ||
} | ||
else if (!dialog.isShow) { | ||
dialog.show(); | ||
} | ||
function btnClickHandler(dialog, type, args) { | ||
// 如果在参数里设置了处理函数,会在fire时执行 | ||
dialog.fire(type); | ||
} | ||
options = options || {}; | ||
// 使用默认foot时,改变显示文字 | ||
if (options.needFoot || dialog.getFoot()) { | ||
var okBtn = dialog.getFoot().getChild('btnOk'); | ||
var cancelBtn = dialog.getFoot().getChild('btnCancel'); | ||
var okText = u.escape(options.okText || ''); | ||
var cancelText = u.escape(options.cancelText || ''); | ||
okBtn.setContent(okText || Dialog.OK_TEXT); | ||
cancelBtn.setContent(cancelText || Dialog.CANCEL_TEXT); | ||
okBtn.un('click'); | ||
cancelBtn.un('click'); | ||
okBtn.on('click', u.partial(btnClickHandler, dialog, 'ok')); | ||
cancelBtn.on('click', u.partial(btnClickHandler, dialog, 'cancel')); | ||
} | ||
function checkHide(useDefault) { | ||
// 返回值为false或被阻止默认行为的event,则不隐藏 | ||
if (useDefault === false | ||
|| useDefault instanceof require('mini-event/Event') | ||
&& useDefault.isDefaultPrevented()) { | ||
return; | ||
} | ||
dialog && dialog.hide(); | ||
} | ||
var Deferred = require('er/Deferred'); | ||
var deferred = new Deferred(); | ||
var blank = function () {}; | ||
dialog.on('ok', deferred.resolver.resolve); | ||
dialog.on('cancel', deferred.resolver.reject); | ||
var onOk = u.bind(options.onOk || blank, dialog); | ||
var onCancel = u.bind(options.onCancel || blank, dialog); | ||
return deferred.promise; | ||
}; | ||
dialog.un('ok'); | ||
dialog.un('cancel'); | ||
dialog.on('ok', function () { | ||
Deferred.when(onOk()).then(checkHide, u.partial(checkHide, false)); | ||
}); | ||
dialog.on('cancel', function () { | ||
Deferred.when(onCancel()).then(checkHide, u.partial(checkHide, false)); | ||
}); | ||
/** | ||
* 等待用户确认 | ||
* | ||
* 参数同`ef.UIView.prototype.confirm`,但返回一个`Promise`对象 | ||
* | ||
* @return {er.Promise} 一个`Promise`对象,用户确认则进入`resolved`状态, | ||
* 用户取消则进入`rejected`状态 | ||
*/ | ||
BaseView.prototype.waitConfirm = function () { | ||
var dialog = this.confirm.apply(this, arguments); | ||
var Deferred = require('er/Deferred'); | ||
var deferred = new Deferred(); | ||
return dialog; | ||
}; | ||
dialog.on('ok', deferred.resolver.resolve); | ||
dialog.on('cancel', deferred.resolver.reject); | ||
/** | ||
* 等待用户确认提示 | ||
* | ||
* 参数同`ef.UIView.prototype.alert`,但返回一个`Promise`对象 | ||
* | ||
* @return {er.Promise} 一个`Promise`对象,用户确认则进入`resolved`状态 | ||
*/ | ||
BaseView.prototype.waitAlert = function () { | ||
var dialog = this.alert.apply(this, arguments); | ||
var deferred = new Deferred(); | ||
return deferred.promise; | ||
}; | ||
dialog.on('ok', deferred.resolver.resolve); | ||
/** | ||
* 等待一个`DialogAction`加载完成 | ||
* | ||
* @return {er.Promise} 一个`Promise`对象, | ||
* 对应的Action加载完成时进入`resolved`状态, | ||
* 如Action加载失败则进入`rejected`状态 | ||
*/ | ||
BaseView.prototype.waitActionDialog = function () { | ||
var dialog = this.popActionDialog.apply(this, arguments); | ||
return deferred.promise; | ||
}; | ||
var Deferred = require('er/Deferred'); | ||
var deferred = new Deferred(); | ||
/** | ||
* 等待用户确认 | ||
* | ||
* 参数同`ef.UIView.prototype.confirm`,但返回一个`Promise`对象 | ||
* | ||
* @return {er.Promise} 一个`Promise`对象,用户确认则进入`resolved`状态, | ||
* 用户取消则进入`rejected`状态 | ||
*/ | ||
BaseView.prototype.waitConfirm = function () { | ||
var dialog = this.confirm.apply(this, arguments); | ||
var deferred = new Deferred(); | ||
dialog.on('actionloaded', deferred.resolver.resolve); | ||
dialog.on('actionloadfail', deferred.resolver.reject); | ||
dialog.on('actionloadabort', deferred.resolver.reject); | ||
dialog.on('ok', deferred.resolver.resolve); | ||
dialog.on('cancel', deferred.resolver.reject); | ||
return deferred.promise; | ||
}; | ||
return deferred.promise; | ||
}; | ||
return BaseView; | ||
} | ||
); | ||
/** | ||
* 等待一个`DialogAction`加载完成 | ||
* | ||
* @return {er.Promise} 一个`Promise`对象, | ||
* 对应的Action加载完成时进入`resolved`状态, | ||
* 如Action加载失败则进入`rejected`状态 | ||
*/ | ||
BaseView.prototype.waitActionDialog = function () { | ||
var dialog = this.popActionDialog.apply(this, arguments); | ||
var deferred = new Deferred(); | ||
dialog.on('actionloaded', deferred.resolver.resolve); | ||
dialog.on('actionloadfail', deferred.resolver.reject); | ||
dialog.on('actionloadabort', deferred.resolver.reject); | ||
return deferred.promise; | ||
}; | ||
/** | ||
* 刷新权限设置,在Action加载过新内容时使用 | ||
*/ | ||
BaseView.prototype.refreshAuth = function () { | ||
var authPanel = this.get('authPanel'); | ||
if (authPanel) { | ||
authPanel.initAuth(); | ||
} | ||
}; | ||
return BaseView; | ||
}); |
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* @file FormAction基类 | ||
* @file 表单类型`Action`基类 | ||
* @author chestnutchen(chenli11@baidu.com) | ||
* @date $DATE$ | ||
*/ | ||
define( | ||
function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var Deferred = require('er/Deferred'); | ||
var BaseAction = require('./BaseAction'); | ||
define(function (require) { | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var Deferred = require('er/Deferred'); | ||
var BaseAction = require('./BaseAction'); | ||
/** | ||
* 表单Action基类 | ||
* | ||
* @extends BaseAction | ||
* @constructor | ||
*/ | ||
function FormAction() { | ||
BaseAction.apply(this, arguments); | ||
} | ||
/** | ||
* 表单类型`Action`基类 | ||
* | ||
* @extends BaseAction | ||
* @constructor | ||
*/ | ||
function FormAction() { | ||
BaseAction.apply(this, arguments); | ||
} | ||
util.inherits(FormAction, BaseAction); | ||
util.inherits(FormAction, BaseAction); | ||
FormAction.prototype.modelType = require('./FormModel'); | ||
/** | ||
* 设置表单提交成功后显示的信息,如果值为`null`或`undefined`则表示不显示任何信息 | ||
* | ||
* 如果该字段有内容,则系统使用该字段与提交表单后服务器返回的数据进行模板格式化, | ||
* 因此可以使用服务器返回的字段为占位符。模板使用`underscore.template`方法 | ||
* | ||
* @type {string | false | null} | ||
*/ | ||
FormAction.prototype.toastMessage = ''; | ||
/** | ||
* 当前页面的分类,始终为`"form"` | ||
* | ||
* @type {string} | ||
* @readonly | ||
* @override | ||
*/ | ||
FormAction.prototype.category = 'form'; | ||
/** | ||
* 获取表单提交成功后显示的信息 | ||
* | ||
* 默认提示信息为“保存成功” | ||
* | ||
* @param {Object} result 提交后服务器端返回的信息 | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getToastMessage = function (result) { | ||
var message = this.toastMessage; | ||
if (message == null) { | ||
return ''; | ||
} | ||
/** | ||
* 设置表单提交成功后显示的信息,如果值为`null`或`undefined`则表示不显示任何信息 | ||
* | ||
* 如果该字段有内容,则系统使用该字段与提交表单后服务器返回的数据进行模板格式化, | ||
* 因此可以使用服务器返回的字段为占位符。模板使用`underscore.template`方法 | ||
* | ||
* @type {string | false | null} | ||
*/ | ||
FormAction.prototype.toastMessage = ''; | ||
if (message) { | ||
return u.template(message, result || {}); | ||
} | ||
else { | ||
return '保存成功'; | ||
} | ||
}; | ||
/** | ||
* 获取表单提交成功后显示的信息 | ||
* | ||
* 默认提示信息为“保存成功” | ||
* | ||
* @param {Object} result 提交后服务器端返回的信息 | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getToastMessage = function (result) { | ||
var message = this.toastMessage; | ||
if (message == null) { | ||
return ''; | ||
} | ||
/** | ||
* 处理提交数据成功后的返回 | ||
* | ||
* @param {Object} result 提交成功后返回的内容 | ||
*/ | ||
FormAction.prototype.handleSubmitResult = function (result) { | ||
var toast = this.getToastMessage(result); | ||
if (toast) { | ||
this.view.showToast(toast); | ||
} | ||
if (typeof this.redirectAfterSubmit === 'function') { | ||
this.redirectAfterSubmit(result); | ||
} | ||
}; | ||
if (message) { | ||
return u.template(message, result || {}); | ||
} | ||
else { | ||
return '保存成功'; | ||
} | ||
}; | ||
/** | ||
* 默认提交/取消后跳转的路径 | ||
* @type {string} | ||
*/ | ||
FormAction.prototype.backLocation = null; | ||
/** | ||
* 处理提交数据成功后的返回 | ||
* | ||
* @param {Object} result 提交成功后返回的内容 | ||
*/ | ||
FormAction.prototype.handleSubmitResult = function (result) { | ||
var toast = this.getToastMessage(result); | ||
if (toast) { | ||
this.view.showToast(toast); | ||
} | ||
if (typeof this.redirectAfterSubmit === 'function') { | ||
this.redirectAfterSubmit(result); | ||
} | ||
}; | ||
/** | ||
* 执行提交成功后的跳转操作 | ||
* 在有referrer的情况下跳转至referrer | ||
* 在没有referrer的情况下history.back() | ||
* | ||
* 可在业务action里边重写 | ||
* | ||
* @param {Object} result 提交后服务器返回的数据 | ||
*/ | ||
FormAction.prototype.redirectAfterSubmit = function (result) { | ||
this.back(this.backLocation, true); | ||
}; | ||
/** | ||
* 执行提交成功后的跳转操作 | ||
* 在有referrer的情况下跳转至referrer | ||
* 在没有referrer的情况下history.back() | ||
* | ||
* 可在业务action里边重写 | ||
* | ||
* @param {Object} result 提交后服务器返回的数据 | ||
*/ | ||
FormAction.prototype.redirectAfterSubmit = function (result) { | ||
this.back(true); | ||
}; | ||
/** | ||
* 处理提交错误 | ||
* | ||
* @param {Object} message 失败时的message对象 | ||
*/ | ||
FormAction.prototype.handleSubmitError = function (message) { | ||
if (message && message.field) { | ||
this.view.notifyErrors(message.field); | ||
this.view.handleValidateInvalid(); | ||
} | ||
this.view.showToast('保存失败'); | ||
}; | ||
/** | ||
* 处理提交错误 | ||
* | ||
* @param {Object} 失败时的message对象 | ||
*/ | ||
FormAction.prototype.handleSubmitError = function (message) { | ||
if (message && message.field) { | ||
this.view.notifyErrors(message.field); | ||
} | ||
this.view.showToast('保存失败'); | ||
}; | ||
/** | ||
* 处理本地的验证错误 | ||
* 没有name的controls请自行扩展处理 | ||
* | ||
* @param {Object|string} errors 本地验证得到的错误信息 | ||
* object视为`FieldError`,string视为`GlobalError` | ||
* object的格式: | ||
* { | ||
* name1: errorMessage1, | ||
* name2: errorMessage2 | ||
* } | ||
* | ||
* @return {Mixed} 本地验证得到的错误信息 | ||
*/ | ||
FormAction.prototype.handleLocalValidationErrors = function (errors) { | ||
if (typeof errors === 'string') { | ||
this.view.alert(errors, '系统提示'); | ||
} | ||
else if (typeof errors === 'object') { | ||
this.view.notifyErrors(errors); | ||
} | ||
return errors; | ||
}; | ||
/** | ||
* 处理本地的验证错误 | ||
* 没有name的controls请自行扩展处理 | ||
* | ||
* @param {meta.FieldError[]} errors 本地验证得到的错误集合 | ||
* @return {Mixed} 处理完后的返回值,返回对象的情况下将显示错误 | ||
*/ | ||
FormAction.prototype.handleLocalValidationErrors = function (errors) { | ||
if (typeof errors === 'object') { | ||
this.view.notifyErrors(errors); | ||
return errors; | ||
} | ||
}; | ||
/** | ||
* 重置的操作 | ||
*/ | ||
FormAction.prototype.reset = function () { | ||
var reset = this.fire('reset'); | ||
if (!reset.isDefaultPrevented()) { | ||
this.view.rollbackFormData(this.model.getDefaultData()); | ||
} | ||
}; | ||
/** | ||
* 设置取消编辑时的提示信息标题 | ||
* | ||
* @type {string} | ||
*/ | ||
FormAction.prototype.cancelConfirmTitle = '确认取消编辑'; | ||
/** | ||
* 设置取消编辑时的提示信息标题 | ||
* | ||
* @type {string} | ||
*/ | ||
FormAction.prototype.cancelConfirmTitle = '确认取消编辑'; | ||
/** | ||
* 获取取消编辑时的提示信息标题 | ||
* | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getCancelConfirmTitle = function () { | ||
return this.cancelConfirmTitle; | ||
}; | ||
/** | ||
* 获取取消编辑时的提示信息标题 | ||
* | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getCancelConfirmTitle = function () { | ||
return this.cancelConfirmTitle; | ||
}; | ||
/** | ||
* 设置取消编辑时的提示信息内容 | ||
* | ||
* @type {string} | ||
*/ | ||
FormAction.prototype.cancelConfirmMessage = | ||
'取消编辑将不保留已经填写的数据,确定继续吗?'; | ||
/** | ||
* 设置取消编辑时的提示信息内容 | ||
* | ||
* @type {string} | ||
*/ | ||
FormAction.prototype.cancelConfirmMessage = | ||
'取消编辑将不保留已经填写的数据,确定继续吗?'; | ||
/** | ||
* 获取取消编辑时的提示信息内容 | ||
* | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getCancelConfirmMessage = function () { | ||
return this.cancelConfirmMessage; | ||
}; | ||
/** | ||
* 获取取消编辑时的提示信息内容 | ||
* | ||
* @return {string} | ||
*/ | ||
FormAction.prototype.getCancelConfirmMessage = function () { | ||
return this.cancelConfirmMessage; | ||
}; | ||
/** | ||
* 取消编辑的操作 | ||
* TODO: 把回滚表单数据放到reset按钮的逻辑里边 | ||
* submitcancel 回滚表单数据,使用原始数据重新填充 | ||
* aftercancel 执行取消编辑后重定向操作 | ||
*/ | ||
FormAction.prototype.cancel = function () { | ||
var submitCancelEvent = this.fire('submitcancel'); | ||
/** | ||
* 取消编辑的操作 | ||
* | ||
* @fires submitcancel | ||
* @fires aftercancel | ||
*/ | ||
FormAction.prototype.cancel = function () { | ||
var submitCancelEvent = this.fire('submitcancel'); | ||
var handleFinishEvent = this.fire('aftercancel'); | ||
if (!submitCancelEvent.isDefaultPrevented()) { | ||
this.view.rollbackFormData(); | ||
} | ||
if (!handleFinishEvent.isDefaultPrevented() && !submitCancelEvent.isDefaultPrevented()) { | ||
this.redirectAfterCancel(); | ||
} | ||
}; | ||
var handleFinishEvent = this.fire('aftercancel'); | ||
/** | ||
* 取消编辑时的确认提示 | ||
*/ | ||
FormAction.prototype.cancelEdit = function () { | ||
var formData = this.view.getFormData(); | ||
if (!handleFinishEvent.isDefaultPrevented()) { | ||
this.redirectAfterCancel(); | ||
} | ||
}; | ||
if (this.model.isFormDataChanged(formData)) { | ||
var options = { | ||
title: this.getCancelConfirmTitle(), | ||
content: this.getCancelConfirmMessage() | ||
}; | ||
this.view.waitConfirm(options) | ||
.then(u.bind(this.cancel, this)); | ||
} | ||
else { | ||
this.cancel(); | ||
} | ||
}; | ||
/** | ||
* 取消编辑时的确认提示 | ||
*/ | ||
FormAction.prototype.cancelEdit = function () { | ||
var formData = this.view.getFormData(); | ||
/** | ||
* 在取消编辑后重定向 | ||
* 在有referrer的情况下跳转至referrer | ||
* 在没有referrer的情况下history.back() | ||
* | ||
* 可在业务action里边重写 | ||
*/ | ||
FormAction.prototype.redirectAfterCancel = function () { | ||
this.back(this.backLocation, true); | ||
}; | ||
if (this.model.isFormDataChanged(formData)) { | ||
var options = { | ||
title: this.getCancelConfirmTitle(), | ||
content: this.getCancelConfirmMessage() | ||
}; | ||
this.view.waitConfirm(options) | ||
.then(u.bind(this.cancel, this)); | ||
/** | ||
* 提交表单 | ||
* | ||
* @param {Object} submitData 表单数据 | ||
* @return {er.Promise} | ||
*/ | ||
FormAction.prototype.submit = function (submitData) { | ||
var handleBeforeSubmit = this.fire('beforesubmit', {submitData: submitData}); | ||
if (!handleBeforeSubmit.isDefaultPrevented()) { | ||
try { | ||
var submitRequester = this.model.submitRequester; | ||
return submitRequester(submitData) | ||
.then( | ||
u.bind(this.handleSubmitResult, this), | ||
u.bind(this.handleSubmitError, this) | ||
); | ||
} | ||
else { | ||
this.cancel(); | ||
catch (ex) { | ||
return Deferred.rejected(ex); | ||
} | ||
}; | ||
} | ||
}; | ||
/** | ||
* 在取消编辑后重定向 | ||
* 在有referrer的情况下跳转至referrer | ||
* 在没有referrer的情况下history.back() | ||
* | ||
* 可在业务action里边重写 | ||
*/ | ||
FormAction.prototype.redirectAfterCancel = function () { | ||
this.back(true); | ||
}; | ||
/** | ||
* 校验表单前可扩展的操作,在提交之前做*异步*的校验 | ||
* 比如弹个框“提交有风险,是否要提交”之类 | ||
* | ||
* @param {Object} submitData 最终要提交的数据 | ||
* @return {*} | ||
* 当且仅当返回Deferred.rejected()阻止后续流程 | ||
* 其他任意返回结果均与Deferred.resolved()等效 | ||
*/ | ||
FormAction.prototype.beforeValidate = function (submitData) { | ||
return true; | ||
}; | ||
/** | ||
* 提交表单 | ||
* | ||
* @param {object} 表单数据 | ||
*/ | ||
FormAction.prototype.submit = function (submitData) { | ||
var localValidationResult = this.model.validateSubmitData(submitData); | ||
if (localValidationResult !== true) { | ||
var handleResult = this.handleLocalValidationErrors(localValidationResult); | ||
return Deferred.rejected(handleResult); | ||
} | ||
/** | ||
* 校验表单后可扩展的动作,在校验之后做*异步*的处理 | ||
* 比如弹个框“提交仍有风险,是否要提交”之类 | ||
* | ||
* @param {Object} submitData 最终要提交的数据 | ||
* @return {*} | ||
* 当且仅当返回Deferred.rejected()阻止后续流程 | ||
* 其他任意返回结果均与Deferred.resolved()等效 | ||
*/ | ||
FormAction.prototype.afterValidate = function (submitData) { | ||
return true; | ||
}; | ||
var handleBeforeSubmit = this.fire('beforesubmit'); | ||
/** | ||
* 进行校验,如果设置了Form的`autoValidate`则先进行表单控件自校验,否则只做自定义校验 | ||
* | ||
* @param {Object} submitData 最终要提交的数据 | ||
* @return {er.Promise} 处理完毕的Promise | ||
*/ | ||
FormAction.prototype.validate = function (submitData) { | ||
var localViewValidationResult = this.view.validate(); | ||
var localModelValidationResult = this.model.validateSubmitData(submitData); | ||
if (localViewValidationResult && localModelValidationResult === true) { | ||
return Deferred.resolved(submitData); | ||
} | ||
if (!handleBeforeSubmit.isDefaultPrevented()) { | ||
try { | ||
var submitRequester = this.model.submitRequester; | ||
return submitRequester(submitData) | ||
.then( | ||
u.bind(this.handleSubmitResult, this), | ||
u.bind(this.handleSubmitError, this) | ||
); | ||
} | ||
catch (ex) { | ||
return Deferred.rejected(ex); | ||
} | ||
} | ||
}; | ||
if (localModelValidationResult !== true) { | ||
this.handleLocalValidationErrors(localModelValidationResult); | ||
} | ||
this.view.handleValidateInvalid(); | ||
/** | ||
* 提交表单前锁定提交,完成提交操作后释放提交 | ||
*/ | ||
FormAction.prototype.submitEdit = function () { | ||
this.view.disableSubmit(); | ||
var formData = this.view.getFormData(); | ||
var submitData = this.model.getSubmitData(formData); | ||
return Deferred.rejected(); | ||
}; | ||
require('er/Deferred') | ||
.when(this.submit(submitData)) | ||
.ensure(this.view.enableSubmit()); | ||
}; | ||
/** | ||
* 此处为点击提交按钮后、表单校验前的准备 | ||
* 主要逻辑是锁定提交,在校验通过并完成提交操作后释放表单的提交操作 | ||
* | ||
* 提交流程: | ||
* disableSubmit -> | ||
* beforeValidate -> | ||
* validate -> | ||
* afterValidate -> | ||
* submit -> | ||
* enableSubmit | ||
* | ||
* 可针对业务需求扩展beforeValidate、afterValidate | ||
* validate、submit若与业务有冲突,也可自行修改,但不推荐 | ||
*/ | ||
FormAction.prototype.submitEdit = function () { | ||
this.view.disableSubmit(); | ||
var formData = this.view.getFormData(); | ||
var submitData = this.model.getSubmitData(formData); | ||
/** | ||
* 初始化交互行为 | ||
* | ||
* @protected | ||
* @override | ||
*/ | ||
FormAction.prototype.initBehavior = function () { | ||
BaseAction.prototype.initBehavior.apply(this, arguments); | ||
this.view.on('submit', this.submitEdit, this); | ||
this.view.on('cancel', this.cancelEdit, this); | ||
}; | ||
return FormAction; | ||
} | ||
); | ||
require('er/Deferred') | ||
.when(this.beforeValidate(submitData)) | ||
.then(u.bind(this.validate, this, submitData)) | ||
.then(u.bind(this.afterValidate, this, submitData)) | ||
.then(u.bind(this.submit, this, submitData)) | ||
.ensure(u.bind(this.view.enableSubmit, this.view)); | ||
}; | ||
/** | ||
* 初始化交互行为 | ||
* | ||
* @protected | ||
* @override | ||
*/ | ||
FormAction.prototype.initBehavior = function () { | ||
BaseAction.prototype.initBehavior.apply(this, arguments); | ||
this.view.on('submit', this.submitEdit, this); | ||
this.view.on('cancel', this.cancelEdit, this); | ||
this.view.on('reset', this.reset, this); | ||
}; | ||
return FormAction; | ||
}); |
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* @file FormModel基类 | ||
* @file 表单类型`Model`基类 | ||
* @author chestnutchen(chenli11@baidu.com) | ||
* @date $DATE$ | ||
*/ | ||
define( | ||
function (require) { | ||
var BaseModel = require('./BaseModel'); | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var datasource = require('er/datasource'); | ||
define(function (require) { | ||
var BaseModel = require('./BaseModel'); | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var datasource = require('er/datasource'); | ||
/** | ||
* 表单数据模型基类 | ||
* | ||
* @extends BaseModel | ||
* @constructor | ||
*/ | ||
function FormModel() { | ||
BaseModel.apply(this, arguments); | ||
} | ||
/** | ||
* 表单类型`Model`基类 | ||
* | ||
* @extends BaseModel | ||
* @constructor | ||
*/ | ||
function FormModel() { | ||
BaseModel.apply(this, arguments); | ||
} | ||
util.inherits(FormModel, BaseModel); | ||
util.inherits(FormModel, BaseModel); | ||
/** | ||
* 表单默认数据配置 | ||
* | ||
* @rule 常用的校验规则 | ||
* @formRequester 常规的缺省表单数据promise (可选) | ||
* | ||
*/ | ||
FormModel.prototype.formRequester = null; | ||
// 提交接口的promise的生成函数 | ||
FormModel.prototype.sumbitRequester = null; | ||
/** | ||
* 表单初始数据请求器 | ||
* | ||
* @type {?function} | ||
*/ | ||
FormModel.prototype.formRequester = null; | ||
// 默认请求参数,针对formData的请求发送 | ||
FormModel.prototype.defaultArgs = {}; | ||
/** | ||
* 表单提交请求器 | ||
* | ||
* @type {function} | ||
*/ | ||
FormModel.prototype.submitRequester = null; | ||
FormModel.prototype.defaultDatasource = { | ||
rule: datasource.constant(require('./rule')), | ||
formData: { | ||
retrieve: function (model) { | ||
if (model.get('formData')) { | ||
return model.get('formData'); | ||
/** | ||
* 默认请求参数,针对formData的请求发送 | ||
* | ||
* @type {Object} | ||
* @protected | ||
*/ | ||
FormModel.prototype.defaultArgs = {}; | ||
/** | ||
* 获取默认请求参数,针对formData的请求发送,默认直接返回`this.defaultArgs` | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
FormModel.prototype.getDefaultArgs = function () { | ||
return this.defaultArgs; | ||
}; | ||
/** | ||
* @inheritDoc | ||
*/ | ||
FormModel.prototype.defaultDatasource = { | ||
rule: datasource.constant(require('./rule')), | ||
formData: { | ||
retrieve: function (model) { | ||
if (model.get('formData')) { | ||
return model.get('formData'); | ||
} | ||
else { | ||
var formRequester = model.formRequester; | ||
if (formRequester) { | ||
return formRequester(model.getDefaultArgs()); | ||
} | ||
else { | ||
var formRequester = model.formRequester; | ||
var defaultArgs = model.defaultArgs; | ||
if (formRequester) { | ||
return formRequester(defaultArgs); | ||
} | ||
else { | ||
return {}; | ||
} | ||
return {}; | ||
} | ||
}, | ||
dump: false | ||
} | ||
}; | ||
} | ||
}, | ||
dump: false | ||
} | ||
}; | ||
/** | ||
* 获取缺省数据 | ||
* | ||
* @return {Object} | ||
*/ | ||
FormModel.prototype.getDefaultData = function () { | ||
return this.get('formData'); | ||
}; | ||
/** | ||
* 获取缺省数据 | ||
* | ||
* @return {Object} | ||
*/ | ||
FormModel.prototype.getDefaultData = function () { | ||
return this.get('formData'); | ||
}; | ||
/** | ||
* 获取最后提交使用的数据 | ||
* | ||
* @return {Object} | ||
*/ | ||
FormModel.prototype.getSubmitData = function (formData) { | ||
var data = u.extend(formData, this.getExtraData()); | ||
data = this.filterData(data); | ||
return data; | ||
}; | ||
/** | ||
* 获取最后提交使用的数据 | ||
* | ||
* @param {Object} formData 从表单中取得的数据 | ||
* @return {Object} 合并后用来提交的数据 | ||
*/ | ||
FormModel.prototype.getSubmitData = function (formData) { | ||
var data = u.extend(formData, this.getExtraData()); | ||
data = this.filterData(data); | ||
return data; | ||
}; | ||
/** | ||
* 为表单数据附加数据(比如上传文件的url) | ||
* | ||
* @param {Object} 附加数据 | ||
*/ | ||
FormModel.prototype.getExtraData = function () { | ||
return {}; | ||
}; | ||
/** | ||
* 为表单数据附加数据(比如上传文件的url) | ||
* | ||
* @return {Object} 附加数据 | ||
*/ | ||
FormModel.prototype.getExtraData = function () { | ||
return {}; | ||
}; | ||
/** | ||
* 过滤提交数据 | ||
* 提交前可对所有数据进行操作,比如转换数据格式 | ||
* | ||
* @param {Object} | ||
*/ | ||
FormModel.prototype.filterData = function(data) { | ||
return data; | ||
}; | ||
/** | ||
* 准备提交数据 | ||
* 提交前可对所有数据进行操作,比如转换数据格式 | ||
* | ||
* @param {Object} data 提交的数据 | ||
* @return {Object} 处理完毕的数据 | ||
*/ | ||
FormModel.prototype.prepareData = function(data) { | ||
return this.filterData(data); | ||
}; | ||
/** | ||
* 表单数据是否改动过 | ||
* | ||
* @param {Object} 新表单数据 | ||
* @return {Boolean} | ||
*/ | ||
FormModel.prototype.isFormDataChanged = function (formData) { | ||
var original = this.get('formData'); | ||
return !u.isEqual( u.purify(formData, null, true), u.purify(original, null, true)); | ||
}; | ||
/** | ||
* 准备提交数据 | ||
* 提交前可对所有数据进行操作,比如转换数据格式 | ||
* | ||
* @deprecated since v0.2.2 名字起得不好,后面使用`prepareData`替代 | ||
* @param {Object} data 提交的数据 | ||
* @return {Object} 处理完毕的数据 | ||
*/ | ||
FormModel.prototype.filterData = function(data) { | ||
return data; | ||
}; | ||
/** | ||
* 检验表单数据有效性,除了控件自动检测之外的逻辑可以在这里扩展 | ||
* | ||
* @param {Object} submitData 提交的数据,包含extraData | ||
* @return {meta.FieldError[] | true} | ||
* {field: {name: message}} | ||
* 返回`true`则验证通过 | ||
*/ | ||
FormModel.prototype.validateSubmitData = function (submitData) { | ||
return true; | ||
}; | ||
/** | ||
* 表单数据是否改动过,默认未改动,取消时直接返回 | ||
* 如果需要提示已修改请按需实现此功能 | ||
* | ||
* @param {Object} present 新表单数据 | ||
* @return {boolean} 是否有变动 | ||
*/ | ||
FormModel.prototype.isFormDataChanged = function (present) { | ||
return false; | ||
}; | ||
return FormModel; | ||
} | ||
); | ||
/** | ||
* 检验表单数据有效性,除了控件自动检测之外的逻辑可以在这里扩展 | ||
* | ||
* @param {Object} submitData 提交的数据,包含extraData | ||
* @return {Object|true} | ||
* 返回object形式为 | ||
* { | ||
* name1: message1 | ||
* name2: message2 | ||
* } | ||
* 的`fieldError`内容,可以触发`FormView`的`notifyErrors` | ||
* 返回`true`则验证通过 | ||
*/ | ||
FormModel.prototype.validateSubmitData = function (submitData) { | ||
return true; | ||
}; | ||
return FormModel; | ||
}); |
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* @file FormView基类 | ||
* @file 表单类型`View`基类 | ||
* @author chestnutchen(chenli11@baidu.com) | ||
* @date $DATE$ | ||
*/ | ||
define( | ||
function (require) { | ||
var util = require('er/util'); | ||
var BaseView = require('./BaseView'); | ||
var u = require('underscore'); | ||
define(function (require) { | ||
var util = require('er/util'); | ||
var BaseView = require('./BaseView'); | ||
var u = require('underscore'); | ||
var lib = require('esui/lib'); | ||
// 使用表单视图,有以下要求: | ||
// | ||
// - 有id为`form`的`Form`控件 | ||
// - 所有触发提交的按钮,会触发`form`的`submit`事件 | ||
// | ||
// 可选: | ||
// | ||
// - 可以有一个id为`cancel`的按钮,点击后会触发`cancel`事件 | ||
/** | ||
* 使用表单视图,有以下要求: | ||
* | ||
* - 有id为`form`的`Form`控件 | ||
* - 所有触发提交的按钮,会触发`form`的`submit`事件 | ||
* - 可以使用`Form`控件的`data-ui-auto-validate`属性, | ||
* 设置为`true`可以在submit之前自动校验含有`name`属性的`InputControl` | ||
* | ||
* 可选: | ||
* | ||
* - 可以有一个id为`cancel`的按钮,点击后会触发`cancel`事件 | ||
* - 可以有一个id为`reset`的按钮,点击后会触发`reset`事件 | ||
/** | ||
* 表单视图基类 | ||
* | ||
* @extends BaseView | ||
* @constructor | ||
*/ | ||
function FormView() { | ||
BaseView.apply(this, arguments); | ||
} | ||
/** | ||
* 表单类型`View`基类 | ||
* | ||
* @extends BaseView | ||
* @constructor | ||
*/ | ||
function FormView() { | ||
BaseView.apply(this, arguments); | ||
} | ||
util.inherits(FormView, BaseView); | ||
util.inherits(FormView, BaseView); | ||
/** | ||
* 从表单中获取数据 | ||
* | ||
* @return {Object} | ||
*/ | ||
FormView.prototype.getFormData = function () { | ||
var form = this.get('form'); | ||
return form ? form.getData() : {}; | ||
}; | ||
/** | ||
* 从表单中获取数据 | ||
* | ||
* @return {Object} | ||
*/ | ||
FormView.prototype.getFormData = function () { | ||
var form = this.get('form'); | ||
return u.extend( | ||
{}, | ||
form ? form.getData() : {}, | ||
this.getExtraFormData() | ||
); | ||
}; | ||
/** | ||
* 回滚表单数据 | ||
* | ||
* @param {Object} key:value形式的数据 key和input的name一一对应 | ||
*/ | ||
FormView.prototype.rollbackFormData = function () { | ||
this.setFormData(this.model.getDefaultData()); | ||
}; | ||
/** | ||
* 获取当前表单需要提交的额外数据 | ||
* | ||
* @return {Object} 表单数据 | ||
*/ | ||
FormView.prototype.getExtraFormData = function () { | ||
return {}; | ||
}; | ||
/** | ||
* 设置表单数据 | ||
* | ||
* @param {Object} key:value形式的数据 key和input的name一一对应 | ||
*/ | ||
FormView.prototype.setFormData = function (formData) { | ||
var form = this.get('form'); | ||
inputs = form.getInputControls(); | ||
u.each(inputs, function (input, index) { | ||
var key = input.name; | ||
if (formData) { | ||
if (u.has(formData, key)) { | ||
input.setValue(formData[key]); | ||
} | ||
/** | ||
* 回滚表单数据 | ||
* | ||
* @param {Object} defaultData key/value形式的数据,key和input的name一一对应 | ||
*/ | ||
FormView.prototype.rollbackFormData = function (defaultData) { | ||
this.setFormData(defaultData); | ||
}; | ||
/** | ||
* 设置表单数据 | ||
* | ||
* @param {Object} formData key:value形式的数据 key和input的name一一对应 | ||
*/ | ||
FormView.prototype.setFormData = function (formData) { | ||
var form = this.get('form'); | ||
var inputs = form.getInputControls(); | ||
u.each(inputs, function (input, index) { | ||
var key = input.name; | ||
if (formData) { | ||
if (u.has(formData, key)) { | ||
input.setValue(formData[key]); | ||
} | ||
}); | ||
this.setExtraFormData(formData); | ||
}; | ||
} | ||
}); | ||
this.setExtraFormData(formData); | ||
}; | ||
/** | ||
* 设置表单额外数据 | ||
* 这个接口提供给不是input的控件去扩展,自个玩去 | ||
* 不知道是不是又是可以砍掉的接口 | ||
* | ||
* @param {Object} key:value形式的数据 key和input的name一一对应 | ||
*/ | ||
FormView.prototype.setExtraFormData = function (formData) { | ||
/** | ||
* 设置表单额外数据 | ||
* 这个接口提供给不是input的控件去扩展,自个玩去 | ||
* | ||
* @param {Object} formData key:value形式的数据 key和input的name一一对应 | ||
*/ | ||
FormView.prototype.setExtraFormData = function (formData) { | ||
return; | ||
}; | ||
/** | ||
* 表单校验 | ||
* 为啥要有这东西?Form控件不是有了吗? | ||
* 问得好,Form控件的beforevalidate事件(同步)在FormView中已经阻止掉了 | ||
* 然后在FormAction中提供了异步的beforeValidate、validate、afterValidate的扩展点 | ||
* 因此FormView必须自己调validate | ||
* 这个方法会在FormAction.validite中和FormModel的校验一起做 | ||
* (还不是一堆蛋疼需求导致的... | ||
* | ||
* @return {boolean} 校验是否成功 | ||
*/ | ||
FormView.prototype.validate = function () { | ||
var form = this.get('form'); | ||
var isAutoValidate = form.get('autoValidate'); | ||
if (!isAutoValidate) { | ||
return true; | ||
} | ||
return form.validate(); | ||
}; | ||
/** | ||
* 向用户通知提交错误信息,默认根据`errors`的`key`字段查找对应`name`的控件并显示错误信息 | ||
* | ||
* @param {Object} errors 错误信息,每个key为控件`name`,value为`errorMessage` | ||
* | ||
*/ | ||
FormView.prototype.notifyErrors = function (errors) { | ||
if (typeof errors !== 'object') { | ||
return; | ||
}; | ||
} | ||
/** | ||
* 向用户通知提交错误信息,默认根据`field`字段查找对应`name`的控件并显示错误信息 | ||
* | ||
* @param {Object} errors 错误信息,每个key为控件`name`,value为`errorMessage` | ||
*/ | ||
FormView.prototype.notifyErrors = function (errors) { | ||
if (typeof errors !== 'object') { | ||
return; | ||
var Validity = require('esui/validator/Validity'); | ||
var ValidityState = require('esui/validator/ValidityState'); | ||
var form = this.get('form'); | ||
u.each(errors, function (message, field) { | ||
var state = new ValidityState(false, message); | ||
var validity = new Validity(); | ||
validity.addState('invalid', state); | ||
var input = form.getInputControls(field)[0]; | ||
if (input && typeof input.showValidity === 'function') { | ||
input.showValidity(validity); | ||
} | ||
}); | ||
}; | ||
var Validity = require('esui/validator/Validity'); | ||
var ValidityState = require('esui/validator/ValidityState'); | ||
var form = this.get('form'); | ||
/** | ||
* 重置表单 | ||
*/ | ||
function reset() { | ||
this.fire('reset'); | ||
} | ||
u.each(errors, function (field, message){ | ||
var state = new ValidityState(false, message); | ||
var validity = new Validity(); | ||
validity.addState('invalid', state); | ||
/** | ||
* 取消编辑 | ||
*/ | ||
function cancelEdit() { | ||
this.fire('cancel'); | ||
} | ||
var input = form.getInputControls(field)[0]; | ||
if (input && typeof input.showValidity === 'function') { | ||
input.showValidity(validity); | ||
/** | ||
* 进入提交前的处理 | ||
* | ||
* @param {Event} e 事件对象 | ||
*/ | ||
function submit(e) { | ||
e.preventDefault(); | ||
this.fire('submit'); | ||
} | ||
/** | ||
* 若页面在目标dom元素下方,设置页面scrollTop至该元素 | ||
* | ||
* @param {Element} element label的dom元素 | ||
*/ | ||
function scrollTo(element) { | ||
var offset = lib.getOffset(element); | ||
if (lib.page.getScrollTop() > offset.top) { | ||
element.scrollIntoView(true); | ||
} | ||
} | ||
/** | ||
* 处理esui表单控件自动校验出错 | ||
* 定位至第一个出错的控件 | ||
* | ||
* @param {Object} form esui表单控件 | ||
* @fire {Event} scrolltofirsterror 定位至页面第一个出错的控件 | ||
*/ | ||
FormView.prototype.handleValidateInvalid = function () { | ||
var me = this; | ||
var form = this.get('form'); | ||
u.some(form.getInputControls(), function (input) { | ||
if (input.hasState('validity-invalid')) { | ||
var e = me.fire('scrolltofirsterror', {firstErrValidity: input}); | ||
if (!e.isDefaultPrevented()) { | ||
scrollTo(input.main); | ||
} | ||
}); | ||
}; | ||
return true; | ||
} | ||
}); | ||
}; | ||
/** | ||
* 取消编辑 | ||
*/ | ||
function cancelEdit() { | ||
this.fire('cancel'); | ||
/** | ||
* 绑定控件事件 | ||
* | ||
* @override | ||
*/ | ||
FormView.prototype.bindEvents = function () { | ||
var form = this.get('form'); | ||
if (form) { | ||
form.on('beforevalidate', submit, this); | ||
} | ||
/** | ||
* 提交数据 | ||
*/ | ||
function submit() { | ||
this.fire('submit'); | ||
var resetButton = this.get('reset'); | ||
if (resetButton) { | ||
resetButton.on('click', reset, this); | ||
} | ||
/** | ||
* 绑定控件事件 | ||
* | ||
* @override | ||
*/ | ||
FormView.prototype.bindEvents = function () { | ||
var form = this.get('form'); | ||
if (form) { | ||
form.on('submit', submit, this); | ||
} | ||
var cancelButton = this.get('cancel'); | ||
if (cancelButton) { | ||
cancelButton.on('click', cancelEdit, this); | ||
} | ||
var cancelButton = this.get('cancel'); | ||
if (cancelButton) { | ||
cancelButton.on('click', cancelEdit, this); | ||
} | ||
BaseView.prototype.bindEvents.apply(this, arguments); | ||
}; | ||
BaseView.prototype.bindEvents.apply(this, arguments); | ||
}; | ||
/** | ||
* 禁用提交操作 | ||
*/ | ||
FormView.prototype.disableSubmit = function () { | ||
if (this.viewContext) { | ||
this.getGroup('submit').disable(); | ||
} | ||
}; | ||
/** | ||
* 禁用提交操作 | ||
*/ | ||
FormView.prototype.disableSubmit = function () { | ||
if (this.viewContext) { | ||
this.getGroup('submit').disable(); | ||
} | ||
}; | ||
/** | ||
* 启用提交操作 | ||
*/ | ||
FormView.prototype.enableSubmit = function () { | ||
if (this.viewContext) { | ||
this.getGroup('submit').enable(); | ||
} | ||
}; | ||
/** | ||
* 启用提交操作 | ||
*/ | ||
FormView.prototype.enableSubmit = function () { | ||
if (this.viewContext) { | ||
this.getGroup('submit').enable(); | ||
} | ||
}; | ||
return FormView; | ||
} | ||
); | ||
return FormView; | ||
}); |
/** | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* @ignore | ||
* @file 列表Action基类 | ||
* @author Justineo | ||
* @date $DATE$ | ||
* @file 列表类型`Action`基类 | ||
* @author Justineo(justice360@gmail.com) | ||
*/ | ||
define( | ||
function (require) { | ||
var BaseAction = require('./BaseAction'); | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var URL = require('er/URL'); | ||
/** | ||
* 列表Action基类 | ||
* | ||
* @param {string} [entityName] 负责的实体名称 | ||
* @extends BaseAction | ||
* @constructor | ||
*/ | ||
function ListAction(entityName) { | ||
BaseAction.apply(this, arguments); | ||
} | ||
define(function (require) { | ||
var BaseAction = require('./BaseAction'); | ||
var util = require('er/util'); | ||
var u = require('underscore'); | ||
var URL = require('er/URL'); | ||
util.inherits(ListAction, BaseAction); | ||
/** | ||
* 列表`Action`基类 | ||
* | ||
* @extends BaseAction | ||
* @constructor | ||
*/ | ||
function ListAction() { | ||
BaseAction.apply(this, arguments); | ||
} | ||
ListAction.prototype.modelType = './ListModel'; | ||
util.inherits(ListAction, BaseAction); | ||
/** | ||
* 进行查询 | ||
* | ||
* @param {Object} args 查询参数 | ||
*/ | ||
ListAction.prototype.performSearch = function (args) { | ||
// 去除默认参数值 | ||
var defaultArgs = this.model.getDefaultArgs(); | ||
var extraArgs = this.model.getExtraQuery(); | ||
args = u.chain(args) | ||
.purify(defaultArgs) | ||
.extend(extraArgs) | ||
.value(); | ||
var event = this.fire('search', { args: args }); | ||
if (!event.isDefaultPrevented()) { | ||
this.redirectForSearch(args); | ||
} | ||
}; | ||
/** | ||
* 在搜索、翻页等操作后选择触发跳转还是仅刷新列表 | ||
* | ||
* @type {boolean} | ||
*/ | ||
ListAction.prototype.redirectAfterChange = true; | ||
/** | ||
* 进行查询引起的重定向操作 | ||
* | ||
* @param {Object} args 查询参数 | ||
*/ | ||
ListAction.prototype.redirectForSearch = function (args) { | ||
var path = this.model.get('url').getPath(); | ||
var url = URL.withQuery(path, args); | ||
this.redirect(url, { force: true }); | ||
}; | ||
/** | ||
* 进行查询 | ||
* | ||
* @param {Object} args 查询参数 | ||
*/ | ||
ListAction.prototype.performSearch = function (args) { | ||
// 去除默认参数值 | ||
var defaultArgs = this.model.getDefaultArgs(); | ||
var extraArgs = this.model.getExtraQuery(); | ||
args = u.chain(args) | ||
.extend(extraArgs) | ||
.purify(defaultArgs) | ||
.value(); | ||
/** | ||
* 获取指定页码的跳转URL | ||
* | ||
* @param {number} pageNo 指定的页码 | ||
* @return {string} | ||
*/ | ||
ListAction.prototype.getURLForPage = function (pageNo) { | ||
var url = this.context.url; | ||
var path = url.getPath(); | ||
var query = url.getQuery(); | ||
query.pageNo = pageNo; | ||
var event = this.fire('search', {args: args}); | ||
if (!event.isDefaultPrevented()) { | ||
this.redirectForSearch(args); | ||
} | ||
}; | ||
// 第一页省去页码参数 | ||
if (pageNo === 1) { | ||
query = u.omit(query, 'pageNo'); | ||
} | ||
/** | ||
* 进行查询引起的重定向操作 | ||
* | ||
* @param {Object} args 查询参数 | ||
*/ | ||
ListAction.prototype.redirectForSearch = function (args) { | ||
var path = this.model.get('url').getPath(); | ||
var url = URL.withQuery(path, args); | ||
this.loadList(url); | ||
}; | ||
return require('er/URL').withQuery(path, query).toString(); | ||
}; | ||
/** | ||
* 获取指定页码的跳转URL | ||
* | ||
* @param {number} pageNo 指定的页码 | ||
* @return {er/URL} 生成的分页URL对象 | ||
*/ | ||
ListAction.prototype.getURLForPage = function (pageNo) { | ||
var url = this.model.get('url'); | ||
var path = url.getPath(); | ||
var query = url.getQuery(); | ||
query.pageNo = pageNo; | ||
/** | ||
* 查询的事件处理函数 | ||
* | ||
* @param {Object} e 事件对象 | ||
* @ignore | ||
*/ | ||
function search(e) { | ||
this.performSearch(e.args); | ||
// 第一页省去页码参数 | ||
if (pageNo === 1) { | ||
query = u.omit(query, 'pageNo'); | ||
} | ||
/** | ||
* 前往指定页 | ||
* | ||
* @param {mini-event.Event} e 事件对象 | ||
* @param {number} e.page 前往的页码 | ||
* @ignore | ||
*/ | ||
function forwardToPage(e) { | ||
var event = this.fire('pagechange', { page: e.page }); | ||
if (!event.isDefaultPrevented()) { | ||
var url = this.getURLForPage(e.page); | ||
this.redirect(url); | ||
} | ||
return require('er/URL').withQuery(path, query); | ||
}; | ||
/** | ||
* 查询的事件处理函数 | ||
* | ||
* @param {Object} e 事件对象 | ||
* @ignore | ||
*/ | ||
function search(e) { | ||
this.performSearch(e.args); | ||
} | ||
/** | ||
* 前往指定页 | ||
* | ||
* @param {mini-event.Event} e 事件对象 | ||
* @param {number} e.page 前往的页码 | ||
* @ignore | ||
*/ | ||
function forwardToPage(e) { | ||
var event = this.fire('pagechange', {page: e.page}); | ||
if (!event.isDefaultPrevented()) { | ||
var url = this.getURLForPage(e.page); | ||
this.loadList(url); | ||
} | ||
} | ||
/** | ||
* 初始化交互行为 | ||
* | ||
* @protected | ||
* @override | ||
*/ | ||
ListAction.prototype.initBehavior = function () { | ||
BaseAction.prototype.initBehavior.apply(this, arguments); | ||
this.view.on('search', search, this); | ||
this.view.on('pagesizechange', search, this); | ||
this.view.on('pagechange', forwardToPage, this); | ||
}; | ||
/** | ||
* 根据新的URL参数刷新列表 | ||
* | ||
* @param {er.URL} [url] 新的URL对象,没有时按当前URL刷新 | ||
* @return {er.Promise} 返回请求的Promise对象 | ||
*/ | ||
ListAction.prototype.loadList = function (url) { | ||
if (this.redirectAfterChange) { | ||
this.redirect(url, {force: true}); | ||
} | ||
else { | ||
var me = this; | ||
url = url || me.model.get('url'); | ||
/** | ||
* 根据布局变化重新调整自身布局 | ||
*/ | ||
ListAction.prototype.adjustLayout = function () { | ||
this.view.adjustLayout(); | ||
}; | ||
return ListAction; | ||
} | ||
); | ||
return me.model.loadData(url).then(function () { | ||
me.redirect(url, {silent: true}); | ||
me.view.refresh(); | ||
}); | ||
} | ||
}; | ||
/** | ||
* 初始化交互行为 | ||
* | ||
* @protected | ||
* @override | ||
*/ | ||
ListAction.prototype.initBehavior = function () { | ||
BaseAction.prototype.initBehavior.apply(this, arguments); | ||
this.view.on('search', search, this); | ||
this.view.on('pagechange', forwardToPage, this); | ||
}; | ||
/** | ||
* 初始化交互行为 | ||
* | ||
* @protected | ||
* @override | ||
*/ | ||
ListAction.prototype.reload = function () { | ||
if (this.redirectAfterChange) { | ||
BaseAction.prototype.reload.call(this); | ||
} | ||
else { | ||
this.loadList(); | ||
} | ||
}; | ||
/** | ||
* 根据布局变化重新调整自身布局 | ||
*/ | ||
ListAction.prototype.adjustLayout = function () { | ||
this.view.adjustLayout(); | ||
}; | ||
// /** | ||
// * @inheritDoc | ||
// * | ||
// * @protected | ||
// * @override | ||
// */ | ||
// ListAction.prototype.filterRedirect = function (url) { | ||
// if (url.getPath() !== this.model.get('url').getPath() | ||
// || this.redirectAfterChange) { | ||
// return true; | ||
// } | ||
// this.loadList(url); | ||
// return false; | ||
// }; | ||
return ListAction; | ||
}); |
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* @file Model基类 | ||
* @file 列表类型Model基类 | ||
* @author Justineo(justice360@gmail.com) | ||
* @date $DATE$ | ||
*/ | ||
define( | ||
function (require) { | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var BaseModel = require('./BaseModel'); | ||
var batUtil = require('../util'); | ||
define(function (require) { | ||
var u = require('underscore'); | ||
var util = require('er/util'); | ||
var BaseModel = require('./BaseModel'); | ||
var batUtil = require('../util'); | ||
/** | ||
* 业务`Model`基类 | ||
* | ||
* @param {Object=} context 初始化时的数据 | ||
* | ||
* @constructor | ||
* @extends ef/BaseModel | ||
*/ | ||
function ListModel(context) { | ||
BaseModel.call(this, context); | ||
} | ||
/** | ||
* 业务`Model`基类 | ||
* | ||
* @param {Object} [context] 初始化时的数据 | ||
* | ||
* @constructor | ||
* @extends ef.BaseModel | ||
*/ | ||
function ListModel(context) { | ||
BaseModel.call(this, context); | ||
} | ||
util.inherits(ListModel, BaseModel); | ||
util.inherits(ListModel, BaseModel); | ||
ListModel.prototype.list; | ||
/** | ||
* 列表数据请求器 | ||
* | ||
* @type {function} | ||
*/ | ||
ListModel.prototype.listRequester; | ||
/** | ||
* 配置默认查询参数 | ||
* | ||
* 如果某个参数与这里的值相同,则不会加到URL中 | ||
* | ||
* 创建`Model`时,如果某个参数不存在,则会自动补上这里的值 | ||
* | ||
* @type {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.defaultArgs = {}; | ||
/** | ||
* 配置默认查询参数 | ||
* | ||
* 如果某个参数与这里的值相同,则不会加到URL中 | ||
* | ||
* 创建`Model`时,如果某个参数不存在,则会自动补上这里的值 | ||
* | ||
* @type {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.defaultArgs = {}; | ||
/** | ||
* 默认查询参数 | ||
* | ||
* 参考{@link ListModel#defaultArgs}属性的说明 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getDefaultArgs = function () { | ||
return u.defaults(this.defaultArgs || {}, { pageNo: 1 }); | ||
}; | ||
/** | ||
* 默认查询参数 | ||
* | ||
* 参考{@link ListModel#defaultArgs}属性的说明 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getDefaultArgs = function () { | ||
return u.defaults(this.defaultArgs || {}, {pageNo: 1}); | ||
}; | ||
/** | ||
* 默认数据源 | ||
*/ | ||
ListModel.prototype.defaultDatasource = { | ||
listPage: { | ||
retrieve: function (model) { | ||
return model.listRequester(model.getQuery()) | ||
.then(function(data) { | ||
var page = data; | ||
page.tableData = page.result; | ||
delete page.result; | ||
return page; | ||
}); | ||
}, | ||
dump: true | ||
/** | ||
* 转换列表数据 | ||
* 将返回值中的`result`字段改为`tableData`来放到Model中 | ||
* | ||
* @param {Object} data 列表请求接口返回的数据 | ||
* @return {Object} 转换完毕的数据 | ||
*/ | ||
function adaptData(data) { | ||
var page = data; | ||
page.tableData = page.result; | ||
delete page.result; | ||
return page; | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
ListModel.prototype.defaultDatasource = { | ||
listPage: { | ||
retrieve: function (model) { | ||
return model.listRequester(model.getQuery()) | ||
.then(adaptData); | ||
}, | ||
dump: true | ||
}, | ||
// 展示时间区间 | ||
time: { | ||
retrieve: function (model) { | ||
var startTime = model.get('startTime'); | ||
var endTime = model.get('endTime'); | ||
// 展示时间区间 | ||
time: { | ||
retrieve: function (model) { | ||
var startTime = model.get('startTime'); | ||
var endTime = model.get('endTime'); | ||
// 有输入参数 | ||
if (startTime && endTime) { | ||
return { | ||
time: batUtil.getTimeRange(startTime, endTime) | ||
}; | ||
} | ||
// 有输入参数 | ||
if (startTime && endTime) { | ||
return { | ||
time: batUtil.getTimeRange(startTime, endTime) | ||
}; | ||
} | ||
// 无输入参数,取默认配置,若无则不需要输出 | ||
var range = model.defaultTimeRange; | ||
if (range) { | ||
return { | ||
time: range | ||
}; | ||
} | ||
else { | ||
return {}; | ||
} | ||
}, | ||
dump: true | ||
// 无输入参数,取默认配置,若无则不需要输出 | ||
var range = model.defaultTimeRange; | ||
if (range) { | ||
return { | ||
time: range | ||
}; | ||
} | ||
else { | ||
return {}; | ||
} | ||
}, | ||
dump: true | ||
}, | ||
// 分页URL模板,就是当前URL中把`page`字段替换掉 | ||
urlTemplate: function (model) { | ||
var url = model.get('url'); | ||
var path = url.getPath(); | ||
// 由于`withQuery`会做URL编码,因此不能直接`query.page = '${page}'`, | ||
// 会被编码成`%24%7Bpage%7D`,此处只能直接操作字符串 | ||
var query = url.getQuery(); | ||
delete query.pageNo; | ||
var template = '#' + require('er/URL').withQuery(path, query); | ||
var delimiter = u.isEmpty(query) ? '~' : '&'; | ||
template += delimiter + 'pageNo=${page}'; | ||
return template; | ||
} | ||
}; | ||
// 分页URL模板,就是当前URL中把`page`字段替换掉 | ||
urlTemplate: function (model) { | ||
var url = model.get('url'); | ||
var path = url.getPath(); | ||
// 由于`withQuery`会做URL编码,因此不能直接`query.page = '${page}'`, | ||
// 会被编码成`%24%7Bpage%7D`,此处只能直接操作字符串 | ||
var query = url.getQuery(); | ||
delete query.pageNo; | ||
var template = '#' + require('er/URL').withQuery(path, query); | ||
var delimiter = u.isEmpty(query) ? '~' : '&'; | ||
template += delimiter + 'pageNo=${page}'; | ||
return template; | ||
} | ||
}; | ||
/** | ||
* 默认选择的时间范围 | ||
* | ||
* @type {?{begin: Date, end: Date}} | ||
* @protected | ||
*/ | ||
ListModel.prototype.defaultTimeRange = null; | ||
/** | ||
* 默认选择的时间范围 | ||
* | ||
* @type {?{begin: Date, end: Date}} | ||
* @protected | ||
*/ | ||
ListModel.prototype.defaultTimeRange = null; | ||
/** | ||
* 默认选择的时间范围 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getQuery = function () { | ||
var url = this.get('url'); | ||
var query = url.getQuery(); | ||
/** | ||
* 生成默认的后端请求参数 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getQuery = function () { | ||
var url = this.get('url'); | ||
var query = url.getQuery(); | ||
// 取一下默认时间配置 | ||
var range = this.defaultTimeRange; | ||
if (range) { | ||
range = batUtil.getTimeRange(range.begin, range.end, { | ||
outputFormat: 'YYYYMMDDHHmmss', | ||
beginKey: 'startTime', | ||
endKey: 'endTime' | ||
}); | ||
} | ||
else { | ||
range = {}; | ||
} | ||
// 取一下默认时间配置 | ||
var range = this.defaultTimeRange; | ||
if (range) { | ||
range = batUtil.getTimeRange(range.begin, range.end, { | ||
outputFormat: 'YYYYMMDDHHmmss', | ||
beginKey: 'startTime', | ||
endKey: 'endTime' | ||
}); | ||
} | ||
else { | ||
range = {}; | ||
} | ||
// 合并默认参数、附加参数,最后再统一处理一下输出 | ||
query = u.chain(query) | ||
.defaults(this.getDefaultArgs()) | ||
.defaults(range) | ||
.extend(this.getExtraQuery()) | ||
.value(); | ||
// 合并默认参数、附加参数,最后再统一处理一下输出 | ||
u.chain(query) | ||
.defaults(this.getDefaultArgs()) | ||
.defaults(range) | ||
.extend(this.getExtraQuery()); | ||
return this.filterQuery(query); | ||
}; | ||
return this.prepareQuery(query); | ||
}; | ||
/** | ||
* 获取附加的请求参数 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getExtraQuery = function () { | ||
return {}; | ||
}; | ||
/** | ||
* 获取除列表本身参数外附加的请求参数 | ||
* | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.getExtraQuery = function () { | ||
return {}; | ||
}; | ||
/** | ||
* 对合并好的请求参数进行统一的后续处理 | ||
* | ||
* @param {Object} query 需要处理的参数对象 | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.filterQuery = function(query) { | ||
return query; | ||
}; | ||
/** | ||
* 对合并好的请求参数进行统一的后续处理 | ||
* | ||
* @deprecated since v0.2.1 名字起得不好,后面使用`prepareQuery`替代 | ||
* @param {Object} query 需要处理的参数对象 | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.filterQuery = function (query) { | ||
return query; | ||
}; | ||
return ListModel; | ||
} | ||
); | ||
/** | ||
* 对合并好的请求参数进行统一的后续处理 | ||
* | ||
* @param {Object} query 需要处理的参数对象 | ||
* @return {Object} | ||
* @protected | ||
*/ | ||
ListModel.prototype.prepareQuery = function (query) { | ||
return this.filterQuery(query); | ||
}; | ||
/** | ||
* 重新读取列表数据 | ||
* | ||
* @param {er/URL} url 根据指定URL读取数据 | ||
* @return {er/Promise} 返回异步请求的Promise对象 | ||
* @protected | ||
*/ | ||
ListModel.prototype.loadData = function (url) { | ||
var me = this; | ||
var urlQuery = url.getQuery(); | ||
me.set('url', url); | ||
me.fill(urlQuery); | ||
return me.listRequester(me.getQuery()) | ||
.then(function(data) { | ||
me.fill(adaptData(data)); | ||
}); | ||
}; | ||
return ListModel; | ||
}); |
/** | ||
* @file [Please Input File Description] | ||
* @file 列表类型`View`基类 | ||
* @author Justineo(justice360@gmail.com) | ||
@@ -13,7 +13,8 @@ */ | ||
var moment = require('moment'); | ||
/** | ||
* [Please Input View Description] | ||
* | ||
* 列表`View`基类 | ||
* | ||
* @constructor | ||
* @extends ef.BaseView | ||
*/ | ||
@@ -23,5 +24,11 @@ function ListView() { | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
ListView.prototype.uiProperties = {}; | ||
/** | ||
* @inheritDoc | ||
*/ | ||
ListView.prototype.uiEvents = {}; | ||
@@ -32,3 +39,2 @@ | ||
* | ||
* @param {ListView} this 当前视图实例 | ||
* @param {mini-event.Event} e 控件事件对象 | ||
@@ -45,11 +51,7 @@ */ | ||
// 总是带上每页显示数 | ||
args.pageSize = this.get('pager').get('pageSize'); | ||
this.fire('search', { args: args }); | ||
this.fire('search', {args: args}); | ||
}; | ||
/** | ||
* 获取查询参数,默认是取`filter`表单的所有数据,加上表格的排序字段 | ||
* 获取查询参数,默认是取`filter`表单的所有数据,加上表格的排序字段和每页显示条目数 | ||
* | ||
@@ -62,2 +64,3 @@ * @return {Object} | ||
var args = form ? form.getData() : {}; | ||
// 加上原本的排序方向和排序字段名 | ||
@@ -72,7 +75,17 @@ args.order = this.model.get('order'); | ||
} | ||
// 日期是独立的 | ||
var range = this.get('range').getValue().split(','); | ||
args.startTime = moment(range[0]).format('YYYYMMDDHHmmss'); | ||
args.endTime = moment(range[1]).format('YYYYMMDDHHmmss'); | ||
var range = this.get('range'); | ||
if (range) { | ||
range = range.getValue().split(','); | ||
args.startTime = moment(range[0]).format('YYYYMMDDHHmmss'); | ||
args.endTime = moment(range[1]).format('YYYYMMDDHHmmss'); | ||
} | ||
// 带上每页显示数 | ||
var pager = this.get('pager'); | ||
if (pager) { | ||
args.pageSize = pager.get('pageSize'); | ||
} | ||
return args; | ||
@@ -89,5 +102,67 @@ }; | ||
var page = e.target.get('page'); | ||
this.fire('pagechange', { page: page }); | ||
this.fire('pagechange', {page: page}); | ||
} | ||
/** | ||
* 根据表格中所选择的行来控制批量更新按钮的启用/禁用状态 | ||
*/ | ||
ListView.prototype.updateBatchButtonStatus = function () { | ||
var items = this.getSelectedItems(); | ||
this.getGroup('batch').set('disabled', u.isEmpty(items)); | ||
this.getGroup('batch').set('readOnly', u.isEmpty(items)); | ||
}; | ||
/** | ||
* 获取table已经选择的列的数据 | ||
* | ||
* @return {Object[]} 当前table的已选择列对应的数据 | ||
*/ | ||
ListView.prototype.getSelectedItems = function () { | ||
var table = this.get('table'); | ||
return table ? table.getSelectedItems() : []; | ||
}; | ||
/** | ||
* 触发批量操作 | ||
* | ||
* @param {Object} e 控件事件对象 | ||
* @ignore | ||
*/ | ||
function batchModify(e) { | ||
var args = { | ||
// 批量操作的类型 | ||
action: e.target.getData('type'), | ||
selectedItems: this.get('table').getSelectedItems() | ||
}; | ||
this.fire('batchmodify', args); | ||
} | ||
/** | ||
* 侧边栏模式改变时要调整整体布局 | ||
* | ||
* @param {Object} e 模式切换事件对象 | ||
*/ | ||
function sidebarModeChange(e) { | ||
// Sidebar目前只提供了操作DOM的方式更新布局,需要对ESUI做优化后再升级这块 | ||
var neighbor = document.getElementById('neighbor'); | ||
if (!neighbor) { | ||
return; | ||
} | ||
if (e.mode === 'fixed') { | ||
neighbor.className = 'ui-sidebar-neighbor'; | ||
} | ||
else { | ||
neighbor.className = 'ui-sidebar-neighbor-hide'; | ||
} | ||
this.adjustLayout(); | ||
} | ||
/** | ||
* @inheritDoc | ||
*/ | ||
ListView.prototype.bindEvents = function() { | ||
@@ -103,2 +178,4 @@ var pager = this.get('pager'); | ||
if (table) { | ||
// 选中表格行后控制批量更新按钮的启用/禁用状态 | ||
table.on('select', this.updateBatchButtonStatus, this); | ||
// 表格排序触发查询 | ||
@@ -114,7 +191,79 @@ table.on('sort', this.submitSearch, this); | ||
var sidebar = this.get('sidebar'); | ||
if (sidebar) { | ||
sidebar.on('modechange', sidebarModeChange, this); | ||
} | ||
u.each( | ||
this.getGroup('batch'), | ||
function (button) { | ||
if (button instanceof require('esui/Button')) { | ||
// 批量更新 | ||
button.on('click', batchModify, this); | ||
} | ||
}, | ||
this | ||
); | ||
BaseView.prototype.bindEvents.apply(this, arguments); | ||
}; | ||
/** | ||
* 控制元素展现 | ||
* | ||
* @override | ||
*/ | ||
ListView.prototype.enterDocument = function () { | ||
BaseView.prototype.enterDocument.apply(this, arguments); | ||
this.updateBatchButtonStatus(); | ||
this.adjustLayout(); | ||
}; | ||
/** | ||
* 根据布局变化重新调整自身布局 | ||
*/ | ||
ListView.prototype.adjustLayout = function () { | ||
var table = this.get('table'); | ||
if (table) { | ||
table.adjustWidth(); | ||
} | ||
}; | ||
/** | ||
* 根据Model数据重新渲染页面 | ||
*/ | ||
ListView.prototype.refresh = function () { | ||
// 刷新列表 | ||
this.refreshList(); | ||
// 最后刷新权限显示 | ||
this.refreshAuth(); | ||
}; | ||
/** | ||
* 根据Model数据重新渲染列表 | ||
*/ | ||
ListView.prototype.refreshList = function () { | ||
var model = this.model; | ||
var table = this.get('table'); | ||
if (table) { | ||
table.setDatasource(model.get('tableData')); | ||
} | ||
var pager = this.get('pager'); | ||
if (pager) { | ||
pager.setProperties( | ||
{ | ||
count: model.get('totalCount'), | ||
page: model.get('pageNo'), | ||
pageSize: model.get('pageSize') | ||
}, | ||
{silent: true} | ||
); | ||
} | ||
}; | ||
require('er/util').inherits(ListView, BaseView); | ||
return ListView; | ||
}); |
@@ -18,7 +18,28 @@ /** | ||
/** | ||
* 电子邮件地址正则 | ||
* 电子邮件地址正则字符串 | ||
*/ | ||
pattern: '^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$' | ||
pattern: '^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$', | ||
internal: { | ||
/** | ||
* 公司电子邮件地址正则字符串 | ||
*/ | ||
pattern: '^\\w+([-+.]\\w+)*@(\\w+\\.)?baidu\\.com$' | ||
} | ||
}, | ||
multiEmail: { | ||
/** | ||
* 电子邮件地址正则字符串 | ||
*/ | ||
pattern: '^((\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*),?)+$', | ||
internal: { | ||
/** | ||
* 公司电子邮件地址正则字符串 | ||
*/ | ||
pattern: '^((\\w+([-+.]\\w+)*@(\\w+\\.)?baidu\\.com),?)+$' | ||
} | ||
}, | ||
description: { | ||
@@ -35,3 +56,3 @@ /** | ||
*/ | ||
pattern: '^(1(3|4|5|8)\\d{9})?$' | ||
pattern: '^(1(3|4|5|8)\\d{9})$' | ||
}, | ||
@@ -44,3 +65,3 @@ | ||
*/ | ||
pattern: '(^((0\\d{2,3})-)(\\d{7,8})(-(\\d{3,}))?$)|(^(1(3|4|5|8)\\d{9})?$)' | ||
pattern: '(^((0\\d{2,3})-)(\\d{7,8})(-(\\d{3,}))?$)|(^(1(3|4|5|8)\\d{9})$)' | ||
}, | ||
@@ -62,3 +83,3 @@ | ||
/** | ||
* 正整数正则 | ||
* 正整数正则字符串 | ||
*/ | ||
@@ -70,3 +91,3 @@ pattern: '^\\d+$' | ||
/** | ||
* 价格类数字正则 | ||
* 价格类数字正则字符串 | ||
* 精确至小数点后两位 | ||
@@ -73,0 +94,0 @@ */ |
/** | ||
* @file [Please input file description] | ||
* @file 常量保存与转换 | ||
* @author Justineo(justice360@gmail.com) | ||
@@ -10,5 +10,23 @@ */ | ||
var map = {}; | ||
/** | ||
* [Please input module description] | ||
*/ | ||
var VL_MAP_SUFFIX = '_MAP'; | ||
var VL_DATASOURCE_SUFFIX = '_DATASOURCE'; | ||
function handleVL(value, key) { | ||
// 数组且只要数组元素都包含v、l两个key,就进行转换 | ||
if ( | ||
u.isArray(value) | ||
&& !u.contains(u.pluck(value, 'v'), undefined) | ||
&& !u.contains(u.pluck(value, 'l'), undefined) | ||
) { | ||
var vlMap = map[key + VL_MAP_SUFFIX] = {}; | ||
var vlDatasource = map[key + VL_DATASOURCE_SUFFIX] = []; | ||
u.each(value, function(item) { | ||
vlMap[item.v] = item.l; | ||
vlDatasource.push(u.mapKey(item, {v: 'value', l: 'text'})); | ||
}); | ||
} | ||
} | ||
var exports = { | ||
@@ -21,2 +39,3 @@ get: function (key) { | ||
map[key] = value; | ||
handleVL(value, key); | ||
}, | ||
@@ -28,9 +47,28 @@ | ||
getMap: function (key) { | ||
return map[key + VL_MAP_SUFFIX]; | ||
}, | ||
getDatasource: function (key) { | ||
return map[key + VL_DATASOURCE_SUFFIX]; | ||
}, | ||
init: function (constants) { | ||
// 先copy所有内容 | ||
u.extend(map, constants); | ||
// 处理`VL`类型常量 | ||
// 例如 [ { v: 'ACTIVE', l: '已启用' }, { v: 'INACTIVE', l: '未启用' } ] | ||
// 将生成如下几种形式的数据: | ||
// 1. 以v为key、以l为value的对象,命名为`[原始常量名]_MAP` | ||
// 如 { ACTIVE: '已启用', INACTIVE: '未启用' } | ||
// 2. 将v/l分别转换为value/text的数组,命名为`[原始常量名]_DATASOURCE` | ||
// 如 [ { value: 'ACTIVE', text: '已启用' }, { value: 'INACTIVE', text: '未启用' } ] | ||
u.each(map, handleVL); | ||
} | ||
}; | ||
// return模块 | ||
return exports; | ||
}); |
/** | ||
* @file [Please input file description] | ||
* @file 用户信息模块 | ||
* @author Justineo(justice360@gmail.com) | ||
@@ -7,8 +7,13 @@ */ | ||
define(function (require) { | ||
var u = require('underscore'); | ||
var permission = require('er/permission'); | ||
var URI = require('urijs'); | ||
var auth = require('./auth'); | ||
/** | ||
* [Please input module description] | ||
* 用户信息模块 | ||
*/ | ||
var exports = { | ||
init: function(session) { | ||
init: function (session) { | ||
if (session.visitor) { | ||
@@ -23,2 +28,50 @@ this.visitor = session.visitor; | ||
} | ||
// 如果配置了权限信息,需要初始化 `er/permission` | ||
var auth = this.visitor.auth; | ||
if (auth) { | ||
permission.add(u.mapObject(auth, function (value) { | ||
return value !== 'none'; | ||
})); | ||
} | ||
}, | ||
getVisitor: function () { | ||
return this.visitor || null; | ||
}, | ||
getVisitorId: function () { | ||
return this.visitor && this.visitor.id; | ||
}, | ||
getAder: function () { | ||
return this.ader || null; | ||
}, | ||
getAderId: function () { | ||
return this.ader && this.ader.id | ||
|| URI.parseQuery(document.location.search).aderId | ||
|| this.visitor && this.visitor.id; | ||
}, | ||
getAuthMap: function () { | ||
var authMap = this.visitor && this.visitor.auth; | ||
return authMap || null; | ||
}, | ||
getAuthType: function (authId) { | ||
return auth.get(authId, this.getAuthMap()); | ||
}, | ||
getAuth: function (authId) { | ||
var authType = this.getAuthType(authId); | ||
return { | ||
type: authType, | ||
id: authId, | ||
isReadOnly: authType === auth.AuthType.READONLY, | ||
isEditable: authType === auth.AuthType.EDITABLE, | ||
isVisible: authType !== auth.AuthType.NONE, | ||
isNone: authType === auth.AuthType.NONE | ||
}; | ||
} | ||
@@ -25,0 +78,0 @@ }; |
/** | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* @file tpl加载插件 | ||
* @author otakustay | ||
* @date $DATE$ | ||
*/ | ||
@@ -15,3 +12,2 @@ define( | ||
var controlModulePrefix = { | ||
// Sidebar不使用esui的,那个不大符合要求 | ||
BoxGroup: 'esui', | ||
@@ -26,2 +22,3 @@ Button: 'esui', | ||
Frame: 'esui', | ||
Image: './ui', | ||
Label: 'esui', | ||
@@ -38,2 +35,3 @@ Link: 'esui', | ||
Select: 'esui', | ||
Sidebar: 'esui', | ||
Tab: 'esui', | ||
@@ -46,6 +44,15 @@ Table: 'esui', | ||
Tree: 'esui', | ||
Uploader: './ui', | ||
Validity: 'esui', | ||
Wizard: 'esui', | ||
ActionPanel: 'ef', | ||
ActionDialog: 'ef' | ||
ActionDialog: 'ef', | ||
SelectorTreeStrategy: './ui', | ||
TreeRichSelector: './ui', | ||
TableRichSelector: './ui', | ||
RichSelector: './ui', | ||
ToggleButton: './ui', | ||
AuthPanel: './ui', | ||
SearchTree: './ui', | ||
RichBoxGroup: './ui' | ||
}; | ||
@@ -132,3 +139,2 @@ | ||
* | ||
* @class tpl | ||
* @singleton | ||
@@ -150,4 +156,4 @@ */ | ||
* @param {string} resourceId 模板资源id | ||
* @param {function} parentRequire 父级`require`函数 | ||
* @param {function} load 加载完成后调用 | ||
* @param {Function} parentRequire 父级`require`函数 | ||
* @param {Function} load 加载完成后调用 | ||
*/ | ||
@@ -162,12 +168,19 @@ load: function (resourceId, parentRequire, load) { | ||
require(dependencies, function () { load(text); }); | ||
require(dependencies, function() { | ||
load(text); | ||
}); | ||
} | ||
var options = { | ||
method: 'GET', | ||
url: parentRequire.toUrl(resourceId), | ||
cache: true, | ||
dataType: 'text' | ||
}; | ||
ajax.request(options).then(addTemplate); | ||
if (/\.html?$/.test(resourceId)) { | ||
var options = { | ||
method: 'GET', | ||
url: parentRequire.toUrl(resourceId), | ||
cache: true, | ||
dataType: 'text' | ||
}; | ||
ajax.request(options).then(addTemplate); | ||
} | ||
else { | ||
require([resourceId], addTemplate); | ||
} | ||
}, | ||
@@ -174,0 +187,0 @@ |
/** | ||
* ADM 2.0 | ||
* Copyright 2013 Baidu Inc. All rights reserved. | ||
* | ||
* @ignore | ||
* @file 让输入控件在特定事件下自动提交表单的扩展 | ||
* @author otakustay | ||
* @date $DATE$ | ||
*/ | ||
@@ -71,4 +66,4 @@ define( | ||
// 如果没指定表单,就沿DOM结构向上找一个表单控件 | ||
var element = this.target | ||
&& this.target.main | ||
var element = this.target | ||
&& this.target.main | ||
&& this.target.main.parentNode; | ||
@@ -128,3 +123,3 @@ while (element) { | ||
); | ||
Extension.prototype.inactivate.apply(this, arguments); | ||
@@ -131,0 +126,0 @@ }; |
@@ -49,3 +49,3 @@ /** | ||
var typeRule = /table-operation-(\w+)/; | ||
var typeRule = /table-operation-(\w+(?:-\w+)*)/; | ||
@@ -63,6 +63,7 @@ function getTipType(element) { | ||
TableTip.prototype.createAndAttachTip = function (elements, type) { | ||
var element = elements[0]; | ||
var options = { | ||
id: 'table-operation-tip-' + u.escape(type), | ||
viewContext: this.target.viewContext, | ||
content: lib.getText(elements[0]), | ||
content: element.getAttribute('data-title') || element.innerHTML, | ||
arrow: true, | ||
@@ -74,3 +75,3 @@ skin: 'table-tip' | ||
u.each( | ||
elements, | ||
elements, | ||
function (element) { | ||
@@ -82,4 +83,4 @@ var options = { | ||
positionOpt: { | ||
bottom: 'bottom', | ||
left: 'left' | ||
top: 'top', | ||
right: 'left' | ||
} | ||
@@ -100,3 +101,4 @@ }; | ||
var elements = document.querySelectorAll('.table-operation'); | ||
// 根据Table主元素来限定范围 | ||
var elements = this.target.main.querySelectorAll('.table-operation'); | ||
@@ -116,3 +118,3 @@ u.chain(elements) | ||
this.target.on('afterrender', this.initTips, this); | ||
this.target.on('bodyChange', this.initTips, this); | ||
}; | ||
@@ -126,3 +128,3 @@ | ||
TableTip.prototype.inactivate = function () { | ||
this.target.un('afterrender', this.initTips, this); | ||
this.target.un('bodyChange', this.initTips, this); | ||
@@ -137,2 +139,2 @@ Extension.prototype.inactivate.apply(this, arguments); | ||
} | ||
); | ||
); |
@@ -154,3 +154,3 @@ /** | ||
this.target.un('afterrender', checkLength, this); | ||
Extension.prototype.inactivate.apply(this, arguments); | ||
@@ -157,0 +157,0 @@ }; |
@@ -13,21 +13,26 @@ define(function (require) { | ||
// 不然会随着跳转被销毁,造成下次用不了 | ||
var Toast = require('./Toast'); | ||
var toastOptions = { | ||
var ui = require('esui'); | ||
require('esui/Dialog'); | ||
var main = document.createElement('div'); | ||
document.body.appendChild(main); | ||
var dialgOptions = { | ||
main: main, | ||
title: '系统提示', | ||
disposeOnHide: false, | ||
autoShow: false, | ||
mask: true, | ||
duration: Infinity, | ||
top: 180, | ||
width: 'auto', | ||
needFoot: false, | ||
skin: 'loading' | ||
}; | ||
globalLoading = new Toast(toastOptions); | ||
globalLoading.on( | ||
'hide', | ||
u.bind(globalLoading.detach, globalLoading) | ||
); | ||
globalLoading.render(); | ||
globalLoading = ui.create('Dialog', dialgOptions); | ||
globalLoading.show(); | ||
} | ||
var properties = { | ||
content: content || '正在读取数据,请稍候...', | ||
status: undefined | ||
content: content || '正在读取数据,请稍候...' | ||
}; | ||
@@ -37,3 +42,5 @@ properties = u.extend(properties, options); | ||
globalLoading.show(); | ||
globalLoading.main.style.zIndex = parseInt(globalLoading.main.style.zIndex, 10) + 1; | ||
loadingCount++; | ||
loadingTimer && clearTimeout(loadingTimer); | ||
return globalLoading; | ||
@@ -40,0 +47,0 @@ } |
507
src/util.js
/** | ||
* Copyright 2014 Baidu Inc. All rights reserved. | ||
* | ||
* | ||
* @ignore | ||
@@ -8,227 +8,354 @@ * @file BAT工具模块 | ||
*/ | ||
define( | ||
function (require) { | ||
var u = require('underscore'); | ||
var moment = require('moment'); | ||
var io = require('./io/serverIO'); | ||
var util = {}; | ||
/** | ||
* 根据URL字符串生成请求发送器 | ||
* | ||
* @param {string | Array.<string> | Object.<string, string>} url 请求路径或多个请求路径的集合 | ||
* @return {Function | Array.<Function> | Object.<string, Function>} 将对应的路径转换为发送器后返回 | ||
*/ | ||
util.genRequesters = function (url) { | ||
if (u.isString(url)) { | ||
// 只有一个URL,直接返回封装过的请求方法 | ||
return function (data) { | ||
return io.post(url, data); | ||
}; | ||
} | ||
else if (u.isObject(url)) { | ||
// 是一个集合,那么每个项目都封装一下 | ||
var map = u.clone(url); | ||
u.each(map, function (url, name) { | ||
if (u.isString(url)) { | ||
map[name] = function (data) { | ||
return io.post(url, data); | ||
}; | ||
} | ||
// 如果不是string那可能封装过了 | ||
}); | ||
return map; | ||
} | ||
}; | ||
define(function (require) { | ||
var u = require('underscore'); | ||
var moment = require('moment'); | ||
var io = require('./io/serverIO'); | ||
util.getTimeRange = function (begin, end, options) { | ||
/** | ||
* 工具模块 | ||
* | ||
* @singleton | ||
*/ | ||
var util = {}; | ||
// 只有一个参数时,认为是options | ||
if (arguments.length === 1) { | ||
options = begin; | ||
/** | ||
* 根据URL字符串生成请求发送器 | ||
* | ||
* 传入一个字符串时,只返回一个发送器函数;传入数组或对象时,递归;传入函数时 | ||
* | ||
* @param {string|Array.<string>|Object.<string, string>|function} url 请求路径或多个请求路径的集合,或是取值函数 | ||
* @param {function(string):boolean} isRequester 判断是否是需要生成请求发送器的路径 | ||
* @return {function|Array.<function>|Object.<string, function>} 将对应的路径转换为发送器后返回 | ||
*/ | ||
util.genRequesters = function (url, isRequester) { | ||
if (u.typeOf(url) === 'String') { | ||
// 只有一个URL,直接返回封装过的请求方法 | ||
// 过滤掉不需要生成的URL | ||
isRequester = isRequester || function (path) { | ||
// 默认跳过以`/download`和`/upload`结尾的路径 | ||
return !/\/(?:up|down)load$/.test(path); | ||
}; | ||
if (!isRequester(url)) { | ||
return url; | ||
} | ||
var defaults = { | ||
inputFormat: 'YYYYMMDDHHmmss', | ||
outputFormat: 'Date' | ||
return function (data, options) { | ||
return io.post(url, data, options); | ||
}; | ||
} | ||
else if (u.typeOf(url) === 'Object' || u.typeOf(url) === 'Array') { | ||
// 是一个集合,那么递归封装一下 | ||
var collection = u.clone(url); | ||
u.each(collection, function (url, key) { | ||
collection[key] = util.genRequesters(url); | ||
}); | ||
return collection; | ||
} | ||
else if (u.typeOf(url) === 'Function') { | ||
// 是一个函数,不用封装 | ||
return url; | ||
} | ||
}; | ||
options = u.defaults({}, options, defaults); | ||
/** | ||
* 生成时间段数据 | ||
* | ||
* 未指定`begin`及`end`时默认取最近7天 | ||
* | ||
* 有两种重载: | ||
* 1. getTimeRange(options) | ||
* 2. getTimeRange(begin, end, options) | ||
* | ||
* @param {Date|string} [begin] 开始日期 | ||
* @param {Date|string} [end] 结束日期 | ||
* @param {Object} [options] 生成选项 | ||
* @param {string} [options.inputFormat] 输入参数的格式,为`'Date'`时会当作`Date`对象,否则为格式字符串 | ||
* @param {string} [options.outputFormat] 输出参数的格式,参见`inputFormat` | ||
* @param {string} [options.beginKey] 结果对象开始时间的键名 | ||
* @param {string} [options.endKey] 结果对象结束时间的键名 | ||
* @return {Object} 时间段的数据,格式由`options`参数决定 | ||
*/ | ||
util.getTimeRange = function (begin, end, options) { | ||
// 解析输入,没有则使用默认时间 | ||
if (begin && end) { | ||
begin = u.isString(begin) | ||
? moment(begin, options.inputFormat) | ||
: moment(begin); | ||
end = u.isString(end) | ||
? moment(end, options.inputFormat) | ||
: moment(end); | ||
} | ||
else { | ||
var now = moment().startOf('day'); | ||
// 只有一个参数时,认为是options | ||
if (arguments.length === 1) { | ||
options = begin; | ||
} | ||
// 默认前七天 | ||
begin = now.clone().subtract('days', 7); | ||
end = now.clone().subtract('day', 1).endOf('day'); | ||
} | ||
var defaults = { | ||
inputFormat: 'YYYYMMDDHHmmss', | ||
outputFormat: 'Date' | ||
}; | ||
// 处理输出 | ||
if (options.outputFormat.toLowerCase() === 'date') { | ||
begin = begin.toDate(); | ||
end = end.toDate(); | ||
} | ||
else { | ||
begin = begin.format(options.outputFormat); | ||
end = end.format(options.outputFormat); | ||
} | ||
options = u.defaults({}, options, defaults); | ||
var keys = { | ||
begin: options.beginKey || 'begin', | ||
end: options.endKey || 'end' | ||
}; | ||
// 解析输入,没有则使用默认时间 | ||
if (begin && end) { | ||
begin = u.isString(begin) | ||
? moment(begin, options.inputFormat) | ||
: moment(begin); | ||
end = u.isString(end) | ||
? moment(end, options.inputFormat) | ||
: moment(end); | ||
} | ||
else { | ||
var now = moment().startOf('day'); | ||
return u.mapKey( | ||
{ | ||
begin: begin, | ||
end: end | ||
}, | ||
{ | ||
begin: keys.begin, | ||
end: keys.end | ||
} | ||
); | ||
// 默认前七天 | ||
begin = now.clone().subtract('days', 7); | ||
end = now.clone().subtract('day', 1).endOf('day'); | ||
} | ||
// 处理输出 | ||
if (options.outputFormat.toLowerCase() === 'date') { | ||
begin = begin.toDate(); | ||
end = end.toDate(); | ||
} | ||
else { | ||
begin = begin.format(options.outputFormat); | ||
end = end.format(options.outputFormat); | ||
} | ||
var keys = { | ||
begin: options.beginKey || 'begin', | ||
end: options.endKey || 'end' | ||
}; | ||
util.toMap = function (list, key, opt_converter) { | ||
var i, item, k, | ||
map = {}, | ||
converter = opt_converter; | ||
return u.mapKey( | ||
{ | ||
begin: begin, | ||
end: end | ||
}, | ||
{ | ||
begin: keys.begin, | ||
end: keys.end | ||
} | ||
); | ||
}; | ||
for (i = list.length; i--;) { | ||
item = list[i]; | ||
k = item[key]; | ||
if (k != null) { | ||
if (u.isFunction(converter)) { | ||
var keyValue = converter(item); | ||
map[keyValue.key] = keyValue.value; | ||
} else if (u.isString(converter)) { | ||
map[k] = item[converter]; | ||
} else { | ||
map[k] = item; | ||
} | ||
/** | ||
* 将同构对象的数组转换为按对象中某个键值为键名的对象 | ||
* | ||
* 可以定义转换器,来转换输出对象的内容 | ||
* `converter`为函数时,接受的参数为输入数组的元素,返回值必须为`{ key: ..., value: ... }`格式,即结果对象的键值对 | ||
* `converter`为字符串时,结果对象键值为数组元素对应键名的键值 | ||
* `converter`缺失时,结果对象键值为数组元素 | ||
* | ||
* 例如: | ||
* `list`为`[ { id: 1, name: 'Thor' }, { id: 2, name: 'Hulk' } ], | ||
* `key`为`id`时, | ||
* | ||
* util.toMap(list, key, function(item) { | ||
* return { | ||
* key: '#' + item.id, | ||
* value: item.name.toUpperCase() | ||
* }; | ||
* }) → { '#1': 'THOR', '#2': 'HULK' } | ||
* | ||
* util.toMap(list, key, 'name') → { '1': 'Thor', '2': 'Hulk' } | ||
* | ||
* util.toMap(list, key) → { '1': { id: 1, name: 'Thor' }, '2': { id: 2, name: 'Hulk' } } | ||
* | ||
* @param {Array.<Object>} list 同构对象的数组 | ||
* @param {string} key 取对应键值为结果对象的键名 | ||
* @param {Function|string} [converter] 转换器 | ||
* @return {Object} 转换完毕的对象 | ||
*/ | ||
util.toMap = function (list, key, converter) { | ||
var i; | ||
var item; | ||
var k; | ||
var map = {}; | ||
for (i = list.length; i--;) { | ||
item = list[i]; | ||
k = item[key]; | ||
if (k != null) { | ||
if (u.isFunction(converter)) { | ||
var keyValue = converter(item); | ||
map[keyValue.key] = keyValue.value; | ||
} | ||
else if (u.isString(converter)) { | ||
map[k] = item[converter]; | ||
} | ||
else { | ||
map[k] = item; | ||
} | ||
} | ||
return map; | ||
} | ||
return map; | ||
}; | ||
/** | ||
* 根据生成在列表页中操作列中的链接HTML | ||
* | ||
* @param {Object} link 链接配置 | ||
* @param {string} [link.className="list-operation"] 链接的className | ||
* @param {string} link.url 链接的目标URL | ||
* @param {string} [link.disabled] 链接是否禁用 | ||
* @param {string} [link.target] 链接的target属性 | ||
* @param {string} link.text 链接文本 | ||
* @param {Object} [link.extra] 附加属性对象,对应kv对会以data-key="value"形式附加到HTML上 | ||
* @return {string} 生成的HTML内容 | ||
*/ | ||
util.genListLink = function (link) { | ||
var defaults = { | ||
className: 'list-operation' | ||
}; | ||
util.genListLink = function (link) { | ||
var defaults = { | ||
className: 'list-operation' | ||
}; | ||
link = u.defaults(link, defaults); | ||
var attrs = { | ||
href: link.url, | ||
'class': link.className | ||
}; | ||
if (link.target && link.target.toLowerCase() !== '_self') { | ||
attrs.target = link.target; | ||
} | ||
link = u.defaults(link, defaults); | ||
attrs = u.map(attrs, function (val, key) { | ||
return key + '="' + u.escape(val) + '"'; | ||
}); | ||
if (link.disabled) { | ||
return '<span class="' + u.escape(link.className) + ' auth-disabled">' | ||
+ u.escape(link.text) + '</span>'; | ||
} | ||
return '<a ' + attrs.join(' ') + '>' | ||
+ u.escape(link.text) + '</a>'; | ||
var attrs = { | ||
href: link.url, | ||
'class': link.className | ||
}; | ||
util.genListCommand = function (command) { | ||
var defaults = { | ||
tagName: 'span', | ||
className: 'list-operation' | ||
}; | ||
command = u.defaults(command, defaults); | ||
var attrs = { | ||
'class': command.className, | ||
'data-command': command.type | ||
}; | ||
if (link.target && link.target.toLowerCase() !== '_self') { | ||
attrs.target = link.target; | ||
} | ||
if (command.args != null) { | ||
attrs['data-command-args'] = command.args; | ||
} | ||
attrs = u.map(attrs, function (val, key) { | ||
return key + '="' + u.escape(val) + '"'; | ||
if (u.typeOf(link.extra) === 'Object') { | ||
u.each(link.extra, function (val, key) { | ||
attrs['data-' + key] = val; | ||
}); | ||
} | ||
var tagName = u.escape(command.tagName); | ||
return '<' + tagName + ' ' + attrs.join(' ') + '>' | ||
attrs = u.map(attrs, function (val, key) { | ||
return key + '="' + u.escape(val) + '"'; | ||
}); | ||
return '<a ' + attrs.join(' ') + '>' | ||
+ u.escape(link.text) + '</a>'; | ||
}; | ||
/** | ||
* 根据生成在列表页中操作列中的操作按钮HTML | ||
* | ||
* @param {Object} command 操作配置 | ||
* @param {string} [command.className="list-operation"] 操作按钮的className | ||
* @param {string} [command.tagName="span"] 操作按钮的HTML元素类型 | ||
* @param {string} command.type 操作按钮点击时触发的事件类型 | ||
* @param {string} [command.disabled] 操作按钮是否禁用 | ||
* @param {string} [command.args] 操作按钮点击后触发事件所带的参数 | ||
* @param {string} command.text 操作按钮显示的文本 | ||
* @param {Object} [command.extra] 附加属性对象,对应kv对会以data-key="value"形式附加到HTML上 | ||
* @return {string} 生成的HTML内容 | ||
*/ | ||
util.genListCommand = function (command) { | ||
var defaults = { | ||
tagName: 'span', | ||
className: 'list-operation' | ||
}; | ||
command = u.defaults(command, defaults); | ||
var tagName = u.escape(command.tagName); | ||
if (command.disabled) { | ||
return '<' + tagName + ' class="' + u.escape(command.className) + ' auth-disabled">' | ||
+ u.escape(command.text) + '</' + tagName + '>'; | ||
} | ||
var attrs = { | ||
'class': command.className, | ||
'data-command': command.type | ||
}; | ||
util.genListOperations = function (operations, config) { | ||
config = config || {}; | ||
var html = u.map( | ||
operations, | ||
function (operation) { | ||
if (operation.url) { | ||
return util.genListLink(operation); | ||
} | ||
else { | ||
return util.genListCommand(operation); | ||
} | ||
if (typeof command.args !== 'undefined') { | ||
attrs['data-command-args'] = '' + command.args; | ||
} | ||
if (u.typeOf(command.extra) === 'Object') { | ||
u.each(command.extra, function (val, key) { | ||
attrs['data-' + key] = val; | ||
}); | ||
} | ||
attrs = u.map(attrs, function (val, key) { | ||
return key + '="' + u.escape(val) + '"'; | ||
}); | ||
return '<' + tagName + ' ' + attrs.join(' ') + '>' | ||
+ u.escape(command.text) + '</' + tagName + '>'; | ||
}; | ||
/** | ||
* 生成列表页操作/链接列表的HTML | ||
* | ||
* @param {Array.<Object>} operations 操作/链接配置的数组 | ||
* @param {Object} config 列表配置 | ||
* @param {string} [config.separator="<span class=\"list-operation-separator\">|</span>"] 列表分隔符 | ||
* @return {string} 生成的HTML内容 | ||
*/ | ||
util.genListOperations = function (operations, config) { | ||
config = config || {}; | ||
var html = u.chain(operations) | ||
.map(function (operation) { | ||
if (operation.url) { | ||
return util.genListLink(operation); | ||
} | ||
); | ||
else { | ||
return util.genListCommand(operation); | ||
} | ||
}) | ||
.compact() | ||
.value(); | ||
return html.join(config.separator || '<span class="list-operation-separator">|</span>'); | ||
}; | ||
return html.join(config.separator || ''); | ||
}; | ||
/** | ||
* 下载文件 | ||
* @param {string} url 文件地址. | ||
*/ | ||
util.download = function (url) { | ||
var divId = '__DownloadContainer__'; | ||
var formId = '__DownloadForm__'; | ||
var iframeId = '__DownloadIframe__'; | ||
var tpl = [ | ||
'<form action="${url}" method="post" id="${formId}" ', | ||
'name="${formId}" target="${iframeId}"></form>', | ||
'<iframe src="about:blank" id="${iframeId}" name="${iframeId}">', | ||
'</iframe>' | ||
].join(''); | ||
/** | ||
* 下载文件 | ||
* @param {string} url 文件地址. | ||
*/ | ||
util.download = function (url) { | ||
var divId = '__DownloadContainer__'; | ||
var formId = '__DownloadForm__'; | ||
var iframeId = '__DownloadIframe__'; | ||
/* eslint-disable fecs-indent */ | ||
var tpl = [ | ||
'<form action="${url}" method="post" id="${formId}" ', | ||
'name="${formId}" target="${iframeId}"></form>', | ||
'<iframe src="about:blank" id="${iframeId}" name="${iframeId}">', | ||
'</iframe>' | ||
].join(''); | ||
/* eslint-enable fecs-indent */ | ||
function getUrlWithAderId() { | ||
var URI = require('urijs'); | ||
var user = require('./system/user'); | ||
var aderId = user.ader && user.ader.id | ||
|| URI.parseQuery(document.location.search).aderId; | ||
var query = aderId ? { aderId: aderId } : {}; | ||
return URI(url).addQuery(query).toString(); | ||
} | ||
function getUrlWithAderId() { | ||
var uri = require('urijs'); | ||
var user = require('./system/user'); | ||
var aderId = user.ader && user.ader.id | ||
|| uri.parseQuery(document.location.search).aderId; | ||
var query = aderId ? {aderId: aderId} : {}; | ||
return uri(url).addQuery(query).toString(); | ||
} | ||
function getDownloadContainer() { | ||
var div = document.getElementById(divId); | ||
if (!div) { | ||
div = document.createElement('div'); | ||
div.id = divId; | ||
div.style.display = 'none'; | ||
document.body.appendChild(div); | ||
} | ||
return div; | ||
function getDownloadContainer() { | ||
var div = document.getElementById(divId); | ||
if (!div) { | ||
div = document.createElement('div'); | ||
div.id = divId; | ||
div.style.display = 'none'; | ||
document.body.appendChild(div); | ||
} | ||
return div; | ||
} | ||
var ctner = getDownloadContainer(); | ||
var render = require('etpl').compile(tpl); | ||
ctner.innerHTML = render({ | ||
url: getUrlWithAderId(url), | ||
formId: formId, | ||
iframeId: iframeId | ||
}); | ||
document.getElementById(formId).submit(); | ||
}; | ||
var ctner = getDownloadContainer(); | ||
var render = require('etpl').compile(tpl); | ||
ctner.innerHTML = render({ | ||
url: getUrlWithAderId(url), | ||
formId: formId, | ||
iframeId: iframeId | ||
}); | ||
document.getElementById(formId).submit(); | ||
}; | ||
return util; | ||
} | ||
); | ||
return util; | ||
}); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
448098
0
84
9594
1
3
2
- Removedef@3.1.x
- Removeder@3.1.x
- Removedesui@>=3.1.0-alpha.5
- Removedetpl@>=2.0.8
- Removedmoment@2.x
- Removedunderscore@1.4.x
- Removedurijs@>=1.12.0
- Removedetpl@3.2.0(transitive)
- Removedmoment@2.30.1(transitive)
- Removedunderscore@1.4.4(transitive)
- Removedurijs@1.19.11(transitive)