Authored by 邱骏

Merge branch 'feature/activity-image-upload' of git.yoho.cn:fe/yoho-activity-pla…

…tform into feature/activity-image-upload
... ... @@ -140,6 +140,67 @@ const userController = {
res.end(exportFile, 'binary');
})
.catch(next);
},
userLoginLog(req, res) {
res.render('user/login-log', {
bodyClass: 'nav-md',
module: 'admin',
page: 'user-login-log'
});
},
exportUserLoginLog(req, res, next) {
const url = req.query.url;
const startTime = req.query.startTime;
const endTime = req.query.endTime;
let conf = {
name: 'mysheet',
cols: [
{
caption: '用户id',
type: 'string'
},
{
caption: '登录时间',
type: 'string'
},
{
caption: '平台',
type: 'string'
},
{
caption: '地址',
type: 'string'
},
{
caption: 'ip',
type: 'string'
}
],
rows: []
};
req.ctx(UserModel).exportUserLoginLog({url, startTime, endTime})
.then(result => {
let temp = [];
_.each(result, item => {
temp = [];
temp.push(item.user_id);
temp.push(moment(item.login_time).format('YYYY-MM-DD HH:mm:ss'));
temp.push(item.platform);
temp.push(item.referer);
temp.push(item.ip);
conf.rows.push(temp);
});
let exportFile = excelExport.execute(conf);
res.setHeader('Content-Type', 'application/vnd.openxmlformats');
res.setHeader('Content-Disposition', 'attachment; filename=user-login-log.xlsx');
res.end(exportFile, 'binary');
})
.catch(next);
}
};
... ...
... ... @@ -8,6 +8,7 @@ const mysqlCli = global.yoho.utils.mysqlCli;
const _ = require('lodash');
const TABLE_USER = 'user';
const TABLE_USER_LOGIN_LOG = 'act_login_user_log';
class UserModel extends global.yoho.BaseModel {
constructor(ctx) {
... ... @@ -64,6 +65,26 @@ class UserModel extends global.yoho.BaseModel {
);
}
/**
* 导出用户登录日志
* @returns {*}
*/
exportUserLoginLog({url, startTime, endTime}) {
let sql = `select user_id,login_time,platform,ip,referer from ${TABLE_USER_LOGIN_LOG} where 1=1`;
if (url) {
sql += ' and referer like :url';
}
if (startTime) {
sql += ' and login_time >= :startTime';
}
if (endTime) {
sql += ' and login_time <= :endTime';
}
return mysqlCli.query(sql, {url: `%${url}%`, startTime, endTime});
}
}
module.exports = UserModel;
... ...
... ... @@ -21,6 +21,10 @@ router.get('/activity/createArticle', activity.createArticlePage);
// 用户管理[page]
router.get('/user/list', user.userListPage);
// 用户登录日志[page]
router.get('/user/login-log', user.userLoginLog);
router.get('/user/exportUserLoginLog', user.exportUserLoginLog);
// 管理员[ajax]
router.post('/api/login', admin.login);
... ...
<!-- page content -->
<div class="right_col" role="main">
<div class="">
<div class="row">
<div class="col-md-12 col-sm-12 col-xs-12">
<div class="x_panel">
<div class="x_title">
<form class="form-horizontal">
<div class="form-group">
<input type="text" id="url" name="url" placeholder="输入查询的url" class="form-control" style="width:350px;">
</div>
<div class="form-group">
<input type="text" id="time" name="time"
class="form-control"
placeholder="开始时间 - 结束时间" style="width:350px;"/>
</div>
<div class="form-group">
<button type="button" class="btn btn-primary btn-export">导出</button>
</div>
<div class="clearfix"></div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- /page content -->
... ...
... ... @@ -13,18 +13,6 @@ _.mergeWith(baseConfig, {
output: {
publicPath: devInfo.publicPath
},
module: {
rules: [{
enforce: 'pre',
test: /(\.js|\.vue)$/,
exclude: /node_modules/,
loader: 'eslint-loader',
options: {
cache: true,
formatter: require('eslint/lib/formatters/codeframe')
}
}]
},
devServer: {
host: devInfo.host,
port: devInfo.port,
... ...
... ... @@ -56,6 +56,7 @@
<li><a><i class="fa fa-users"></i> 用户管理 <span class="fa fa-chevron-down"></span></a>
<ul class="nav child_menu">
<li><a href="/admin/user/list">用户列表</a></li>
<li><a href="/admin/user/login-log">用户登录日志</a></li>
</ul>
</li>
</ul>
... ...
... ... @@ -50,6 +50,7 @@
"fast-safe-stringify": "^1.2.0",
"feed": "^1.1.0",
"geetest": "^4.1.2",
"jsonp": "^0.2.1",
"lodash": "^4.17.4",
"mOxie": "^1.5.5",
"memory-cache": "^0.1.6",
... ...
import qs from 'yoho-qs';
import cookie from 'yoho-cookie';
import jsonp from 'jsonp';
import 'whatwg-fetch';
const uid = qs.yh_uid;
... ... @@ -10,22 +12,38 @@ if (process.env.NODE_ENV === 'production') {
baseUrl = 'http://localhost:6006';
}
const loginLog = userId => {
if (!cookie.get('_loginLog')) {
fetch(`${baseUrl}/user/loginLog`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
uid: userId
})
}).then(res => {
if (res.ok) {
cookie.set('_loginLog', true);
}
});
}
};
if (!uid) {
fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => {
if (res.ok) {
res.json().then(data => {
window.location.href = data.loginUrl;
jsonp('http://m.yohobuy.com/passport/login/user', {}, (errMsg, userRes) => {
if (userRes.code !== 200) {
fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => {
if (res.ok) {
res.json().then(data => {
window.location.href = data.loginUrl;
});
}
});
} else {
loginLog(userRes.data);
}
});
} else {
fetch(`${baseUrl}/user/loginLog`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
uid
})
});
loginLog(uid);
}
... ...
... ... @@ -185,7 +185,7 @@ function bind_qiniu_upload() {
// why? reference: https://github.com/qiniu/js-sdk/issues/266
Promise.all([
require('moxie/bin/js/moxie'),
require('mOxie/bin/js/moxie'),
require('plupload/js/plupload.dev'),
require('qiniu-js')
]).then(([moxie, plupload]) => {
... ...
require('admin/user-login-log.page.css');
require('bootstrap-daterangepicker');
require('bootpag/lib/jquery.bootpag.min');
const moment = require('moment');
function bind_date_picker() {
if (typeof ($.fn.daterangepicker) === 'undefined') {
return;
}
$('#time').daterangepicker({
timePicker: true,
timePickerIncrement: 30,
startDate: moment().add(-30, 'day'),
endDate: moment(),
locale: {
format: 'YYYY-MM-DD HH:mm:ss'
}
});
}
function bind_button_event() {
$('.btn-export').click(() => {
const url = $('#url').val();
const time = $('#time').val();
const timeArray = time.split(' - ');
let startTime, endTime;
if (timeArray.length === 2) {
startTime = moment(timeArray[0]).valueOf();
endTime = moment(timeArray[1]).valueOf();
}
window.open(`/admin/user/exportUserLoginLog?startTime=${startTime}&endTime=${endTime}&url=${url}`, '_blank');
});
}
(() => {
bind_date_picker();
bind_button_event();
})();
\ No newline at end of file
... ...
@import '~bootstrap-daterangepicker';
\ No newline at end of file
... ...
... ... @@ -2055,7 +2055,7 @@ debug@0.7.4, debug@~0.7.4:
version "0.7.4"
resolved "http://npm.yoho.cn/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39"
debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8:
debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.1.3, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8:
version "2.6.8"
resolved "http://npm.yoho.cn/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc"
dependencies:
... ... @@ -4369,6 +4369,12 @@ jsonify@~0.0.0:
version "0.0.0"
resolved "http://npm.yoho.cn/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73"
jsonp@^0.2.1:
version "0.2.1"
resolved "http://npm.yoho.cn/jsonp/-/jsonp-0.2.1.tgz#a65b4fa0f10bda719a05441ea7b94c55f3e15bae"
dependencies:
debug "^2.1.3"
jsonparse@0.0.5:
version "0.0.5"
resolved "http://npm.yoho.cn/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64"
... ...