Merge branch 'feature/activity-image-upload' of git.yoho.cn:fe/yoho-activity-pla…
…tform into feature/activity-image-upload
Showing
12 changed files
with
196 additions
and
27 deletions
@@ -140,6 +140,67 @@ const userController = { | @@ -140,6 +140,67 @@ const userController = { | ||
140 | res.end(exportFile, 'binary'); | 140 | res.end(exportFile, 'binary'); |
141 | }) | 141 | }) |
142 | .catch(next); | 142 | .catch(next); |
143 | + }, | ||
144 | + userLoginLog(req, res) { | ||
145 | + res.render('user/login-log', { | ||
146 | + bodyClass: 'nav-md', | ||
147 | + module: 'admin', | ||
148 | + page: 'user-login-log' | ||
149 | + }); | ||
150 | + }, | ||
151 | + exportUserLoginLog(req, res, next) { | ||
152 | + const url = req.query.url; | ||
153 | + const startTime = req.query.startTime; | ||
154 | + const endTime = req.query.endTime; | ||
155 | + | ||
156 | + let conf = { | ||
157 | + name: 'mysheet', | ||
158 | + cols: [ | ||
159 | + { | ||
160 | + caption: '用户id', | ||
161 | + type: 'string' | ||
162 | + }, | ||
163 | + { | ||
164 | + caption: '登录时间', | ||
165 | + type: 'string' | ||
166 | + }, | ||
167 | + { | ||
168 | + caption: '平台', | ||
169 | + type: 'string' | ||
170 | + }, | ||
171 | + { | ||
172 | + caption: '地址', | ||
173 | + type: 'string' | ||
174 | + }, | ||
175 | + { | ||
176 | + caption: 'ip', | ||
177 | + type: 'string' | ||
178 | + } | ||
179 | + ], | ||
180 | + rows: [] | ||
181 | + }; | ||
182 | + | ||
183 | + req.ctx(UserModel).exportUserLoginLog({url, startTime, endTime}) | ||
184 | + .then(result => { | ||
185 | + let temp = []; | ||
186 | + | ||
187 | + _.each(result, item => { | ||
188 | + temp = []; | ||
189 | + temp.push(item.user_id); | ||
190 | + temp.push(moment(item.login_time).format('YYYY-MM-DD HH:mm:ss')); | ||
191 | + temp.push(item.platform); | ||
192 | + temp.push(item.referer); | ||
193 | + temp.push(item.ip); | ||
194 | + conf.rows.push(temp); | ||
195 | + }); | ||
196 | + | ||
197 | + let exportFile = excelExport.execute(conf); | ||
198 | + | ||
199 | + res.setHeader('Content-Type', 'application/vnd.openxmlformats'); | ||
200 | + res.setHeader('Content-Disposition', 'attachment; filename=user-login-log.xlsx'); | ||
201 | + res.end(exportFile, 'binary'); | ||
202 | + }) | ||
203 | + .catch(next); | ||
143 | } | 204 | } |
144 | }; | 205 | }; |
145 | 206 |
@@ -8,6 +8,7 @@ const mysqlCli = global.yoho.utils.mysqlCli; | @@ -8,6 +8,7 @@ const mysqlCli = global.yoho.utils.mysqlCli; | ||
8 | 8 | ||
9 | const _ = require('lodash'); | 9 | const _ = require('lodash'); |
10 | const TABLE_USER = 'user'; | 10 | const TABLE_USER = 'user'; |
11 | +const TABLE_USER_LOGIN_LOG = 'act_login_user_log'; | ||
11 | 12 | ||
12 | class UserModel extends global.yoho.BaseModel { | 13 | class UserModel extends global.yoho.BaseModel { |
13 | constructor(ctx) { | 14 | constructor(ctx) { |
@@ -64,6 +65,26 @@ class UserModel extends global.yoho.BaseModel { | @@ -64,6 +65,26 @@ class UserModel extends global.yoho.BaseModel { | ||
64 | ); | 65 | ); |
65 | } | 66 | } |
66 | 67 | ||
68 | + /** | ||
69 | + * 导出用户登录日志 | ||
70 | + * @returns {*} | ||
71 | + */ | ||
72 | + exportUserLoginLog({url, startTime, endTime}) { | ||
73 | + let sql = `select user_id,login_time,platform,ip,referer from ${TABLE_USER_LOGIN_LOG} where 1=1`; | ||
74 | + | ||
75 | + if (url) { | ||
76 | + sql += ' and referer like :url'; | ||
77 | + } | ||
78 | + if (startTime) { | ||
79 | + sql += ' and login_time >= :startTime'; | ||
80 | + } | ||
81 | + if (endTime) { | ||
82 | + sql += ' and login_time <= :endTime'; | ||
83 | + } | ||
84 | + | ||
85 | + return mysqlCli.query(sql, {url: `%${url}%`, startTime, endTime}); | ||
86 | + } | ||
87 | + | ||
67 | } | 88 | } |
68 | 89 | ||
69 | module.exports = UserModel; | 90 | module.exports = UserModel; |
@@ -21,6 +21,10 @@ router.get('/activity/createArticle', activity.createArticlePage); | @@ -21,6 +21,10 @@ router.get('/activity/createArticle', activity.createArticlePage); | ||
21 | // 用户管理[page] | 21 | // 用户管理[page] |
22 | router.get('/user/list', user.userListPage); | 22 | router.get('/user/list', user.userListPage); |
23 | 23 | ||
24 | +// 用户登录日志[page] | ||
25 | +router.get('/user/login-log', user.userLoginLog); | ||
26 | +router.get('/user/exportUserLoginLog', user.exportUserLoginLog); | ||
27 | + | ||
24 | 28 | ||
25 | // 管理员[ajax] | 29 | // 管理员[ajax] |
26 | router.post('/api/login', admin.login); | 30 | router.post('/api/login', admin.login); |
apps/admin/views/action/user/login-log.hbs
0 → 100644
1 | +<!-- page content --> | ||
2 | +<div class="right_col" role="main"> | ||
3 | + <div class=""> | ||
4 | + <div class="row"> | ||
5 | + <div class="col-md-12 col-sm-12 col-xs-12"> | ||
6 | + <div class="x_panel"> | ||
7 | + <div class="x_title"> | ||
8 | + <form class="form-horizontal"> | ||
9 | + <div class="form-group"> | ||
10 | + <input type="text" id="url" name="url" placeholder="输入查询的url" class="form-control" style="width:350px;"> | ||
11 | + </div> | ||
12 | + <div class="form-group"> | ||
13 | + <input type="text" id="time" name="time" | ||
14 | + class="form-control" | ||
15 | + placeholder="开始时间 - 结束时间" style="width:350px;"/> | ||
16 | + </div> | ||
17 | + <div class="form-group"> | ||
18 | + <button type="button" class="btn btn-primary btn-export">导出</button> | ||
19 | + </div> | ||
20 | + <div class="clearfix"></div> | ||
21 | + </form> | ||
22 | + </div> | ||
23 | + </div> | ||
24 | + </div> | ||
25 | + </div> | ||
26 | + </div> | ||
27 | +</div> | ||
28 | +<!-- /page content --> |
@@ -13,18 +13,6 @@ _.mergeWith(baseConfig, { | @@ -13,18 +13,6 @@ _.mergeWith(baseConfig, { | ||
13 | output: { | 13 | output: { |
14 | publicPath: devInfo.publicPath | 14 | publicPath: devInfo.publicPath |
15 | }, | 15 | }, |
16 | - module: { | ||
17 | - rules: [{ | ||
18 | - enforce: 'pre', | ||
19 | - test: /(\.js|\.vue)$/, | ||
20 | - exclude: /node_modules/, | ||
21 | - loader: 'eslint-loader', | ||
22 | - options: { | ||
23 | - cache: true, | ||
24 | - formatter: require('eslint/lib/formatters/codeframe') | ||
25 | - } | ||
26 | - }] | ||
27 | - }, | ||
28 | devServer: { | 16 | devServer: { |
29 | host: devInfo.host, | 17 | host: devInfo.host, |
30 | port: devInfo.port, | 18 | port: devInfo.port, |
@@ -56,6 +56,7 @@ | @@ -56,6 +56,7 @@ | ||
56 | <li><a><i class="fa fa-users"></i> 用户管理 <span class="fa fa-chevron-down"></span></a> | 56 | <li><a><i class="fa fa-users"></i> 用户管理 <span class="fa fa-chevron-down"></span></a> |
57 | <ul class="nav child_menu"> | 57 | <ul class="nav child_menu"> |
58 | <li><a href="/admin/user/list">用户列表</a></li> | 58 | <li><a href="/admin/user/list">用户列表</a></li> |
59 | + <li><a href="/admin/user/login-log">用户登录日志</a></li> | ||
59 | </ul> | 60 | </ul> |
60 | </li> | 61 | </li> |
61 | </ul> | 62 | </ul> |
@@ -50,6 +50,7 @@ | @@ -50,6 +50,7 @@ | ||
50 | "fast-safe-stringify": "^1.2.0", | 50 | "fast-safe-stringify": "^1.2.0", |
51 | "feed": "^1.1.0", | 51 | "feed": "^1.1.0", |
52 | "geetest": "^4.1.2", | 52 | "geetest": "^4.1.2", |
53 | + "jsonp": "^0.2.1", | ||
53 | "lodash": "^4.17.4", | 54 | "lodash": "^4.17.4", |
54 | "mOxie": "^1.5.5", | 55 | "mOxie": "^1.5.5", |
55 | "memory-cache": "^0.1.6", | 56 | "memory-cache": "^0.1.6", |
1 | import qs from 'yoho-qs'; | 1 | import qs from 'yoho-qs'; |
2 | +import cookie from 'yoho-cookie'; | ||
3 | +import jsonp from 'jsonp'; | ||
2 | import 'whatwg-fetch'; | 4 | import 'whatwg-fetch'; |
3 | 5 | ||
4 | const uid = qs.yh_uid; | 6 | const uid = qs.yh_uid; |
@@ -10,22 +12,38 @@ if (process.env.NODE_ENV === 'production') { | @@ -10,22 +12,38 @@ if (process.env.NODE_ENV === 'production') { | ||
10 | baseUrl = 'http://localhost:6006'; | 12 | baseUrl = 'http://localhost:6006'; |
11 | } | 13 | } |
12 | 14 | ||
15 | +const loginLog = userId => { | ||
16 | + if (!cookie.get('_loginLog')) { | ||
17 | + fetch(`${baseUrl}/user/loginLog`, { | ||
18 | + method: 'POST', | ||
19 | + headers: { | ||
20 | + 'Content-Type': 'application/json' | ||
21 | + }, | ||
22 | + body: JSON.stringify({ | ||
23 | + uid: userId | ||
24 | + }) | ||
25 | + }).then(res => { | ||
26 | + if (res.ok) { | ||
27 | + cookie.set('_loginLog', true); | ||
28 | + } | ||
29 | + }); | ||
30 | + } | ||
31 | +}; | ||
32 | + | ||
13 | if (!uid) { | 33 | if (!uid) { |
14 | - fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => { | ||
15 | - if (res.ok) { | ||
16 | - res.json().then(data => { | ||
17 | - window.location.href = data.loginUrl; | 34 | + jsonp('http://m.yohobuy.com/passport/login/user', {}, (errMsg, userRes) => { |
35 | + if (userRes.code !== 200) { | ||
36 | + fetch(`${baseUrl}/user/getLoginUrl?url=${window.location.href}`).then(res => { | ||
37 | + if (res.ok) { | ||
38 | + res.json().then(data => { | ||
39 | + window.location.href = data.loginUrl; | ||
40 | + }); | ||
41 | + } | ||
18 | }); | 42 | }); |
43 | + } else { | ||
44 | + loginLog(userRes.data); | ||
19 | } | 45 | } |
20 | }); | 46 | }); |
21 | } else { | 47 | } else { |
22 | - fetch(`${baseUrl}/user/loginLog`, { | ||
23 | - method: 'POST', | ||
24 | - headers: { | ||
25 | - 'Content-Type': 'application/json' | ||
26 | - }, | ||
27 | - body: JSON.stringify({ | ||
28 | - uid | ||
29 | - }) | ||
30 | - }); | 48 | + loginLog(uid); |
31 | } | 49 | } |
@@ -185,7 +185,7 @@ function bind_qiniu_upload() { | @@ -185,7 +185,7 @@ function bind_qiniu_upload() { | ||
185 | 185 | ||
186 | // why? reference: https://github.com/qiniu/js-sdk/issues/266 | 186 | // why? reference: https://github.com/qiniu/js-sdk/issues/266 |
187 | Promise.all([ | 187 | Promise.all([ |
188 | - require('moxie/bin/js/moxie'), | 188 | + require('mOxie/bin/js/moxie'), |
189 | require('plupload/js/plupload.dev'), | 189 | require('plupload/js/plupload.dev'), |
190 | require('qiniu-js') | 190 | require('qiniu-js') |
191 | ]).then(([moxie, plupload]) => { | 191 | ]).then(([moxie, plupload]) => { |
public/js/admin/user-login-log.page.js
0 → 100644
1 | +require('admin/user-login-log.page.css'); | ||
2 | +require('bootstrap-daterangepicker'); | ||
3 | +require('bootpag/lib/jquery.bootpag.min'); | ||
4 | +const moment = require('moment'); | ||
5 | + | ||
6 | +function bind_date_picker() { | ||
7 | + if (typeof ($.fn.daterangepicker) === 'undefined') { | ||
8 | + return; | ||
9 | + } | ||
10 | + | ||
11 | + $('#time').daterangepicker({ | ||
12 | + timePicker: true, | ||
13 | + timePickerIncrement: 30, | ||
14 | + startDate: moment().add(-30, 'day'), | ||
15 | + endDate: moment(), | ||
16 | + locale: { | ||
17 | + format: 'YYYY-MM-DD HH:mm:ss' | ||
18 | + } | ||
19 | + }); | ||
20 | +} | ||
21 | +function bind_button_event() { | ||
22 | + $('.btn-export').click(() => { | ||
23 | + const url = $('#url').val(); | ||
24 | + const time = $('#time').val(); | ||
25 | + const timeArray = time.split(' - '); | ||
26 | + let startTime, endTime; | ||
27 | + | ||
28 | + if (timeArray.length === 2) { | ||
29 | + startTime = moment(timeArray[0]).valueOf(); | ||
30 | + endTime = moment(timeArray[1]).valueOf(); | ||
31 | + } | ||
32 | + | ||
33 | + window.open(`/admin/user/exportUserLoginLog?startTime=${startTime}&endTime=${endTime}&url=${url}`, '_blank'); | ||
34 | + }); | ||
35 | +} | ||
36 | + | ||
37 | +(() => { | ||
38 | + bind_date_picker(); | ||
39 | + bind_button_event(); | ||
40 | +})(); |
public/scss/admin/user-login-log.page.css
0 → 100644
1 | +@import '~bootstrap-daterangepicker'; |
@@ -2055,7 +2055,7 @@ debug@0.7.4, debug@~0.7.4: | @@ -2055,7 +2055,7 @@ debug@0.7.4, debug@~0.7.4: | ||
2055 | version "0.7.4" | 2055 | version "0.7.4" |
2056 | resolved "http://npm.yoho.cn/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" | 2056 | resolved "http://npm.yoho.cn/debug/-/debug-0.7.4.tgz#06e1ea8082c2cb14e39806e22e2f6f757f92af39" |
2057 | 2057 | ||
2058 | -debug@2, debug@2.6.8, debug@^2.1.1, debug@^2.2.0, debug@^2.6.0, debug@^2.6.8: | 2058 | +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: |
2059 | version "2.6.8" | 2059 | version "2.6.8" |
2060 | resolved "http://npm.yoho.cn/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" | 2060 | resolved "http://npm.yoho.cn/debug/-/debug-2.6.8.tgz#e731531ca2ede27d188222427da17821d68ff4fc" |
2061 | dependencies: | 2061 | dependencies: |
@@ -4369,6 +4369,12 @@ jsonify@~0.0.0: | @@ -4369,6 +4369,12 @@ jsonify@~0.0.0: | ||
4369 | version "0.0.0" | 4369 | version "0.0.0" |
4370 | resolved "http://npm.yoho.cn/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" | 4370 | resolved "http://npm.yoho.cn/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" |
4371 | 4371 | ||
4372 | +jsonp@^0.2.1: | ||
4373 | + version "0.2.1" | ||
4374 | + resolved "http://npm.yoho.cn/jsonp/-/jsonp-0.2.1.tgz#a65b4fa0f10bda719a05441ea7b94c55f3e15bae" | ||
4375 | + dependencies: | ||
4376 | + debug "^2.1.3" | ||
4377 | + | ||
4372 | jsonparse@0.0.5: | 4378 | jsonparse@0.0.5: |
4373 | version "0.0.5" | 4379 | version "0.0.5" |
4374 | resolved "http://npm.yoho.cn/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" | 4380 | resolved "http://npm.yoho.cn/jsonparse/-/jsonparse-0.0.5.tgz#330542ad3f0a654665b778f3eb2d9a9fa507ac64" |
-
Please register or login to post a comment