This commit is contained in:
zuiidea
2016-09-08 21:49:18 +08:00
parent 9838869517
commit 9077b5ff30
54 changed files with 3499 additions and 258 deletions
+168
View File
@@ -0,0 +1,168 @@
import globalConfig from '../config';
/**
* 日志工具类 <br/>
*
* <p>
* 工程变大之后, 日志就很重要了, 总不能一直console.log.
* 本来想看看有没有现成的工具, 找到一个log4js的库, 但只能用在node环境下, 浏览器环境下似乎没什么好用的.
* 作为一个用惯了slf4j的人, 干脆自己写个吧, 练练手.
* 目前有以下功能:
* <ul>
* <li>支持常用的日志级别: debug/info/warn/error, 说实话其他的级别极少用到</li>
* <li>支持变量替换: 类似slf4j中的logger.info("a={}",a)这种, 其实console本身已经支持的</li>
* <li>根据日志级别设置样式: debug是黑色, info是默认, warn是黄色, error是红色, 看起来清晰很多</li>
* <li>定义每个logger的名字: 也算是常规功能了吧</li>
* </ul>
* 我是尽量按着slf4j的习惯来设计的, 目前还比较简单.
* 不支持pattern/appender之类的, 但对于二手前端来说, 也算够用了.
* </p>
*/
class Logger {
// 定义一些预设的日志级别
// 目前只有4种级别
static LOG_LEVEL_DEBUG = 1;
static LOG_LEVEL_INFO = 2;
static LOG_LEVEL_WARN = 3;
static LOG_LEVEL_ERROR = 4;
/*暂存所有logger*/
static loggerMap = new Map();
/*默认的logger*/
static defaultLogger = new Logger();
/**
* 获取一个Logger实例
*
* @param name
* @returns {*}
*/
static getLogger(name) {
if (name && name !== '') {
// 缓存
if (Logger.loggerMap.has(name)) {
return Logger.loggerMap.get(name);
}
const logger = new Logger(name);
Logger.loggerMap.set(name, logger);
return logger;
} else {
return Logger.defaultLogger;
}
}
constructor(name) {
this.name = name; // logger的名字
const configLogLevel = globalConfig.logLevel;
if (configLogLevel === 'debug') {
this.logLevel = Logger.LOG_LEVEL_DEBUG;
} else if (configLogLevel === 'info') {
this.logLevel = Logger.LOG_LEVEL_INFO;
} else if (configLogLevel === 'warn') {
this.logLevel = Logger.LOG_LEVEL_WARN;
} else if (configLogLevel === 'error') {
this.logLevel = Logger.LOG_LEVEL_ERROR;
} else {
// 默认都是info级别
this.error('unsupported logLevel: %s, use INFO instead', configLogLevel);
this.logLevel = Logger.LOG_LEVEL_INFO;
}
}
/**
* 设置日志级别, 只有4种级别可选
*
* @param newLogLevel 1~4之间的一个数字
*/
setLogLevel(newLogLevel) {
if (isNaN(newLogLevel)) {
this.error('setLogLevel error, not a number: %s', newLogLevel);
}
if (newLogLevel < 1 || newLogLevel > 4) {
this.error('setLogLevel error, input = %s, must between 1 and 4', newLogLevel);
}
this.logLevel = newLogLevel;
}
/**
* 打印info日志
*
* @param pattern 日志格式, 支持%d/%s等占位符
* @param args 可变参数, 用于替换pattern中的占位符
*/
info(pattern, ...args) {
// 先判断日志级别
if (this.logLevel > Logger.LOG_LEVEL_INFO)
return;
if (this.name)
args.unshift(`${this.name}: ${pattern}`);
else
args.unshift(pattern);
console.log.apply(console, args);
}
/**
* 打印error日志
*
* @param pattern
* @param args
*/
error(pattern, ...args) {
if (this.logLevel > Logger.LOG_LEVEL_ERROR)
return;
args.unshift('background: red; color: #bada55;');
if (this.name)
args.unshift(`%c${this.name}: ${pattern}`);
else
args.unshift(`%c${pattern}`);
console.error.apply(console, args);
}
/**
* 打印debug日志
*
* @param pattern
* @param args
*/
debug(pattern, ...args) {
if (this.logLevel > Logger.LOG_LEVEL_DEBUG)
return;
args.unshift('background: black; color: #bada55;');
if (this.name)
args.unshift(`%c${this.name}: ${pattern}`);
else
args.unshift(`%c${pattern}`);
console.debug.apply(console, args);
}
/**
* 打印warn日志
*
* @param pattern
* @param args
*/
warn(pattern, ...args) {
if (this.logLevel > Logger.LOG_LEVEL_WARN)
return;
args.unshift('background: yellow; color: black;');
if (this.name)
args.unshift(`%c${this.name}: ${pattern}`);
else
args.unshift(`%c${pattern}`);
console.warn.apply(console, args);
}
}
export default Logger;
+227
View File
@@ -0,0 +1,227 @@
/**
* 对superagent api的一个mock, 用于debug
*/
import Logger from './Logger';
import superagent from 'superagent';
import globalConfig from '../config';
// superagent的文档: http://visionmedia.github.io/superagent/#error-handling
// 常用的api形式: 只要mock这两种就可以了
// ajax.get(url).end(func(err,res))
// ajax.post(url).send(obj).end(func(err,res))
const logger = Logger.getLogger('mockAjax');
let currentUrl; // 一个临时变量, 当前正在请求的url
const error = {}; // 理论上服务端不会返回error, 都是HTTP200
const result = { // 暂存mock的ajax返回, 总共有5个字段
success: true,
code: 0,
message: 'just a mock ;) ',
total: 10000,
data: {},
};
const successResult = (data) => {
result.success = true;
result.data = data;
result.code = 0;
result.message = '';
result.total = 10000;
};
const errorResult = (code, message) => {
result.success = false;
result.data = {};
result.code = code;
result.message = message;
result.total = 0;
};
// 从url中取出表名
const getTableNameFromUrl = (url) => {
// url的格式是apiPath/{tableName}/[insert|update|delete]
const apiPath = globalConfig.getAPIPath();
let begin = apiPath.length + 1;
while (url.charAt(begin) === '/') {
begin++;
}
let end = begin;
while (url.charAt(end) !== '/') {
end++;
}
const tableName = url.substring(begin, end);
logger.debug('getTableNameFromUrl: begin=%d, end=%d, tableName=%s', begin, end, tableName);
return tableName;
};
// 根据某个表的dataSchema创造mock数据
const mockResult = (tableName, queryObj) => {
logger.debug('begin to mock data for table %s', tableName);
// 尝试加载schema文件
let schema;
try {
schema = require(`../schema/${tableName}.dataSchema.js`);
} catch (e) {
logger.error('can not find dataSchema file for table %s', tableName);
logger.error('%o', e); // 这个方式有点不爽...但是如果不设置pattern, 就不能用样式了, 纠结...
// 设置返回结果为失败
errorResult(100, `can not find dataSchema file for table ${tableName}`);
return;
}
// 一般来说, 传入的查询条件都是肯定会有page/pageSize的, 以防万一
if (!queryObj.page) {
queryObj.page = 1;
}
if (!queryObj.pageSize) {
queryObj.pageSize = 50;
}
const tmp = [];
for (let i = 0; i < queryObj.pageSize; i++) {
const record = {};
// 为了让mock的数据有些区别, 把page算进去
schema.forEach((column)=> {
if (column.dataType === 'int') {
record[column.key] = 10000 * queryObj.page + i;
} else if (column.dataType === 'float') {
record[column.key] = 1.0 * queryObj.page + i * 0.01;
} else if (column.dataType === 'varchar') {
record[column.key] = `page=${queryObj.page} num=${i}`;
} else if (column.dataType === 'datetime') {
record[column.key] = new Date().plusDays(i).format('yyyy-MM-dd HH:mm:ss');
} else {
logger.error('unsupported dataType %s', column.dataType);
}
});
tmp.push(record);
}
successResult(tmp);
};
// 开始mock supergent的各个方法
const end = (func) => {
// 模拟一个延时
// 总是感觉setTimeout有点low啊, 不知道有没有更好的办法
setTimeout(func, 1500, null, {body: result}); // 延时执行func函数, 参数跟在最后
};
const get = (url) => {
logger.debug('get url %s', url);
currentUrl = url;
// TODO: 要根据url返回不同的数据, 这里设置各种get的url
if (url === `${globalConfig.getAPIPath()}${globalConfig.login.getCurrentUser}`)
successResult('guest'); // debug模式下都是guest用户
else
errorResult(200, `unsupported url ${url}`);
return {end};
};
const type = (str) => {
logger.debug('set type = %s', str);
return {send};
};
const send = (obj) => {
logger.debug('send obj: %o', obj);
// TODO: 要根据url返回不同的数据, 这里设置各种post的url
if (currentUrl === `${globalConfig.getAPIPath()}${globalConfig.login.validate}`) {
// 验证登录的用户名和密码
if (obj.username === 'guest' && obj.password === 'guest') {
successResult('guest');
} else {
errorResult(300, 'error username or password');
}
} else {
// 如果不是登录请求, 目前只能是CRUD请求, 当然以后可能会扩展
const tableName = getTableNameFromUrl(currentUrl);
if (tableName) {
// 要根据增删改查的情况分别模拟
if (currentUrl.indexOf('select') > 0) {
mockResult(tableName, obj);
} else if (currentUrl.indexOf('insert') > 0) {
mockResult(tableName, obj);
const tmp = result.data[0];
successResult(tmp);
} else if (currentUrl.indexOf('update') > 0) {
const tmp = currentUrl.indexOf('keys=');
successResult(currentUrl.substring(tmp).split(',').length);
} else {
errorResult(400, `unsupported url ${currentUrl}`);
}
} else {
errorResult(400, `unsupported url ${currentUrl}`);
}
}
return {end};
};
const post = (url) => {
logger.debug('post url %s', url);
currentUrl = url;
// TODO: 要根据url返回不同的数据, 这里设置各种post的url
if (currentUrl.indexOf('delete') > 0) {
const tmp = currentUrl.indexOf('keys=');
successResult(currentUrl.substring(tmp).split(',').length);
}
return {send, type, end};
};
// 一个辅助的方法, superagent判断返回是否成功太麻烦了啊...要判断3个条件
const isSuccess = (res) => {
if (res && res.body && res.body.success) {
return true;
} else {
return false;
}
};
// mock的版本只提供简单的API, 以后可能要扩展
const mockAjax = {get, post, isSuccess};
// 对superagent的API简单包装下
const realAjax = {
get: function (url) {
return this.innerConfig(superagent.get(url));
},
post: function (url) {
return this.innerConfig(superagent.post(url));
},
// 一些全局的配置
innerConfig: function (req) {
let tmp = req;
// 是否是跨域请求
if (globalConfig.isCrossDomain()) {
tmp = tmp.withCredentials();
}
// 设置全局的超时时间
if (globalConfig.api.timeout && !isNaN(globalConfig.api.timeout)) {
tmp = tmp.timeout(globalConfig.api.timeout);
}
return tmp;
},
isSuccess,
};
if (globalConfig.debug === true) {
module.exports = mockAjax;
} else {
module.exports = realAjax;
}
+56
View File
@@ -0,0 +1,56 @@
// 一些辅助用的工具方法
// 很多都是gross hack, 属于历史遗留问题
/** 对Date的扩展,将 Date 转化为指定格式的String * 月(M)、日(d)、12小时(h)、24小时(H)、分(m)、秒(s)、周(E)、季度(q)
* 可以用 1-2 个占位符 * 年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字)
* eg:
* (new Date()).pattern("yyyy-MM-dd hh:mm:ss.S")==> 2006-07-02 08:09:04.423
* (new Date()).pattern("yyyy-MM-dd E HH:mm:ss") ==> 2009-03-10 二 20:09:04
* (new Date()).pattern("yyyy-MM-dd EE hh:mm:ss") ==> 2009-03-10 周二 08:09:04
* (new Date()).pattern("yyyy-MM-dd EEE hh:mm:ss") ==> 2009-03-10 星期二 08:09:04
* (new Date()).pattern("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18
*/
Date.prototype.format = function (fmt) {
var o = {
"M+": this.getMonth() + 1, //月份
"d+": this.getDate(), //日
"h+": this.getHours() % 12 == 0 ? 12 : this.getHours() % 12, //小时
"H+": this.getHours(), //小时
"m+": this.getMinutes(), //分
"s+": this.getSeconds(), //秒
"q+": Math.floor((this.getMonth() + 3) / 3), //季度
"S": this.getMilliseconds() //毫秒
};
var week = {
"0": "/u65e5",
"1": "/u4e00",
"2": "/u4e8c",
"3": "/u4e09",
"4": "/u56db",
"5": "/u4e94",
"6": "/u516d"
};
if (/(y+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
}
if (/(E+)/.test(fmt)) {
fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? "/u661f/u671f" : "/u5468") : "") + week[this.getDay() + ""]);
}
for (var k in o) {
if (new RegExp("(" + k + ")").test(fmt)) {
fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length)));
}
}
return fmt;
};
/**
* 在当前日期的基础上再增加几天
*
* @param num 要增加的天数
*/
Date.prototype.plusDays = function (num) {
var tmp = new Date();
tmp.setDate(this.getDate() + num);
return tmp;
};