From dd06dfe5fcacf180f0ee2fa057c74254036d6ce4 Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Wed, 16 Aug 2017 18:04:16 +0800 Subject: [PATCH 1/9] feat: add interface and col route --- .../containers/Project/Interface/Interface.js | 44 ++++++--- .../InterfaceList/InterfaceContent.js | 93 ++++++++++++++++--- .../Interface/InterfaceList/InterfaceMenu.js | 36 ++++--- .../Project/Interface/interface.scss | 3 + client/containers/Project/Project.js | 4 +- .../reducer/middleware/messageMiddleware.js | 1 + client/reducer/modules/interface.js | 29 ++---- 7 files changed, 139 insertions(+), 71 deletions(-) diff --git a/client/containers/Project/Interface/Interface.js b/client/containers/Project/Interface/Interface.js index f6c4b114..a8a3a193 100644 --- a/client/containers/Project/Interface/Interface.js +++ b/client/containers/Project/Interface/Interface.js @@ -1,6 +1,8 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { Row, Col, Tabs } from 'antd'; +import { Route } from 'react-router-dom'; + import './interface.scss' import InterfaceMenu from './InterfaceList/InterfaceMenu.js' @@ -9,37 +11,50 @@ import InterfaceContent from './InterfaceList/InterfaceContent.js' import InterfaceColMenu from './InterfaceCol/InterfaceColMenu.js' import InterfaceColContent from './InterfaceCol/InterfaceColContent.js' +class InterfaceRoute extends Component { + static propTypes = { + match: PropTypes.object + } + constructor(props){ + super(props) + } + render() { + let C, props = this.props; + if (props.match.params.action === 'api') { + C = InterfaceContent; + } else if (props.match.params.action === 'col') { + C = InterfaceColContent; + } + return + } +} + + class Interface extends Component { static propTypes = { match: PropTypes.object } constructor(props) { - super(props) + super(props) this.state = { - contentView: 'list' + curkey: this.props.match.params.action } } - handleTab = (key) => { + onChange = (key)=>{ this.setState({ - contentView: key + curkey: key }) } render() { - const {contentView} = this.state; - let content; - content = contentView === 'list' ? - - : - return
- - + + @@ -48,10 +63,11 @@ class Interface extends Component {
+
- {content} +
@@ -59,4 +75,6 @@ class Interface extends Component { } } + + export default Interface \ No newline at end of file diff --git a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js index bb9cb3b8..2eb414af 100644 --- a/client/containers/Project/Interface/InterfaceList/InterfaceContent.js +++ b/client/containers/Project/Interface/InterfaceList/InterfaceContent.js @@ -1,25 +1,92 @@ -import React from 'react' +import React, { Component } from 'react' +import { connect } from 'react-redux'; +import PropTypes from 'prop-types' import { Tabs } from 'antd'; import Edit from './Edit.js' import View from './View.js' import Run from './Run.js' +import { fetchInterfaceData } from '../../../../reducer/modules/interface.js'; +import { withRouter } from 'react-router-dom'; const TabPane = Tabs.TabPane; +@connect( + state => { + return { + curdata: state.inter.curdata, + list: state.inter.list + } + }, + { + fetchInterfaceData + } +) +class Content extends Component { + static propTypes = { + match: PropTypes.object, + list: PropTypes.array, + curdata: PropTypes.object, + fetchInterfaceData: PropTypes.func + } + constructor(props) { + super(props) + this.state = { + curtab: 'view' + } + this._actionId = 0; + } -const Content = () => { - return
- - - + componentWillReceiveProps(nextProps){ + this.handleRequest(nextProps) + } + + handleRequest(nextProps){ + let matchParams = nextProps.match.params; + let _actionId; + _actionId = matchParams.actionId; + _actionId = parseInt(matchParams.actionId, 10); + if(!nextProps.curdata)return; + if(this._actionId !== _actionId){ + this._actionId = _actionId; + this.props.fetchInterfaceData(_actionId) + } + this.setState({ + curtab: 'view' + }) + } + + onChange = (key)=>{ + this.setState({ + curtab: key + }) + } + + render() { + const tabs = + + {/* */} - - + + - - + + {/* */} - -
+ ; + let tabContent; + if (this.state.curtab === 'view') { + tabContent = ; + } else if (this.state.curtab === 'edit') { + tabContent = + } else if (this.state.curtab === 'run') { + tabContent = + } + + return
+ {tabs} + {tabContent} +
+ } } -export default Content \ No newline at end of file + +export default withRouter(Content) \ No newline at end of file diff --git a/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js b/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js index d1926bee..aaca6ccd 100644 --- a/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js +++ b/client/containers/Project/Interface/InterfaceList/InterfaceMenu.js @@ -1,10 +1,11 @@ import React, { Component } from 'react' import { connect } from 'react-redux'; import PropTypes from 'prop-types' -import { fetchInterfaceList, fetchInterfaceData, changeInterfaceId, addInterfaceData, deleteInterfaceData } from '../../../../reducer/modules/interface.js'; +import { fetchInterfaceList, fetchInterfaceData, addInterfaceData, deleteInterfaceData } from '../../../../reducer/modules/interface.js'; import { Menu, Input, Icon, Tag, Modal, message } from 'antd'; import AddInterfaceForm from './AddInterfaceForm'; import axios from 'axios' +import { Link,withRouter } from 'react-router-dom'; const confirm = Modal.confirm; @@ -14,29 +15,27 @@ const confirm = Modal.confirm; state => { return { list: state.inter.list, - curProject: state.project.curProject, - interfaceId: state.inter.interfaceId + curProject: state.project.curProject } }, { fetchInterfaceList, fetchInterfaceData, - changeInterfaceId, addInterfaceData, deleteInterfaceData } ) class InterfaceMenu extends Component { static propTypes = { + match: PropTypes.object, projectId: PropTypes.string, - interfaceId: PropTypes.number, list: PropTypes.array, fetchInterfaceList: PropTypes.func, curProject: PropTypes.object, fetchInterfaceData: PropTypes.func, - changeInterfaceId: PropTypes.func, addInterfaceData: PropTypes.func, - deleteInterfaceData: PropTypes.func + deleteInterfaceData: PropTypes.func, + history: PropTypes.object } showModal = () => { @@ -59,26 +58,21 @@ class InterfaceMenu extends Component { delIcon: null, filter: '' } + } async handleRequest() { let result = await this.props.fetchInterfaceList(this.props.projectId); - let interfaces = result.payload.data; - if (interfaces.length > 0) { - this.props.changeInterfaceId(interfaces[0]._id) - await this.props.fetchInterfaceData(interfaces[0]._id) + let params = this.props.match.params; + if(!params.actionId){ + this.props.history.replace('/project/'+params.id + '/interface/api/' + result.payload.data[0]._id) } } componentWillMount() { this.handleRequest() } - - onSelect = (item) => { - this.props.changeInterfaceId(parseInt(item.key, 10)) - this.props.fetchInterfaceData(parseInt(item.key, 10)) - } - + handleAddInterface = (data) => { data.project_id = this.props.projectId; axios.post('/api/interface/add', data).then((res) => { @@ -127,6 +121,7 @@ class InterfaceMenu extends Component { render() { const items = []; + const matchParams = this.props.match.params; this.props.list.forEach((item) => { let color, filter = this.state.filter; if(filter && item.title.indexOf(filter) === -1 && item.path.indexOf(filter) === -1){ @@ -141,9 +136,10 @@ class InterfaceMenu extends Component { } items.push( + {item.method} - {item.title} + {item.title} {this.showConfirm(item._id)}} style={{ display: this.state.delIcon == item._id ? 'block' : 'none' }} className="interface-delete-icon" /> ) @@ -163,7 +159,7 @@ class InterfaceMenu extends Component {
- + {items} @@ -171,4 +167,4 @@ class InterfaceMenu extends Component { } } -export default InterfaceMenu \ No newline at end of file +export default withRouter(InterfaceMenu) \ No newline at end of file diff --git a/client/containers/Project/Interface/interface.scss b/client/containers/Project/Interface/interface.scss index 760e4052..ebe76ec3 100644 --- a/client/containers/Project/Interface/interface.scss +++ b/client/containers/Project/Interface/interface.scss @@ -45,6 +45,9 @@ padding: 0 5px; width: 40px; } + .interface-item{ + display: inline + } .interface-delete-icon{ position: absolute; diff --git a/client/containers/Project/Project.js b/client/containers/Project/Project.js index 67ae1ffc..dc7903c9 100644 --- a/client/containers/Project/Project.js +++ b/client/containers/Project/Project.js @@ -43,7 +43,7 @@ export default class Project extends Component { default={'动态'} data={[{ name: '接口', - path: `/project/${match.params.id}/interface` + path: `/project/${match.params.id}/interface/api` }, { name: '设置', path: `/project/${match.params.id}/setting` @@ -54,7 +54,7 @@ export default class Project extends Component { - + diff --git a/client/reducer/middleware/messageMiddleware.js b/client/reducer/middleware/messageMiddleware.js index 5c16bde7..f2ce0cf0 100644 --- a/client/reducer/middleware/messageMiddleware.js +++ b/client/reducer/middleware/messageMiddleware.js @@ -1,6 +1,7 @@ import { message } from 'antd'; export default () => next => action => { + if(!action) return; if (action.error) { message.error((action.payload && action.payload.message) || '服务器错误'); } else if (action.payload && action.payload.data && action.payload.data.errcode && action.payload.data.errcode !== 40011) { diff --git a/client/reducer/modules/interface.js b/client/reducer/modules/interface.js index 1aa3c799..fec2591a 100644 --- a/client/reducer/modules/interface.js +++ b/client/reducer/modules/interface.js @@ -2,7 +2,6 @@ import axios from 'axios' // Actions const FETCH_INTERFACE_DATA = 'yapi/interface/FETCH_INTERFACE_DATA'; const FETCH_INTERFACE_LIST = 'yapi/interface/FETCH_INTERFACE_LIST'; -const CHANGE_INTERFACE_ID = 'yapi/interface/CHANGE_INTERFACE_ID'; const ADD_INTERFACE_DATA = 'yapi/interface/ADD_INTERFACE_DATA'; const DELETE_INTERFACE_DATA = 'yapi/interface/DELETE_INTERFACE_DATA'; const UPDATE_INTERFACE_DATA = 'yapi/interface/UPDATE_INTERFACE_DATA'; @@ -11,7 +10,6 @@ const UPDATE_INTERFACE_DATA = 'yapi/interface/UPDATE_INTERFACE_DATA'; // Reducer const initialState = { - interfaceId: 0, curdata: {}, list: [] } @@ -26,20 +24,17 @@ export default (state = initialState, action) => { case DELETE_INTERFACE_DATA: return (() => { - let newlist = state.list.filter(data => data._id !== action.payload), newid, curdata; + let newlist = state.list.filter(data => data._id !== action.payload), curdata; - if (state.interfaceId === action.payload && state.list.length > 0) { - newid = state.list[0]._id + if (state.list.length > 0) { curdata = state.list[0] } else if (state.list.length == 0) { - newid = 0; curdata = {} } return { ...state, curdata: curdata, - interfaceId: newid, list: newlist } })() @@ -48,13 +43,7 @@ export default (state = initialState, action) => { return { ...state, curdata: action.payload, - list: [].concat(state.list, action.payload), - interfaceId: action.payload._id - } - case CHANGE_INTERFACE_ID: - return { - ...state, - interfaceId: action.payload + list: [].concat(state.list, action.payload) } case FETCH_INTERFACE_DATA: return { @@ -65,20 +54,13 @@ export default (state = initialState, action) => { return { ...state, list: action.payload.data, - curdata: action.payload.data.length > 0 ? action.payload.data[0] : {} + curdata: {} } default: return state } } -export function changeInterfaceId(id) { - return { - type: CHANGE_INTERFACE_ID, - payload: id - } -} - export function updateInterfaceData(updata) { return { @@ -94,13 +76,14 @@ export async function deleteInterfaceData(id) { await axios.post('/api/interface/del', { id: id }) return { type: DELETE_INTERFACE_DATA, - payload: id + payload: true } } // Action Creators export async function fetchInterfaceData(interfaceId) { let result = await axios.get('/api/interface/get?id=' + interfaceId); + return { type: FETCH_INTERFACE_DATA, payload: result.data From 4a181c563a07865ab1388ed95f4ad8c2932a42a5 Mon Sep 17 00:00:00 2001 From: "wenbo.dong" Date: Wed, 16 Aug 2017 20:31:59 +0800 Subject: [PATCH 2/9] =?UTF-8?q?feat:=20=E9=A1=B9=E7=9B=AE=E6=88=90?= =?UTF-8?q?=E5=91=98=E5=88=97=E8=A1=A8=E5=88=A0=E9=99=A4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UsernameAutoComplete.js | 2 +- .../containers/Group/MemberList/MemberList.js | 55 +++++++++++++------ client/reducer/modules/group.js | 13 ++++- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/client/components/UsernameAutoComplete/UsernameAutoComplete.js b/client/components/UsernameAutoComplete/UsernameAutoComplete.js index 40b07a22..55aef157 100644 --- a/client/components/UsernameAutoComplete/UsernameAutoComplete.js +++ b/client/components/UsernameAutoComplete/UsernameAutoComplete.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { AutoComplete } from 'antd'; -import axios from 'axios' +import axios from 'axios'; class UsernameAutoComplete extends Component { constructor(props) { diff --git a/client/containers/Group/MemberList/MemberList.js b/client/containers/Group/MemberList/MemberList.js index cafafb3d..5668128c 100644 --- a/client/containers/Group/MemberList/MemberList.js +++ b/client/containers/Group/MemberList/MemberList.js @@ -2,10 +2,10 @@ import React, { Component } from 'react'; import { Table } from 'antd'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { Select, Button, Modal, Row, Col, message } from 'antd'; +import { Select, Button, Modal, Row, Col, message, Popconfirm } from 'antd'; import './MemberList.scss'; import { autobind } from 'core-decorators'; -import { fetchGroupMemberList, fetchGroupMsg, addMember } from '../../../reducer/modules/group.js' +import { fetchGroupMemberList, fetchGroupMsg, addMember, delMember } from '../../../reducer/modules/group.js' import UsernameAutoComplete from '../../../components/UsernameAutoComplete/UsernameAutoComplete.js'; const Option = Select.Option; @@ -29,7 +29,8 @@ const arrayAddKey = (arr) => { { fetchGroupMemberList, fetchGroupMsg, - addMember + addMember, + delMember } ) class MemberList extends Component { @@ -50,6 +51,7 @@ class MemberList extends Component { fetchGroupMemberList: PropTypes.func, fetchGroupMsg: PropTypes.func, addMember: PropTypes.func, + delMember: PropTypes.func, role: PropTypes.string } @@ -58,12 +60,36 @@ class MemberList extends Component { console.log(`selected ${value}`); } - showModal = () => { + @autobind + showAddMemberModal() { this.setState({ visible: true }); } + @autobind + reFetchList() { + this.props.fetchGroupMemberList(this.props.currGroup._id).then((res) => { + this.setState({ + userInfo: arrayAddKey(res.payload.data.data), + visible: false + }); + }); + } + + @autobind + deleteConfirm(member_uid) { + return () => { + const id = this.props.currGroup._id; + this.props.delMember({ id, member_uid }).then((res) => { + if (!res.payload.data.errcode) { + message.success(res.payload.data.errmsg); + this.reFetchList(); // 添加成功后重新获取分组成员列表 + } + }); + } + } + @autobind handleOk() { console.log(this.props.currGroup._id, this.state.inputUid); @@ -74,13 +100,7 @@ class MemberList extends Component { console.log(res); if (!res.payload.data.errcode) { message.success('添加成功!'); - // 添加成功后重新获取分组成员列表 - this.props.fetchGroupMemberList(this.props.currGroup._id).then((res) => { - this.setState({ - userInfo: arrayAddKey(res.payload.data.data), - visible: false - }); - }); + this.reFetchList(); // 添加成功后重新获取分组成员列表 } }); } @@ -94,8 +114,10 @@ class MemberList extends Component { } @autobind - changeMemberRole(e) { - console.log(e); + changeMemberRole(value) { + return () => { + console.log(this.props.currGroup._id, value); + } } componentWillReceiveProps(nextProps) { @@ -136,7 +158,6 @@ class MemberList extends Component { } render() { - console.log(this.state); const columns = [{ title: this.props.currGroup.group_name + ' 分组成员 ('+this.state.userInfo.length + ') 人', dataIndex: 'username', @@ -148,7 +169,7 @@ class MemberList extends Component { ); } }, { - title: (this.state.role === 'owner' || this.state.role === 'admin') ?
: '', + title: (this.state.role === 'owner' || this.state.role === 'admin') ?
: '', key: 'action', className: 'member-opration', render: (text, record) => { @@ -159,7 +180,9 @@ class MemberList extends Component { - + + + + + ) + } +} diff --git a/client/containers/Project/Interface/InterfaceList/Run.js b/client/containers/Project/Interface/InterfaceList/Run/Run.js similarity index 95% rename from client/containers/Project/Interface/InterfaceList/Run.js rename to client/containers/Project/Interface/InterfaceList/Run/Run.js index e6445f57..b6d815d0 100644 --- a/client/containers/Project/Interface/InterfaceList/Run.js +++ b/client/containers/Project/Interface/InterfaceList/Run/Run.js @@ -1,12 +1,13 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio } from 'antd' +import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio, Tooltip } from 'antd' import { autobind } from 'core-decorators'; import crossRequest from 'cross-request'; import { withRouter } from 'react-router'; // import axios from 'axios'; import URL from 'url'; +import AddColModal from './AddColModal' // import { // } from '../../../reducer/modules/group.js' @@ -47,7 +48,8 @@ export default class Run extends Component { headers: [], currDomain: '', bodyType: '', - bodyOther: '' + bodyOther: '', + addColModalVisible: false } constructor(props) { @@ -399,17 +401,21 @@ export default class Run extends Component { - - + + + + + + @@ -541,6 +547,11 @@ export default class Run extends Component { + this.setState({addColModalVisible: false})} + onOk={null} + > ) } diff --git a/client/containers/Project/Interface/InterfaceList/Run.scss b/client/containers/Project/Interface/InterfaceList/Run/Run.scss similarity index 60% rename from client/containers/Project/Interface/InterfaceList/Run.scss rename to client/containers/Project/Interface/InterfaceList/Run/Run.scss index c2668768..faa90d45 100644 --- a/client/containers/Project/Interface/InterfaceList/Run.scss +++ b/client/containers/Project/Interface/InterfaceList/Run/Run.scss @@ -28,3 +28,20 @@ color: #108ee9; } } +.add-col-modal { + .col-list { + height: 200px; + overflow: auto; + margin: 7px 0 15px 0; + background: #eaeaea; + .col-item { + padding: 7px 10px 7px 10px; + } + .col-item:hover { + background: #fa0; + } + .col-item.selected { + background: #108ee9; + } + } +} From 0d3d8a251b7ab6433c86cd28047da766f95da22c Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Thu, 17 Aug 2017 11:24:46 +0800 Subject: [PATCH 5/9] feat: add websocket to solve interface edit and add addMember role and add case req_params --- .../containers/Project/Interface/Interface.js | 28 ++++---- .../Project/Interface/InterfaceList/Edit.js | 22 ++++++ package.json | 1 + server/app.js | 7 +- server/controllers/group.js | 5 +- server/controllers/interface.js | 11 +++ server/controllers/project.js | 4 +- server/models/interface.js | 1 + server/models/interfaceCase.js | 3 + server/websocket.js | 23 ++++++ server_dist/app.js | 12 +++- server_dist/controllers/group.js | 44 ++++++------ server_dist/controllers/interface.js | 40 +++++++++++ server_dist/controllers/project.js | 31 ++++---- server_dist/models/interface.js | 1 + server_dist/models/interfaceCase.js | 3 + server_dist/websocket.js | 70 +++++++++++++++++++ 17 files changed, 251 insertions(+), 55 deletions(-) create mode 100644 server/websocket.js create mode 100644 server_dist/websocket.js diff --git a/client/containers/Project/Interface/Interface.js b/client/containers/Project/Interface/Interface.js index a8a3a193..71a46aa0 100644 --- a/client/containers/Project/Interface/Interface.js +++ b/client/containers/Project/Interface/Interface.js @@ -11,22 +11,18 @@ import InterfaceContent from './InterfaceList/InterfaceContent.js' import InterfaceColMenu from './InterfaceCol/InterfaceColMenu.js' import InterfaceColContent from './InterfaceCol/InterfaceColContent.js' -class InterfaceRoute extends Component { - static propTypes = { - match: PropTypes.object - } - constructor(props){ - super(props) - } - render() { - let C, props = this.props; - if (props.match.params.action === 'api') { - C = InterfaceContent; - } else if (props.match.params.action === 'col') { - C = InterfaceColContent; - } - return +const InterfaceRoute = (props) => { + let C; + if (props.match.params.action === 'api') { + C = InterfaceContent; + } else if (props.match.params.action === 'col') { + C = InterfaceColContent; } + return +} + +InterfaceRoute.propTypes = { + match: PropTypes.object } @@ -42,7 +38,7 @@ class Interface extends Component { } } - onChange = (key)=>{ + onChange = (key) => { this.setState({ curkey: key }) diff --git a/client/containers/Project/Interface/InterfaceList/Edit.js b/client/containers/Project/Interface/InterfaceList/Edit.js index 698458d8..e0e2b3fb 100644 --- a/client/containers/Project/Interface/InterfaceList/Edit.js +++ b/client/containers/Project/Interface/InterfaceList/Edit.js @@ -44,6 +44,28 @@ class InterfaceEdit extends Component{ } } + componentWillMount(){ + let s = new WebSocket('ws://yapi.local.qunar.com:3000/api/interface/solve_conflict?id=1'); + s.onopen = (e)=>{ + console.log('open',e) + s.send('abc') + } + + s.onclose = (e)=>{ + console.log('close',e) + } + + s.onmessage = (e)=>{ + console.log('message',e) + } + + s.onerror = (e)=>{ + console.log('error',e) + } + + + } + render(){ return
diff --git a/package.json b/package.json index b148cc87..0972c053 100644 --- a/package.json +++ b/package.json @@ -64,6 +64,7 @@ "koa-session-minimal": "^3.0.3", "koa-static": "^3.0.0", "koa-views": "^5.2.0", + "koa-websocket": "^4.0.0", "mock": "^0.1.1", "mockjs": "^1.0.1-beta3", "moment": "^2.18.1", diff --git a/server/app.js b/server/app.js index b86b7cc2..28219a9a 100644 --- a/server/app.js +++ b/server/app.js @@ -7,9 +7,12 @@ import Koa from 'koa'; import koaStatic from 'koa-static'; import bodyParser from 'koa-bodyparser'; import router from './router.js'; +import websockify from 'koa-websocket'; +import websocket from './websocket.js' + yapi.connect = dbModule.connect(); -const app = new Koa(); +const app = websockify(new Koa()); let indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; @@ -18,7 +21,7 @@ app.use(bodyParser()); app.use(router.routes()); app.use(router.allowedMethods()); - +websocket(app); app.use( async (ctx, next) => { if( /^\/(?!api)[a-zA-Z0-9\/\-]*$/.test(ctx.path) ){ diff --git a/server/controllers/group.js b/server/controllers/group.js index 2f8935b1..a7158792 100644 --- a/server/controllers/group.js +++ b/server/controllers/group.js @@ -127,6 +127,7 @@ class groupController extends baseController { * @foldnumber 10 * @param {String} id 项目分组id * @param {String} member_uid 项目分组成员uid + * @param {String} role 成员角色,owner or dev * @returns {Object} * @example */ @@ -144,11 +145,13 @@ class groupController extends baseController { return ctx.body = yapi.commons.resReturn(null, 400, '分组id不能为空'); } + params.role = params.role === 'owner' ? 'owner' : 'dev'; + var check = await groupInst.checkMemberRepeat(params.id, params.member_uid); if (check > 0) { return ctx.body = yapi.commons.resReturn(null, 400, '成员已存在'); } - let groupUserdata = await this.getUserdata(params.member_uid); + let groupUserdata = await this.getUserdata(params.member_uid, params.role); if (groupUserdata === null) { return ctx.body = yapi.commons.resReturn(null, 400, '组长uid不存在') } diff --git a/server/controllers/interface.js b/server/controllers/interface.js index dca0cf79..197eeab6 100644 --- a/server/controllers/interface.js +++ b/server/controllers/interface.js @@ -304,6 +304,17 @@ class interfaceController extends baseController { ctx.body = yapi.commons.resReturn(null, 402, err.message); } } + + async solveConflict(ctx) { + let id = parseInt(ctx.query.id, 10); + if(!id) return ctx.websocket.send("id 参数有误"); + + ctx.websocket.send('Hello World'); + ctx.websocket.on('message', function (message) { + // do something with the message from client + console.log(message); + }); + } } module.exports = interfaceController; \ No newline at end of file diff --git a/server/controllers/project.js b/server/controllers/project.js index e07e7c7e..99d80d82 100644 --- a/server/controllers/project.js +++ b/server/controllers/project.js @@ -141,7 +141,9 @@ class projectController extends baseController { return ctx.body = yapi.commons.resReturn(null, 400, '项目成员已存在'); } - let userdata = await this.getUserdata(params.member_uid); + params.role = params.role === 'owner' ? 'owner' : 'dev'; + + let userdata = await this.getUserdata(params.member_uid, params.role); if(userdata === null){ return ctx.body = yapi.commons.resReturn(null, 400, '成员uid不存在') } diff --git a/server/models/interface.js b/server/models/interface.js index 9303fc30..3c3f12ee 100644 --- a/server/models/interface.js +++ b/server/models/interface.js @@ -13,6 +13,7 @@ class interfaceModel extends baseModel { path: { type: String, required: true }, method: { type: String, required: true }, project_id: { type: Number, required: true }, + edit_uid: {type: Number}, status: {type: String, enum: ['undone', 'done'], default: 'undone'}, desc: String, add_time: Number, diff --git a/server/models/interfaceCase.js b/server/models/interfaceCase.js index 86f8c4cd..0c851a4f 100644 --- a/server/models/interfaceCase.js +++ b/server/models/interfaceCase.js @@ -19,6 +19,9 @@ class interfaceCase extends baseModel { domain: {type: String }, path: { type: String }, method: { type: String }, + req_params: [{ + name: String, value: String + }], req_query: [{ name: String, value: String }], diff --git a/server/websocket.js b/server/websocket.js new file mode 100644 index 00000000..9b3a1dfa --- /dev/null +++ b/server/websocket.js @@ -0,0 +1,23 @@ +import koaRouter from 'koa-router'; +const route = require('koa-route'); +import interfaceController from './controllers/interface.js'; + + +function websocket(app) { + console.log('load websocket...') + app.ws.use(function (ctx, next) { + return next(ctx); + }); + app.ws.use(route.all('/api/interface/solve_conflict', async function (ctx) { + let inst = new interfaceController(ctx); + await inst.init(ctx); + if (inst.$auth === true) { + await inst.solveConflict.call(inst, ctx); + } else { + ctx.ws.send('请登录...'); + } + })); + +} + +module.exports = websocket \ No newline at end of file diff --git a/server_dist/app.js b/server_dist/app.js index 10be0ec7..4a1d40eb 100644 --- a/server_dist/app.js +++ b/server_dist/app.js @@ -40,13 +40,21 @@ var _router = require('./router.js'); var _router2 = _interopRequireDefault(_router); +var _koaWebsocket = require('koa-websocket'); + +var _koaWebsocket2 = _interopRequireDefault(_koaWebsocket); + +var _websocket = require('./websocket.js'); + +var _websocket2 = _interopRequireDefault(_websocket); + function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } _yapi2.default.commons = _commons2.default; _yapi2.default.connect = _db2.default.connect(); -var app = new _koa2.default(); +var app = (0, _koaWebsocket2.default)(new _koa2.default()); var indexFile = process.argv[2] === 'dev' ? 'dev.html' : 'index.html'; app.use(_mockServer2.default); @@ -54,6 +62,8 @@ app.use((0, _koaBodyparser2.default)()); app.use(_router2.default.routes()); app.use(_router2.default.allowedMethods()); +(0, _websocket2.default)(app); + app.use(function () { var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx, next) { return _regenerator2.default.wrap(function _callee$(_context) { diff --git a/server_dist/controllers/group.js b/server_dist/controllers/group.js index 602f57fc..7ad45555 100644 --- a/server_dist/controllers/group.js +++ b/server_dist/controllers/group.js @@ -325,6 +325,7 @@ var groupController = function (_baseController) { * @foldnumber 10 * @param {String} id 项目分组id * @param {String} member_uid 项目分组成员uid + * @param {String} role 成员角色,owner or dev * @returns {Object} * @example */ @@ -357,66 +358,69 @@ var groupController = function (_baseController) { return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '分组id不能为空')); case 6: - _context4.next = 8; + + params.role = params.role === 'owner' ? 'owner' : 'dev'; + + _context4.next = 9; return groupInst.checkMemberRepeat(params.id, params.member_uid); - case 8: + case 9: check = _context4.sent; if (!(check > 0)) { - _context4.next = 11; + _context4.next = 12; break; } return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员已存在')); - case 11: - _context4.next = 13; - return this.getUserdata(params.member_uid); + case 12: + _context4.next = 14; + return this.getUserdata(params.member_uid, params.role); - case 13: + case 14: groupUserdata = _context4.sent; if (!(groupUserdata === null)) { - _context4.next = 16; + _context4.next = 17; break; } return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '组长uid不存在')); - case 16: + case 17: if (!(groupUserdata._role === 'admin')) { - _context4.next = 18; + _context4.next = 19; break; } return _context4.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '不能邀请管理员')); - case 18: + case 19: delete groupUserdata._role; - _context4.prev = 19; - _context4.next = 22; + _context4.prev = 20; + _context4.next = 23; return groupInst.addMember(params.id, groupUserdata); - case 22: + case 23: result = _context4.sent; ctx.body = _yapi2.default.commons.resReturn(result); - _context4.next = 29; + _context4.next = 30; break; - case 26: - _context4.prev = 26; - _context4.t0 = _context4['catch'](19); + case 27: + _context4.prev = 27; + _context4.t0 = _context4['catch'](20); ctx.body = _yapi2.default.commons.resReturn(null, 402, _context4.t0.message); - case 29: + case 30: case 'end': return _context4.stop(); } } - }, _callee4, this, [[19, 26]]); + }, _callee4, this, [[20, 27]]); })); function addMember(_x5) { diff --git a/server_dist/controllers/interface.js b/server_dist/controllers/interface.js index 2e39a0c9..fedf23ff 100644 --- a/server_dist/controllers/interface.js +++ b/server_dist/controllers/interface.js @@ -589,6 +589,46 @@ var interfaceController = function (_baseController) { return del; }() + }, { + key: 'solveConflict', + value: function () { + var _ref6 = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee6(ctx) { + var id; + return _regenerator2.default.wrap(function _callee6$(_context6) { + while (1) { + switch (_context6.prev = _context6.next) { + case 0: + id = parseInt(ctx.query.id, 10); + + if (id) { + _context6.next = 3; + break; + } + + return _context6.abrupt('return', ctx.websocket.send("id 参数有误")); + + case 3: + + ctx.websocket.send('Hello World'); + ctx.websocket.on('message', function (message) { + // do something with the message from client + console.log(message); + }); + + case 5: + case 'end': + return _context6.stop(); + } + } + }, _callee6, this); + })); + + function solveConflict(_x6) { + return _ref6.apply(this, arguments); + } + + return solveConflict; + }() }]); return interfaceController; }(_base2.default); diff --git a/server_dist/controllers/project.js b/server_dist/controllers/project.js index facb459d..f0021295 100644 --- a/server_dist/controllers/project.js +++ b/server_dist/controllers/project.js @@ -313,25 +313,28 @@ var projectController = function (_baseController) { return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '项目成员已存在')); case 15: - _context2.next = 17; - return this.getUserdata(params.member_uid); - case 17: + params.role = params.role === 'owner' ? 'owner' : 'dev'; + + _context2.next = 18; + return this.getUserdata(params.member_uid, params.role); + + case 18: userdata = _context2.sent; if (!(userdata === null)) { - _context2.next = 20; + _context2.next = 21; break; } return _context2.abrupt('return', ctx.body = _yapi2.default.commons.resReturn(null, 400, '成员uid不存在')); - case 20: - _context2.prev = 20; - _context2.next = 23; + case 21: + _context2.prev = 21; + _context2.next = 24; return this.Model.addMember(params.id, userdata); - case 23: + case 24: result = _context2.sent; username = this.getUsername(); @@ -343,21 +346,21 @@ var projectController = function (_baseController) { typeid: params.id }); ctx.body = _yapi2.default.commons.resReturn(result); - _context2.next = 32; + _context2.next = 33; break; - case 29: - _context2.prev = 29; - _context2.t1 = _context2['catch'](20); + case 30: + _context2.prev = 30; + _context2.t1 = _context2['catch'](21); ctx.body = _yapi2.default.commons.resReturn(null, 402, _context2.t1.message); - case 32: + case 33: case 'end': return _context2.stop(); } } - }, _callee2, this, [[20, 29]]); + }, _callee2, this, [[21, 30]]); })); function addMember(_x2) { diff --git a/server_dist/models/interface.js b/server_dist/models/interface.js index ae7c4e6f..e596c40f 100644 --- a/server_dist/models/interface.js +++ b/server_dist/models/interface.js @@ -52,6 +52,7 @@ var interfaceModel = function (_baseModel) { path: { type: String, required: true }, method: { type: String, required: true }, project_id: { type: Number, required: true }, + edit_uid: { type: Number }, status: { type: String, enum: ['undone', 'done'], default: 'undone' }, desc: String, add_time: Number, diff --git a/server_dist/models/interfaceCase.js b/server_dist/models/interfaceCase.js index 785cb488..e75cc85d 100644 --- a/server_dist/models/interfaceCase.js +++ b/server_dist/models/interfaceCase.js @@ -58,6 +58,9 @@ var interfaceCase = function (_baseModel) { domain: { type: String }, path: { type: String }, method: { type: String }, + req_params: [{ + name: String, value: String + }], req_query: [{ name: String, value: String }], diff --git a/server_dist/websocket.js b/server_dist/websocket.js new file mode 100644 index 00000000..50a9b8f6 --- /dev/null +++ b/server_dist/websocket.js @@ -0,0 +1,70 @@ +'use strict'; + +var _regenerator = require('babel-runtime/regenerator'); + +var _regenerator2 = _interopRequireDefault(_regenerator); + +var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); + +var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); + +var _koaRouter = require('koa-router'); + +var _koaRouter2 = _interopRequireDefault(_koaRouter); + +var _interface = require('./controllers/interface.js'); + +var _interface2 = _interopRequireDefault(_interface); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } + +var route = require('koa-route'); + + +function websocket(app) { + console.log('load websocket...'); + app.ws.use(function (ctx, next) { + return next(ctx); + }); + app.ws.use(route.all('/api/interface/solve_conflict', function () { + var _ref = (0, _asyncToGenerator3.default)(_regenerator2.default.mark(function _callee(ctx) { + var inst; + return _regenerator2.default.wrap(function _callee$(_context) { + while (1) { + switch (_context.prev = _context.next) { + case 0: + inst = new _interface2.default(ctx); + _context.next = 3; + return inst.init(ctx); + + case 3: + if (!(inst.$auth === true)) { + _context.next = 8; + break; + } + + _context.next = 6; + return inst.solveConflict.call(inst, ctx); + + case 6: + _context.next = 9; + break; + + case 8: + ctx.ws.send('请登录...'); + + case 9: + case 'end': + return _context.stop(); + } + } + }, _callee, this); + })); + + return function (_x) { + return _ref.apply(this, arguments); + }; + }())); +} + +module.exports = websocket; \ No newline at end of file From ecc2ca518a352f2c0ba448db5850459e25710f5f Mon Sep 17 00:00:00 2001 From: "wenbo.dong" Date: Thu, 17 Aug 2017 11:31:57 +0800 Subject: [PATCH 6/9] =?UTF-8?q?docs:=20UsernameAutoComplete=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../UsernameAutoComplete.js | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/client/components/UsernameAutoComplete/UsernameAutoComplete.js b/client/components/UsernameAutoComplete/UsernameAutoComplete.js index 55aef157..ce0f4ed6 100644 --- a/client/components/UsernameAutoComplete/UsernameAutoComplete.js +++ b/client/components/UsernameAutoComplete/UsernameAutoComplete.js @@ -3,6 +3,37 @@ import PropTypes from 'prop-types'; import { AutoComplete } from 'antd'; import axios from 'axios'; +/** + * 用户名输入框自动完成组件 + * + * @component UsernameAutoComplete + * @examplelanguage js + * + * * 用户名输入框自动完成组件 + * * 用户名输入框自动完成组件 + * + * + */ + + /** + * 获取自动输入的用户信息 + * + * 获取子组件state + * @property callbackState + * @type function + * @description 类型提示:支持数组传值;也支持用函数格式化字符串:函数有两个参数(scale, index); + * 受控属性:滑块滑到某一刻度时所展示的刻度文本信息。如果不需要标签,请将该属性设置为 [] 空列表来覆盖默认转换函数。 + * @returns {object} {uid: xxx, username: xxx} + * @examplelanguage js + * @example + * onUserSelect(childState) { + * this.setState({ + * uid: childState.uid, + * username: childState.username + * }) + * } + * + */ class UsernameAutoComplete extends Component { constructor(props) { super(props); From e1c4f8c55ae80170407d68942c295bc087657e4b Mon Sep 17 00:00:00 2001 From: zwjamnsss Date: Thu, 17 Aug 2017 11:40:59 +0800 Subject: [PATCH 7/9] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=94=A8?= =?UTF-8?q?=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InterfaceList/Run/AddColModal.js | 14 +++++- .../Interface/InterfaceList/Run/Run.js | 44 +++++++++++++++++-- 2 files changed, 52 insertions(+), 6 deletions(-) diff --git a/client/containers/Project/Interface/InterfaceList/Run/AddColModal.js b/client/containers/Project/Interface/InterfaceList/Run/AddColModal.js index 6611e97c..b6de42fe 100644 --- a/client/containers/Project/Interface/InterfaceList/Run/AddColModal.js +++ b/client/containers/Project/Interface/InterfaceList/Run/AddColModal.js @@ -32,7 +32,8 @@ export default class AddColModal extends Component { visible: false, addColName: '', addColDesc: '', - id: 0 + id: 0, + caseName: '' } constructor(props) { @@ -71,9 +72,18 @@ export default class AddColModal extends Component { className="add-col-modal" title="添加到集合" visible={this.props.visible} - onOk={() => this.props.onOk(id)} + onOk={() => this.props.onOk(id, this.state.caseName)} onCancel={this.props.onCancel} > + +
接口用例名:
+ + this.setState({caseName: e.target.value})}> + +

请选择添加到的集合:

    { diff --git a/client/containers/Project/Interface/InterfaceList/Run/Run.js b/client/containers/Project/Interface/InterfaceList/Run/Run.js index b6d815d0..81595052 100644 --- a/client/containers/Project/Interface/InterfaceList/Run/Run.js +++ b/client/containers/Project/Interface/InterfaceList/Run/Run.js @@ -1,11 +1,11 @@ import React, { Component } from 'react' import PropTypes from 'prop-types' import { connect } from 'react-redux' -import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio, Tooltip } from 'antd' +import { Button, Input, Select, Card, Alert, Spin, Icon, Collapse, Radio, Tooltip, message } from 'antd' import { autobind } from 'core-decorators'; import crossRequest from 'cross-request'; import { withRouter } from 'react-router'; -// import axios from 'axios'; +import axios from 'axios'; import URL from 'url'; import AddColModal from './AddColModal' @@ -357,6 +357,42 @@ export default class Run extends Component { console.log(index) } + saveToCol = async (colId, caseName) => { + const project_id = this.props.match.params.id; + const { + currDomain: domain, + pathname: path, + method, + pathParam: req_params, + query: req_query, + headers: req_headers, + bodyType: req_body_type, + bodyForm: req_body_form, + bodyOther: req_body_other + } = this.state; + const res = await axios.post('/api/col/add_case', { + casename: caseName, + col_id: colId, + project_id, + env: '', + domain, + path, + method, + req_params, + req_query, + req_headers, + req_body_type, + req_body_form, + req_body_other + }); + if (res.data.errcode) { + message.error(res.data.errmsg) + } else { + message.success('添加成功') + this.setState({addColModalVisible: false}) + } + } + render () { const { method, domains, pathParam, pathname, query, headers, bodyForm, bodyOther, currDomain, bodyType } = this.state; @@ -409,7 +445,7 @@ export default class Run extends Component { loading={this.state.loading} >发送 - +
) From 92a1be59e545e01d9729938c2be85c7e0ff92dc9 Mon Sep 17 00:00:00 2001 From: suxiaoxin Date: Thu, 17 Aug 2017 12:03:55 +0800 Subject: [PATCH 8/9] fix: add koa-route --- client/containers/Project/Interface/InterfaceList/Edit.js | 2 ++ package.json | 1 + server/controllers/interface.js | 4 ++++ server/models/interface.js | 2 +- server_dist/controllers/interface.js | 6 +++++- server_dist/models/interface.js | 2 +- 6 files changed, 14 insertions(+), 3 deletions(-) diff --git a/client/containers/Project/Interface/InterfaceList/Edit.js b/client/containers/Project/Interface/InterfaceList/Edit.js index e0e2b3fb..221ff48a 100644 --- a/client/containers/Project/Interface/InterfaceList/Edit.js +++ b/client/containers/Project/Interface/InterfaceList/Edit.js @@ -49,6 +49,8 @@ class InterfaceEdit extends Component{ s.onopen = (e)=>{ console.log('open',e) s.send('abc') + //s.close() + s.send('aaaaa') } s.onclose = (e)=>{ diff --git a/package.json b/package.json index 0972c053..944111e1 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "koa-bodyparser": "^3.2.0", "koa-logger": "^3.0.0", "koa-mysql-session": "0.0.2", + "koa-route": "^3.2.0", "koa-router": "^7.0.1", "koa-send": "^3.2.0", "koa-session-minimal": "^3.0.3", diff --git a/server/controllers/interface.js b/server/controllers/interface.js index 197eeab6..8ac07ca6 100644 --- a/server/controllers/interface.js +++ b/server/controllers/interface.js @@ -314,6 +314,10 @@ class interfaceController extends baseController { // do something with the message from client console.log(message); }); + + ctx.websocket.on('close', function(){ + console.log('websocket: close') + }) } } diff --git a/server/models/interface.js b/server/models/interface.js index 3c3f12ee..c1e706f0 100644 --- a/server/models/interface.js +++ b/server/models/interface.js @@ -13,7 +13,7 @@ class interfaceModel extends baseModel { path: { type: String, required: true }, method: { type: String, required: true }, project_id: { type: Number, required: true }, - edit_uid: {type: Number}, + edit_uid: {type: Number, default: 0}, status: {type: String, enum: ['undone', 'done'], default: 'undone'}, desc: String, add_time: Number, diff --git a/server_dist/controllers/interface.js b/server_dist/controllers/interface.js index fedf23ff..c148d038 100644 --- a/server_dist/controllers/interface.js +++ b/server_dist/controllers/interface.js @@ -615,7 +615,11 @@ var interfaceController = function (_baseController) { console.log(message); }); - case 5: + ctx.websocket.on('close', function () { + console.log('websocket: close'); + }); + + case 6: case 'end': return _context6.stop(); } diff --git a/server_dist/models/interface.js b/server_dist/models/interface.js index e596c40f..f3a98dc1 100644 --- a/server_dist/models/interface.js +++ b/server_dist/models/interface.js @@ -52,7 +52,7 @@ var interfaceModel = function (_baseModel) { path: { type: String, required: true }, method: { type: String, required: true }, project_id: { type: Number, required: true }, - edit_uid: { type: Number }, + edit_uid: { type: Number, default: 0 }, status: { type: String, enum: ['undone', 'done'], default: 'undone' }, desc: String, add_time: Number, From 8b4327a4424c9bdb0bf363c3a3526c8db1a448b5 Mon Sep 17 00:00:00 2001 From: "wenbo.dong" Date: Thu, 17 Aug 2017 12:09:08 +0800 Subject: [PATCH 9/9] =?UTF-8?q?feat:=20=E7=A9=BA=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E7=BB=84=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../containers/Group/ProjectList/ProjectList.js | 9 ++++++--- .../Group/ProjectList/ProjectList.scss | 5 +++++ client/containers/Project/Project.js | 2 +- client/containers/Project/Setting/Setting.js | 17 +++++++++++++++++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/client/containers/Group/ProjectList/ProjectList.js b/client/containers/Group/ProjectList/ProjectList.js index 5828b950..e8c145a9 100644 --- a/client/containers/Group/ProjectList/ProjectList.js +++ b/client/containers/Group/ProjectList/ProjectList.js @@ -1,7 +1,7 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { connect } from 'react-redux'; -import { message, Row, Col } from 'antd'; +import { message, Row, Col, Icon } from 'antd'; import { addProject, fetchProjectList, delProject, changeUpdateModal, changeTableLoading } from '../../../reducer/modules/project'; import ProjectCard from '../../../components/ProjectCard/ProjectCard.js'; // import variable from '../../../constants/variable'; @@ -180,12 +180,15 @@ class ProjectList extends Component { return (
- {projectData.map((item, index) => { + {projectData.length ? projectData.map((item, index) => { return ( ); - })} + }) : (
+

该分组还没有项目呢

+

请点击右上角 “” 按钮添加项目

+
)}
); diff --git a/client/containers/Group/ProjectList/ProjectList.scss b/client/containers/Group/ProjectList/ProjectList.scss index ce02bda9..87271ab2 100644 --- a/client/containers/Group/ProjectList/ProjectList.scss +++ b/client/containers/Group/ProjectList/ProjectList.scss @@ -31,3 +31,8 @@ cursor: not-allowed; opacity: 0.5; } + +.empty-tip { + text-align: center; + font-size: .14rem; +} diff --git a/client/containers/Project/Project.js b/client/containers/Project/Project.js index dc7903c9..c7f1b3ab 100644 --- a/client/containers/Project/Project.js +++ b/client/containers/Project/Project.js @@ -6,7 +6,7 @@ import { Subnav } from '../../components/index' import { getProject } from '../../reducer/modules/project'; import Interface from './Interface/Interface.js' import Activity from './Activity/Activity.js' -import { Setting } from './Setting/Setting.js' +import Setting from './Setting/Setting.js' @connect( diff --git a/client/containers/Project/Setting/Setting.js b/client/containers/Project/Setting/Setting.js index e69de29b..2769cf24 100644 --- a/client/containers/Project/Setting/Setting.js +++ b/client/containers/Project/Setting/Setting.js @@ -0,0 +1,17 @@ +import React, { Component } from 'react' + +class Setting extends Component { + render () { + return ( +
+
+
+ t额st +
+
+
+ ) + } +} + +export default Setting