From b6656a10d2bdc209bb91f63bf4ff35776796591d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=A5=9D=E9=91=AB=E5=A5=94?= Date: Fri, 14 Jul 2017 16:41:14 +0800 Subject: [PATCH 01/10] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E5=90=8D=EF=BC=8Femail=EF=BC=8F=E7=BB=84=EF=BC=8F?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E6=A8=A1=E7=B3=8A=E6=90=9C=E7=B4=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server/controllers/project.js | 33 ++++++++++++- server/controllers/user.js | 26 ++++++++++ server/models/group.js | 6 +++ server/models/project.js | 7 +++ server/models/user.js | 8 ++++ server/router.js | 2 + server/utils/commons.js | 7 +++ server_dist/controllers/project.js | 76 +++++++++++++++++++++++++++++- server_dist/controllers/user.js | 66 +++++++++++++++++++++++++- server_dist/models/group.js | 7 +++ server_dist/models/project.js | 7 +++ server_dist/models/user.js | 7 +++ server_dist/router.js | 2 + server_dist/utils/commons.js | 7 +++ 14 files changed, 258 insertions(+), 3 deletions(-) diff --git a/server/controllers/project.js b/server/controllers/project.js index 7509f16a..b778acc1 100644 --- a/server/controllers/project.js +++ b/server/controllers/project.js @@ -2,12 +2,14 @@ import projectModel from '../models/project.js' import yapi from '../yapi.js' import baseController from './base.js' import interfaceModel from '../models/interface.js' +import groupModel from '../models/group' class projectController extends baseController { constructor(ctx){ super(ctx) this.Model = yapi.getInst(projectModel); + this.groupModel = yapi.getInst(groupModel); } /** @@ -79,7 +81,7 @@ class projectController extends baseController { * @category project * @foldnumber 10 * @param {Number} id 项目id,不能为空 - * @param {member_uid} uid 项目成员uid,不能为空 + * @param {String} member_uid 项目成员uid,不能为空 * @returns {Object} * @example ./api/project/add_member.json */ @@ -277,6 +279,35 @@ class projectController extends baseController { ctx.body = yapi.commons.resReturn(null, 402, e.message) } } + + /** + * 模糊搜索项目名称或者组名称 + * @interface /project/search + * @method GET + * @category project + * @foldnumber 10 + * @param {String} q + * @return {Object} + * @example + */ + async search(ctx) { + const { q } = ctx.request.query; + + if (!q) { + return ctx.body = yapi.commons.resReturn(void 0, 400, 'No keyword.') + } + + if (!yapi.commons.validateSearchKeyword(q)) { + return ctx.body = yapi.commons.resReturn(void 0, 400, 'Bad query.') + } + + let queryList = { + project: await this.Model.search(q), + group: await this.groupModel.search(q) + } + + return ctx.body = yapi.commons.resReturn(queryList, 200, 'ok') + } } module.exports = projectController; \ No newline at end of file diff --git a/server/controllers/user.js b/server/controllers/user.js index cb2f59a9..2ebd6a3d 100644 --- a/server/controllers/user.js +++ b/server/controllers/user.js @@ -9,6 +9,7 @@ const jwt = require('jsonwebtoken'); class userController extends baseController{ constructor(ctx){ super(ctx) + this.Model = yapi.getInst(userModel); } /** * 用户登录接口 @@ -358,6 +359,31 @@ class userController extends baseController{ ctx.body = yapi.commons.resReturn(null,402,e.message); } } + + /** + * 模糊搜索用户名或者email + * @interface /user/search + * @method GET + * @category user + * @foldnumber 10 + * @param {String} q + * @return {Object} + * @example + */ + async search(ctx) { + const { q } = ctx.request.query; + + if (!q) { + return ctx.body = yapi.commons.resReturn(void 0, 400, 'No keyword.') + } + + if (!yapi.commons.validateSearchKeyword(q)) { + return ctx.body = yapi.commons.resReturn(void 0, 400, 'Bad query.') + } + + let queryList = await this.Model.search(q); + return ctx.body = yapi.commons.resReturn(queryList, 200, 'ok') + } } module.exports = userController \ No newline at end of file diff --git a/server/models/group.js b/server/models/group.js index bf15c1fb..7b7c598f 100644 --- a/server/models/group.js +++ b/server/models/group.js @@ -44,6 +44,12 @@ class groupModel extends baseModel{ }) } + search(keyword) { + return this.model.find({ + name: new RegExp(keyword, 'ig') + }) + .limit(10) + } } diff --git a/server/models/project.js b/server/models/project.js index 00f4e19b..dfcd585c 100644 --- a/server/models/project.js +++ b/server/models/project.js @@ -107,6 +107,13 @@ class projectModel extends baseModel{ }) } + search(keyword) { + return this.model.find({ + name: new RegExp(keyword, 'ig') + }) + .limit(10) + } + } module.exports = projectModel; \ No newline at end of file diff --git a/server/models/user.js b/server/models/user.js index 85d7b07f..b0ded45d 100644 --- a/server/models/user.js +++ b/server/models/user.js @@ -59,6 +59,14 @@ class userModel extends baseModel{ up_time: yapi.commons.time() }) } + search(keyword) { + return this.model.find({ + $or: [ + { email: new RegExp(keyword, 'i') }, + { username: new RegExp(keyword, 'i')} + ] + }).limit(10) + } } diff --git a/server/router.js b/server/router.js index cf96b9ad..4529f415 100644 --- a/server/router.js +++ b/server/router.js @@ -44,6 +44,7 @@ createAction('user', 'del', 'post', 'del') createAction('user', 'status', 'get', 'getLoginStatus') createAction('user', 'logout', 'get', 'logout') createAction('user', 'login_by_token', 'post', 'loginByToken') +createAction('user', 'search', 'get', 'search') //project @@ -54,6 +55,7 @@ createAction('project', 'up', 'post', 'up') createAction('project', 'del', 'post', 'del') createAction('project', 'add_member', 'post', 'addMember') createAction('project', 'del_member', 'post', 'delMember') +createAction('project', 'search', 'get', 'search') //interface createAction('interface', 'add', 'post', 'add') diff --git a/server/utils/commons.js b/server/utils/commons.js index 833b8625..9db55a0b 100644 --- a/server/utils/commons.js +++ b/server/utils/commons.js @@ -113,4 +113,11 @@ exports.sendMail = (options,cb) => { subject: 'yapi平台', html: options.contents }, cb) +} + +exports.validateSearchKeyword = keyword => { + if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) { + return false; + } + return true; } \ No newline at end of file diff --git a/server_dist/controllers/project.js b/server_dist/controllers/project.js index da0e462f..55d71834 100644 --- a/server_dist/controllers/project.js +++ b/server_dist/controllers/project.js @@ -44,6 +44,10 @@ var _interface = require('../models/interface.js'); var _interface2 = _interopRequireDefault(_interface); +var _group = require('../models/group'); + +var _group2 = _interopRequireDefault(_group); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var projectController = function (_baseController) { @@ -55,6 +59,7 @@ var projectController = function (_baseController) { var _this = (0, _possibleConstructorReturn3.default)(this, (projectController.__proto__ || (0, _getPrototypeOf2.default)(projectController)).call(this, ctx)); _this.Model = _yapi2.default.getInst(_project2.default); + _this.groupModel = _yapi2.default.getInst(_group2.default); return _this; } @@ -194,7 +199,7 @@ var projectController = function (_baseController) { * @category project * @foldnumber 10 * @param {Number} id 项目id,不能为空 - * @param {member_uid} uid 项目成员uid,不能为空 + * @param {String} member_uid 项目成员uid,不能为空 * @returns {Object} * @example ./api/project/add_member.json */ @@ -696,6 +701,75 @@ var projectController = function (_baseController) { return up; }() + + /** + * 模糊搜索项目名称或者组名称 + * @interface /project/search + * @method GET + * @category project + * @foldnumber 10 + * @param {String} q + * @return {Object} + * @example + */ + + }, { + key: 'search', + value: function () { + var _ref8 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee8(ctx) { + var q, queryList; + return _regenerator2.default.wrap(function _callee8$(_context8) { + while (1) { + switch (_context8.prev = _context8.next) { + case 0: + q = ctx.request.query.q; + + if (q) { + _context8.next = 3; + break; + } + + return _context8.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(void 0, 400, 'No keyword.')); + + case 3: + if (_yapi2.default.commons.validateSearchKeyword(q)) { + _context8.next = 5; + break; + } + + return _context8.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(void 0, 400, 'Bad query.')); + + case 5: + _context8.next = 7; + return this.Model.search(q); + + case 7: + _context8.t0 = _context8.sent; + _context8.next = 10; + return this.groupModel.search(q); + + case 10: + _context8.t1 = _context8.sent; + queryList = { + project: _context8.t0, + group: _context8.t1 + }; + return _context8.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(queryList, 200, 'ok')); + + case 13: + case 'end': + return _context8.stop(); + } + } + }, _callee8, this); + })); + + function search(_x8) { + return _ref8.apply(this, arguments); + } + + return search; + }() }]); return projectController; }(_base2.default); diff --git a/server_dist/controllers/user.js b/server_dist/controllers/user.js index f2011768..bca15e46 100644 --- a/server_dist/controllers/user.js +++ b/server_dist/controllers/user.js @@ -61,7 +61,11 @@ var userController = function (_baseController) { function userController(ctx) { (0, _classCallCheck3.default)(this, userController); - return (0, _possibleConstructorReturn3.default)(this, (userController.__proto__ || (0, _getPrototypeOf2.default)(userController)).call(this, ctx)); + + var _this = (0, _possibleConstructorReturn3.default)(this, (userController.__proto__ || (0, _getPrototypeOf2.default)(userController)).call(this, ctx)); + + _this.Model = _yapi2.default.getInst(_user2.default); + return _this; } /** * 用户登录接口 @@ -825,6 +829,66 @@ var userController = function (_baseController) { return update; }() + + /** + * 模糊搜索用户名或者email + * @interface /user/search + * @method GET + * @category user + * @foldnumber 10 + * @param {String} q + * @return {Object} + * @example + */ + + }, { + key: 'search', + value: function () { + var _ref13 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee13(ctx) { + var q, queryList; + return _regenerator2.default.wrap(function _callee13$(_context13) { + while (1) { + switch (_context13.prev = _context13.next) { + case 0: + q = ctx.request.query.q; + + if (q) { + _context13.next = 3; + break; + } + + return _context13.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(void 0, 400, 'No keyword.')); + + case 3: + if (_yapi2.default.commons.validateSearchKeyword(q)) { + _context13.next = 5; + break; + } + + return _context13.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(void 0, 400, 'Bad query.')); + + case 5: + _context13.next = 7; + return this.Model.search(q); + + case 7: + queryList = _context13.sent; + return _context13.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(queryList, 200, 'ok')); + + case 9: + case 'end': + return _context13.stop(); + } + } + }, _callee13, this); + })); + + function search(_x14) { + return _ref13.apply(this, arguments); + } + + return search; + }() }]); return userController; }(_base2.default); diff --git a/server_dist/models/group.js b/server_dist/models/group.js index e60eb34f..84ea1a38 100644 --- a/server_dist/models/group.js +++ b/server_dist/models/group.js @@ -94,6 +94,13 @@ var groupModel = function (_baseModel) { up_time: _yapi2.default.commons.time() }); } + }, { + key: 'search', + value: function search(keyword) { + return this.model.find({ + name: new RegExp(keyword, 'ig') + }).limit(10); + } }]); return groupModel; }(_base2.default); diff --git a/server_dist/models/project.js b/server_dist/models/project.js index 094fb158..010f615f 100644 --- a/server_dist/models/project.js +++ b/server_dist/models/project.js @@ -154,6 +154,13 @@ var projectModel = function (_baseModel) { members: [uid] }); } + }, { + key: 'search', + value: function search(keyword) { + return this.model.find({ + name: new RegExp(keyword, 'ig') + }).limit(10); + } }]); return projectModel; }(_base2.default); diff --git a/server_dist/models/user.js b/server_dist/models/user.js index 01101c94..5fd644a4 100644 --- a/server_dist/models/user.js +++ b/server_dist/models/user.js @@ -115,6 +115,13 @@ var userModel = function (_baseModel) { up_time: _yapi2.default.commons.time() }); } + }, { + key: 'search', + value: function search(keyword) { + return this.model.find({ + $or: [{ email: new RegExp(keyword, 'i') }, { username: new RegExp(keyword, 'i') }] + }).limit(10); + } }]); return userModel; }(_base2.default); diff --git a/server_dist/router.js b/server_dist/router.js index ebb9b56d..74988a75 100644 --- a/server_dist/router.js +++ b/server_dist/router.js @@ -71,6 +71,7 @@ createAction('user', 'del', 'post', 'del'); createAction('user', 'status', 'get', 'getLoginStatus'); createAction('user', 'logout', 'get', 'logout'); createAction('user', 'login_by_token', 'post', 'loginByToken'); +createAction('user', 'search', 'get', 'search'); //project createAction('project', 'add', 'post', 'add'); @@ -80,6 +81,7 @@ createAction('project', 'up', 'post', 'up'); createAction('project', 'del', 'post', 'del'); createAction('project', 'add_member', 'post', 'addMember'); createAction('project', 'del_member', 'post', 'delMember'); +createAction('project', 'search', 'get', 'search'); //interface createAction('interface', 'add', 'post', 'add'); diff --git a/server_dist/utils/commons.js b/server_dist/utils/commons.js index 57f6cb39..39a6dabf 100644 --- a/server_dist/utils/commons.js +++ b/server_dist/utils/commons.js @@ -134,4 +134,11 @@ exports.sendMail = function (options, cb) { subject: 'yapi平台', html: options.contents }, cb); +}; + +exports.validateSearchKeyword = function (keyword) { + if (/^\*|\?|\+|\$|\^|\\|\.$/.test(keyword)) { + return false; + } + return true; }; \ No newline at end of file From 37762a109d0d2dd9d9c893eae27c6a430f18919d Mon Sep 17 00:00:00 2001 From: "wenbo.dong" Date: Fri, 14 Jul 2017 16:54:20 +0800 Subject: [PATCH 02/10] =?UTF-8?q?feat:=20=E6=96=B0=E5=BB=BA=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/actions/login.js | 16 ++- .../Login/{loginWrap.js => login-wrap.js} | 2 +- client/containers/Login/login.scss | 7 + client/containers/Login/reg.js | 58 +++++++- .../ProjectGroups/ProjectList/index.js | 135 +++++++++++++++++- client/containers/index.js | 2 +- 6 files changed, 205 insertions(+), 15 deletions(-) rename client/containers/Login/{loginWrap.js => login-wrap.js} (97%) create mode 100644 client/containers/Login/login.scss diff --git a/client/actions/login.js b/client/actions/login.js index cf5b344f..be866b50 100644 --- a/client/actions/login.js +++ b/client/actions/login.js @@ -1,6 +1,5 @@ import { LOGIN, - REGISTER, LOGIN_TYPE } from '../constants/action-types.js'; import axios from 'axios'; @@ -24,9 +23,18 @@ const loginActions = (data) => { } const regActions = (data) => { - return { - type: REGISTER, - data + console.log(data); + const param = { + email: data.email, + password: data.password, + username: data.userName + } + return () => { + axios.get('/user/login', param).then((res) => { + console.log(res); + }).catch((err) => { + console.log(err); + }); } } diff --git a/client/containers/Login/loginWrap.js b/client/containers/Login/login-wrap.js similarity index 97% rename from client/containers/Login/loginWrap.js rename to client/containers/Login/login-wrap.js index 0e616cf8..6fab88b0 100644 --- a/client/containers/Login/loginWrap.js +++ b/client/containers/Login/login-wrap.js @@ -1,4 +1,4 @@ -import './Login.scss' +import './login.scss' import React, { Component } from 'react' import { connect } from 'react-redux' import PropTypes from 'prop-types' diff --git a/client/containers/Login/login.scss b/client/containers/Login/login.scss new file mode 100644 index 00000000..23812370 --- /dev/null +++ b/client/containers/Login/login.scss @@ -0,0 +1,7 @@ +@import '../../styles/common.scss'; + +/* .login-main.css */ +.login-form { + width: 4rem; + margin: 1rem auto; +} diff --git a/client/containers/Login/reg.js b/client/containers/Login/reg.js index 87f25aa0..d95f6287 100644 --- a/client/containers/Login/reg.js +++ b/client/containers/Login/reg.js @@ -18,7 +18,10 @@ const FormItem = Form.Item; class Reg extends Component { constructor(props) { - super(props) + super(props); + this.state = { + confirmDirty: false + } } static propTypes = { @@ -29,17 +32,41 @@ class Reg extends Component { handleSubmit = (e) => { e.preventDefault(); const form = this.props.form; - form.validateFields((err, values) => { + form.validateFieldsAndScroll((err, values) => { if (!err) { this.props.regActions(values); } }); } + handleConfirmBlur = (e) => { + const value = e.target.value; + this.setState({ confirmDirty: this.state.confirmDirty || !!value }); + } + + checkPassword = (rule, value, callback) => { + const form = this.props.form; + if (value && value !== form.getFieldValue('password')) { + callback('Two passwords that you enter is inconsistent!'); + } else { + callback(); + } + } + + checkConfirm = (rule, value, callback) => { + const form = this.props.form; + if (value && this.state.confirmDirty) { + form.validateFields(['confirm'], { force: true }); + } + callback(); + } + render() { const { getFieldDecorator } = this.props.form; return (
+ + {/* 用户名 */} {getFieldDecorator('userName', { rules: [{ required: true, message: '请输入用户名!' }] @@ -47,6 +74,8 @@ class Reg extends Component { } placeholder="Username" /> )} + + {/* Emaiil */} {getFieldDecorator('email', { rules: [{ required: true, message: '请输入email!' }] @@ -54,13 +83,36 @@ class Reg extends Component { } placeholder="Email" /> )} + + {/* 密码 */} {getFieldDecorator('password', { - rules: [{ required: true, message: '请输入密码!' }] + rules: [{ + required: true, + message: '请输入密码!' + }, { + validator: this.checkConfirm + }] })( } type="password" placeholder="Password" /> )} + + {/* 密码二次确认 */} + + {getFieldDecorator('confirm', { + rules: [{ + required: true, + message: '请再次输入密码密码!' + }, { + validator: this.checkPassword + }] + })( + } type="password" placeholder="Confirm Password" /> + )} + + + {/* 注册按钮 */} diff --git a/client/containers/ProjectGroups/ProjectList/index.js b/client/containers/ProjectGroups/ProjectList/index.js index e7446659..a577b695 100644 --- a/client/containers/ProjectGroups/ProjectList/index.js +++ b/client/containers/ProjectGroups/ProjectList/index.js @@ -1,7 +1,9 @@ import React, { Component } from 'react' -// import PropTypes from 'prop-types' +import PropTypes from 'prop-types' // import { connect } from 'react-redux' -import { Table } from 'antd' +import { Table, Button, Modal, Form, Input, Icon, Tooltip } from 'antd'; +const { TextArea } = Input; +const FormItem = Form.Item; const columns = [{ title: 'Name', @@ -35,12 +37,133 @@ const data = [{ age: 32 }]; - - -export default class GroupList extends Component { +const formItemLayout = { + labelCol: { + xs: { span: 24 }, + sm: { span: 6 } + }, + wrapperCol: { + xs: { span: 24 }, + sm: { span: 14 } + } +}; +class ProjectList extends Component { + constructor(props) { + super(props); + this.state = { + visible: false + } + } + static propTypes = { + form: PropTypes.object + } + addProject = () => { + this.setState({ + visible: true + }); + } + handleOk = (e) => { + e.preventDefault(); + this.props.form.validateFields((err, values) => { + if (!err) { + console.log('Received values of form: ', values); + this.setState({ + visible: false + }); + } + }); + } + handleCancel = () => { + this.props.form.resetFields(); + this.setState({ + visible: false + }); + } + handleSubmit = (e) => { + console.log(e); + } render() { + const { getFieldDecorator } = this.props.form; return ( - +
+ + + + + {getFieldDecorator('name', { + rules: [{ + required: true, message: '请输入项目名称!' + }] + })( + + )} + + + + 线上域名  + + + + + )} + > + {getFieldDecorator('prd_host', { + rules: [{ + required: true, message: '请输入项目线上域名!' + }] + })( + + )} + + + + {getFieldDecorator('basepath', { + rules: [{ + required: true, message: '请输入项目基本路径!' + }] + })( + + )} + + + + {getFieldDecorator('desc', { + rules: [{ + required: true, message: '请输入描述!' + }] + })( +