├── App ├── main │ ├── upload.scss │ ├── index.scss │ ├── register.scss │ ├── login.scss │ ├── upload.jsx │ ├── index.js │ ├── register.js │ ├── login.js │ └── router.js ├── user │ ├── redux │ │ ├── me.jpg │ │ ├── flogo.jpg │ │ └── Actions.js │ ├── index.js │ ├── bean.js │ ├── components │ │ ├── Nav.js │ │ └── PrdList.jsx │ └── router.js ├── tmp │ ├── index.js │ ├── profile.js │ └── bean.js ├── api │ ├── filter.js │ ├── output_preview.vue │ ├── vuex │ │ ├── store.js │ │ ├── getters.js │ │ ├── action.js │ │ └── mutations.js │ ├── editor │ │ ├── codemirror_alias.js │ │ ├── directive.js │ │ └── comment_input.vue │ ├── index.js │ ├── detail_preview.vue │ ├── bean.js │ ├── components │ │ ├── sub_slide_menu.vue │ │ └── slide_menu.vue │ ├── comment_input.vue │ ├── main.vue │ ├── category.vue │ └── java2json.vue ├── team │ ├── bean.js │ ├── routers.js │ ├── index.js │ ├── redux │ │ ├── configure_store.js │ │ ├── app.scss │ │ ├── reducer.js │ │ ├── app.js │ │ ├── Detail.js │ │ └── action.js │ └── components │ │ ├── Member.js │ │ ├── Team.js │ │ ├── InviteMember.jsx │ │ └── AddTeam.js ├── project │ ├── bean.js │ ├── index.js │ ├── redux │ │ ├── app.scss │ │ ├── configure_store.js │ │ ├── action.js │ │ ├── reducer.js │ │ └── app.js │ ├── components │ │ ├── Nav.js │ │ ├── Project.js │ │ └── AddProject.js │ └── router.js ├── iconfont │ ├── routers.js │ ├── index.js │ ├── bean.js │ ├── redux │ │ ├── configure_store.js │ │ ├── app.scss │ │ ├── reducer.js │ │ ├── app.js │ │ ├── Detail.js │ │ └── action.js │ └── components │ │ ├── Member.js │ │ ├── Team.js │ │ ├── InviteMember.jsx │ │ └── AddTeam.js ├── prd │ ├── index.js │ ├── redux │ │ ├── app.scss │ │ ├── configure_store.js │ │ ├── reducer.js │ │ ├── Actions.js │ │ └── app.js │ ├── bean.js │ ├── components │ │ └── Nav.js │ └── router.js ├── layout │ ├── menu.js │ ├── layout.scss │ └── layout.js ├── message │ ├── index.js │ ├── bean.js │ └── router.js ├── calendar │ ├── index.js │ ├── router.js │ └── calendar.vue ├── importdb │ ├── index.js │ ├── router.js │ └── import.vue └── main_vue │ ├── index.vue │ ├── index.scss │ └── top_nav.vue ├── assets └── avatar │ ├── jade.jpg │ ├── jade.png │ └── default.png ├── document ├── API管理平台操作手册.pdf └── API管理平台操作手册.docx ├── view ├── error.jade └── layout.jade ├── .eslintignore ├── common ├── db.js ├── app.js ├── vue_common.js └── message.js ├── .editorconfig ├── server.js ├── config_template.js ├── .eslintrc ├── .gitignore ├── socket ├── client.js └── server.js ├── README.md ├── package.json └── app.js /App/main/upload.scss: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /App/user/redux/me.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/App/user/redux/me.jpg -------------------------------------------------------------------------------- /App/user/redux/flogo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/App/user/redux/flogo.jpg -------------------------------------------------------------------------------- /assets/avatar/jade.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/assets/avatar/jade.jpg -------------------------------------------------------------------------------- /assets/avatar/jade.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/assets/avatar/jade.png -------------------------------------------------------------------------------- /document/API管理平台操作手册.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/document/API管理平台操作手册.pdf -------------------------------------------------------------------------------- /assets/avatar/default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/assets/avatar/default.png -------------------------------------------------------------------------------- /document/API管理平台操作手册.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwfe/fete/HEAD/document/API管理平台操作手册.docx -------------------------------------------------------------------------------- /view/error.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= message 5 | h2= error.status 6 | pre #{error.stack} 7 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | common 5 | layout 6 | main 7 | team 8 | user 9 | view 10 | pagination.vue 11 | plugins 12 | -------------------------------------------------------------------------------- /App/tmp/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-28 01:04:46 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 14:13:03 6 | */ 7 | 8 | 'use strict'; -------------------------------------------------------------------------------- /App/tmp/profile.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 15:27:49 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 17:09:46 6 | */ 7 | 8 | 'use strict'; -------------------------------------------------------------------------------- /App/main/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 14:11:06 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 14:12:24 6 | */ 7 | .mod-index { 8 | 9 | } -------------------------------------------------------------------------------- /common/db.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 15/6/30. 3 | * zhangyuyu@superjia.com 4 | */ 5 | var monk = require('monk'); 6 | 7 | import config from '../config'; 8 | 9 | var db = monk(config.mongodbConnection); 10 | 11 | module.exports = db; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /App/api/filter.js: -------------------------------------------------------------------------------- 1 | Vue.filter('exceptBy', (array, value) => { 2 | const ret = [] 3 | for (let i = 0, len = array.length; i < len; i++) { 4 | const item = array[i] 5 | if (item.name !== value) { 6 | ret.push(item) 7 | } 8 | } 9 | return ret 10 | }) 11 | -------------------------------------------------------------------------------- /common/app.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto') 2 | let md5sum = crypto.createHash('md5') 3 | md5sum.update('111111' + 'fete') //config.passwordKey 4 | md5sum = md5sum.digest('hex') 5 | 6 | console.log(crypto) 7 | // console.log(md5sum) 8 | // d04a711547058ed0efe27d8c5203f58f 9 | // d04a711547058ed0efe27d8c5203f58f 10 | 11 | -------------------------------------------------------------------------------- /App/user/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:37:38 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import { render } from 'react-dom'; 11 | import App from './redux/app'; 12 | render(, document.getElementById('main')) 13 | -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | require('babel-core/register')({ 3 | presets: ['es2015-node5', 'stage-0', 'react'] 4 | }); 5 | 6 | try{ 7 | var setIterm2Badge = require('set-iterm2-badge')('FETE SERVER'); 8 | }catch(e){ 9 | 10 | } 11 | 12 | var app = require('./app') 13 | 14 | console.log('服务已经启动....端口:3888'); 15 | 16 | // listen 17 | app.listen(3888); 18 | -------------------------------------------------------------------------------- /App/main/register.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-31 13:58:57 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-01 19:02:46 6 | */ 7 | #main { 8 | width: 435px; 9 | } 10 | .mod-register { 11 | width: 400px; 12 | margin: 60px auto; 13 | .title { 14 | text-align: center; 15 | } 16 | .form { 17 | margin-top: 20px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /App/main/login.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-31 13:58:57 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-01 19:02:46 6 | */ 7 | #main { 8 | width: 435px; 9 | } 10 | .mod-login { 11 | width: 400px; 12 | margin: 60px auto; 13 | .title { 14 | text-align: center; 15 | } 16 | .form { 17 | margin-top: 20px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /App/team/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:04:44 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-16 10:30:16 6 | */ 7 | 8 | 'use strict'; 9 | const team = { 10 | _id: '', 11 | id: '', //自己生成的团队id 12 | name: '', //团队名称 13 | description: '', //团队描述 14 | createUser: '', //创建人 15 | createTime: '', //创建时间 16 | updateTime: '' //更新时间 17 | } -------------------------------------------------------------------------------- /App/project/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:04:44 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-16 10:30:16 6 | */ 7 | 8 | 'use strict'; 9 | const project = { 10 | _id: '', 11 | id: '', //自己生成的项目id 12 | name: '', //项目名称 13 | description: '', //项目描述 14 | teamId: '', 15 | createUser: '', //创建人 16 | createTime: '', //创建时间 17 | updateTime: '' //更新时间 18 | } 19 | -------------------------------------------------------------------------------- /App/tmp/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:06:25 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 17:08:36 6 | */ 7 | 8 | 'use strict'; 9 | const user = { 10 | _id:'', 11 | username: '', 12 | password: '', 13 | teams: [{ 14 | id: '', 15 | role: 'owner|admin|member', //owner:创建者,admin:管理员,member:普通成员 16 | status: 'normal|invite|apply'//normal:正常,invited:邀请加入,apply:申请加入 17 | }] 18 | } 19 | -------------------------------------------------------------------------------- /App/team/routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/7/2. 3 | * zhangyuyu@superjia.com 4 | */ 5 | 6 | 'use strict'; 7 | import React from 'react'; 8 | import { IndexRoute, Route, Router } from 'react-router'; 9 | import App from './redux/app'; 10 | import Detail from './redux/Detail'; 11 | 12 | export default ( 13 |
14 | 15 | 16 | 17 | 18 |
19 | ); 20 | -------------------------------------------------------------------------------- /App/iconfont/routers.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/7/2. 3 | * zhangyuyu@superjia.com 4 | */ 5 | 6 | 'use strict'; 7 | import React from 'react'; 8 | import { IndexRoute, Route, Router } from 'react-router'; 9 | import App from './redux/app'; 10 | import Detail from './redux/Detail'; 11 | 12 | export default ( 13 |
14 | 15 | 16 | 17 | 18 |
19 | ); 20 | -------------------------------------------------------------------------------- /App/api/output_preview.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 24 | 25 | 27 | -------------------------------------------------------------------------------- /App/prd/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:37:38 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import { render } from 'react-dom'; 11 | import { Provider } from 'react-redux'; 12 | import App from './redux/app'; 13 | import configureStore from './redux/configure_store'; 14 | 15 | const store = configureStore() 16 | 17 | render( 18 | 19 | 20 | , 21 | document.getElementById('main') 22 | ) 23 | -------------------------------------------------------------------------------- /App/project/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:37:38 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import { render } from 'react-dom'; 11 | import { Provider } from 'react-redux'; 12 | import App from './redux/app'; 13 | import configureStore from './redux/configure_store'; 14 | 15 | const store = configureStore() 16 | 17 | render( 18 | 19 | 20 | , 21 | document.getElementById('main') 22 | ) 23 | -------------------------------------------------------------------------------- /App/user/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:04:44 4 | * @Last modified by: lancui 5 | * @Last modified time: 2016-08-15 11:08:76 6 | */ 7 | 8 | 'use strict'; 9 | const user = { 10 | _id:'', 11 | username: '', // 登录名 12 | password: '', // 密码 13 | sex: '', // 性别 14 | role: '', // 角色 15 | phone: '', // 手机号 16 | message: '', // 消息接收 默认yes 17 | img: '', // 头像url 18 | teams: [{ 19 | id: '', 20 | role: 'owner|admin|member', //owner:创建者,admin:管理员,member:普通成员 21 | status: 'normal|invite|apply'//normal:正常,invited:邀请加入,apply:申请加入 22 | }] 23 | } 24 | -------------------------------------------------------------------------------- /App/layout/menu.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 12:13:11 4 | * @Last modified by: lancui 5 | * @Last modified time: 2016-06-29 17:06:63 6 | */ 7 | 8 | 'use strict'; 9 | const menus = [{ 10 | key: 'index', 11 | text: '首页', 12 | link: '/' 13 | }, { 14 | key: 'team', 15 | text: '团队', 16 | link: '/team', 17 | isVueLink: false 18 | }, { 19 | key: 'api', 20 | text: 'API', 21 | link: '/api/all', 22 | isVueLink: false 23 | }, { 24 | key: 'calendar', 25 | text: '日历', 26 | link: '/calendar', 27 | isVueLink: false 28 | }]; 29 | 30 | export default menus; 31 | -------------------------------------------------------------------------------- /App/team/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:37:38 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import {render} from 'react-dom'; 11 | import {Provider} from 'react-redux'; 12 | import { 13 | ReduxRouter, 14 | } from 'redux-router'; 15 | import configureStore from './redux/configure_store'; 16 | import routers from './routers' 17 | 18 | const store = configureStore() 19 | 20 | render( 21 | 22 | 23 | , 24 | document.getElementById('main') 25 | ) 26 | -------------------------------------------------------------------------------- /App/iconfont/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:37:38 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import {render} from 'react-dom'; 11 | import {Provider} from 'react-redux'; 12 | import { 13 | ReduxRouter, 14 | } from 'redux-router'; 15 | import configureStore from './redux/configure_store'; 16 | import routers from './routers' 17 | 18 | const store = configureStore() 19 | 20 | render( 21 | 22 | 23 | , 24 | document.getElementById('main') 25 | ) 26 | -------------------------------------------------------------------------------- /App/api/vuex/store.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: geyuanjun 3 | * @Date: 2016-06-29 11:33:13 4 | * @Email: geyuanjun.sh@superjia.com 5 | * @Last modified by: geyuanjun 6 | * @Last modified time: 2016-07-12 10:46:48 7 | */ 8 | 9 | import Vuex from 'vuex' 10 | import mutations from './mutations' 11 | Vue.use(Vuex) 12 | const state = { 13 | list: [], 14 | list_active: {}, 15 | userId: '123', 16 | prdId: '', 17 | teamId: '', 18 | projectId: '', 19 | apiRoot: '', 20 | prdList: [], 21 | categories: [], 22 | cate_active: '全部', 23 | originPrd: '', 24 | exceptMePrdData: [] 25 | } 26 | 27 | export default new Vuex.Store({ 28 | state, 29 | mutations 30 | }) 31 | -------------------------------------------------------------------------------- /App/message/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-22 14:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-14 18:07:55 7 | */ 8 | 9 | const router = vueCommon.createRouter(); 10 | router.map({ 11 | '/': { 12 | name: 'index', 13 | component: require('../main_vue/index.vue'), 14 | subRoutes: { 15 | message: { 16 | name: 'message', 17 | component: require('./message.vue') 18 | } 19 | } 20 | } 21 | }); 22 | 23 | document.getElementById('fete').innerHTML = ''; 24 | const app = Vue.extend(); 25 | router.start(app, '#fete'); 26 | -------------------------------------------------------------------------------- /App/calendar/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-22 14:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-14 18:07:55 7 | */ 8 | 9 | const router = vueCommon.createRouter(); 10 | router.map({ 11 | '/': { 12 | name: 'index', 13 | component: require('../main_vue/index.vue'), 14 | subRoutes: { 15 | calendar: { 16 | name: 'calendar', 17 | component: require('./calendar.vue') 18 | } 19 | } 20 | } 21 | }); 22 | 23 | document.getElementById('fete').innerHTML = ''; 24 | const app = Vue.extend(); 25 | router.start(app, '#fete'); 26 | -------------------------------------------------------------------------------- /App/importdb/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-22 14:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-14 18:07:66 7 | */ 8 | 9 | const router = vueCommon.createRouter(); 10 | router.map({ 11 | '/': { 12 | name: 'index', 13 | component: require('../main_vue/index.vue'), 14 | subRoutes: { 15 | importdb: { 16 | name: 'importdb', 17 | component: require('./import.vue') 18 | } 19 | } 20 | } 21 | }); 22 | 23 | document.getElementById('fete').innerHTML = ''; 24 | const app = Vue.extend(); 25 | router.start(app, '#fete'); 26 | -------------------------------------------------------------------------------- /App/api/editor/codemirror_alias.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: chenjiangsong 3 | * @Date: 2016-07-11 18:07:00 4 | * @Email: chenjiangsong.sh@superjia.com 5 | * @Last modified by: chenjiangsong 6 | * @Last modified time: 2016-07-11 19:07:03 7 | */ 8 | 9 | const CodeMirror = require('codemirror/lib/codemirror.js'); 10 | require('codemirror/lib/codemirror.css'); 11 | require('codemirror/addon/lint/lint.css'); 12 | require('codemirror/mode/javascript/javascript.js'); 13 | require('codemirror/mode/css/css.js'); 14 | require('jsonlint/lib/jsonlint.js'); 15 | require('codemirror/addon/lint/lint.js'); 16 | require('codemirror/addon/lint/json-lint.js'); 17 | require('codemirror/addon/selection/active-line.js') 18 | -------------------------------------------------------------------------------- /config_template.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-29 17:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-15 14:07:51 7 | */ 8 | 9 | 10 | 11 | /** 12 | * Created by zyy on 15/6/26. 13 | * zhangyuyu@superjia.com 14 | */ 15 | module.exports = { 16 | env: 'dev', 17 | passwordKey: 'fete', 18 | authKey: 'fete', 19 | host: 'http://localhost:3810', 20 | mongodbConnection: '192.168.1.46/fete', 21 | hostIp: 'localhost', 22 | socketPort: 3000, 23 | // redis: { 24 | // host: "localhost", 25 | // //pass: '', 26 | // port: 6379, 27 | // db: "0" 28 | // } 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /App/layout/layout.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-31 14:07:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-16 11:42:09 6 | */ 7 | html,body { 8 | background-color: #ececec; 9 | min-height: 100%; 10 | } 11 | #fete { 12 | min-height: 100%; 13 | } 14 | #header { 15 | background-color: #fff; 16 | border-bottom: 1px solid #e9e9e9; 17 | box-shadow: 0 1px 6px hsla(0,0%,39%,.2); 18 | } 19 | .header-wrap { 20 | width: 1200px; 21 | margin: 0 auto; 22 | .ant-menu-horizontal { 23 | border-bottom: none; 24 | } 25 | } 26 | #main { 27 | width: 1200px; 28 | min-height: 400px; 29 | margin: 10px auto; 30 | padding: 16px; 31 | background-color: #fff; 32 | } -------------------------------------------------------------------------------- /common/vue_common.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: chenjiangsong 3 | * @Date: 2016-07-01 16:07:00 4 | * @Email: chenjiangsong.sh@superjia.com 5 | * @Last modified by: chenjiangsong 6 | * @Last modified time: 2016-07-05 21:07:82 7 | */ 8 | 9 | // vue 配置项目公共 10 | Vue.use(VueRouter); 11 | 12 | module.exports = { 13 | createRouter: function (params) { 14 | let defaultOpts = { 15 | history: true, 16 | saveScrollPosition: true 17 | }; 18 | 19 | if (typeof params === 'string') { 20 | defaultOpts.root = params; 21 | } else if (typeof params === 'object') { 22 | defaultOpts = $.extend({}, defaultOpts, params); 23 | } 24 | 25 | return new VueRouter(defaultOpts); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb", 3 | "env": { 4 | "browser": true, 5 | "node": true, 6 | "es6": true 7 | }, 8 | "rules": { 9 | "semi": 0, 10 | "comma-dangle": 0, 11 | "global-require": 0, 12 | "no-alert": 0, 13 | "no-console": 0, 14 | "no-param-reassign": 0, 15 | "max-len": 0, 16 | "func-names": 0, 17 | "no-underscore-dangle": 0, 18 | "no-unused-vars": 1, 19 | "object-shorthand": 1, 20 | "arrow-body-style": 1, 21 | "no-new": 0, 22 | "no-unused-expressions": 0 23 | }, 24 | "globals": { 25 | "$": true, 26 | "_": true, 27 | "Vue": true, 28 | "vueCommon": true, 29 | "pageConfig": true, 30 | "CodeMirror": true, 31 | "socket": true, 32 | "toastr": true, 33 | "Clipboard": true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /App/project/redux/app.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-15 22:44:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 23:27:53 6 | */ 7 | .mod-project { 8 | .project-nav { 9 | margin: 0 -16px 16px; 10 | padding: 0 16px 16px; 11 | border-bottom: 1px solid #e9e9e9; 12 | } 13 | .project-add { 14 | cursor: pointer; 15 | text-align: center; 16 | .anticon-plus { 17 | font-size: 60px; 18 | } 19 | } 20 | .ant-card-body { 21 | height: 100px; 22 | } 23 | .gutter-row { 24 | margin-bottom: 16px; 25 | } 26 | } 27 | .project { 28 | .description { 29 | height: 30px; 30 | } 31 | .operators { 32 | display: none; 33 | float: right; 34 | } 35 | &:hover .operators { 36 | display: block; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /App/iconfont/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:04:44 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-16 10:30:16 6 | */ 7 | 8 | 'use strict'; 9 | const iconfont = { 10 | _id: '', 11 | id: '', //自己生成的iconfontid 12 | name: '', //名称 13 | className: '', //className 14 | code: '', //code 15 | filePath: '', //存储路径 16 | teamId: '', //属于哪个团队,是主团队 17 | createUser: '', //创建人 18 | createTime: '', //创建时间 19 | updateTime: '' //更新时间 20 | } 21 | 22 | //iconfont team team,主从团队关系 23 | const iconfontTeam = { 24 | _id: '', 25 | teamId: '',//主团队id 26 | childTeamId: '', //附属团队id 27 | } 28 | 29 | //iconfont中的iconfont,主要是附属团队 30 | const teamIconfont = { 31 | _id: '', 32 | teamId: '', //team id 33 | iconfontId: '',//iconfont id 34 | } 35 | -------------------------------------------------------------------------------- /common/message.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 15/6/26. 3 | * zhangyuyu@superjia.com 4 | */ 5 | module.exports = { 6 | //系统错误码 7 | 1001: '暂无数据', 8 | 1002: '没有权限', 9 | 1003: '请求参数错误', 10 | 1004: '无效操作', 11 | //10xxx和user相关 12 | 10001: '请先登录', 13 | 10002: '该用户已经存在', 14 | 10003: '该邮箱已经存在', 15 | 10004: '该用户不存在', 16 | 10005: '密码不正确', 17 | //11xxx和team相关 18 | 11001: '请先加入该团队', 19 | 11002: '只有团队创建者才能管理该团队', 20 | 11003: '该团队不存在', 21 | //12xxx和project相关 22 | 12001: '只有项目创建者才能该管理项目', 23 | 12002: '该项目不存在', 24 | //13xxx和prd相关 25 | 13001: '只有项目创建者才能该管理PRD', 26 | 13002: '该PRD不存在', 27 | //15XXXX api 相关 28 | 150001: '添加API失败', 29 | 150002: '删除API失败', 30 | 150003: 'API不存在', 31 | 150004: '同步PRD失败', 32 | 150005: '同步API失败' 33 | } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Directory for instrumented libs generated by jscoverage/JSCover 12 | lib-cov 13 | 14 | # Coverage directory used by tools like istanbul 15 | coverage 16 | 17 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 18 | .grunt 19 | 20 | # node-waf configuration 21 | .lock-wscript 22 | 23 | # Compiled binary addons (http://nodejs.org/api/addons.html) 24 | build/Release 25 | 26 | # Dependency directory 27 | node_modules 28 | 29 | # Optional npm cache directory 30 | .npm 31 | 32 | .idea 33 | .vscode 34 | .tmp 35 | 36 | # Optional REPL history 37 | .node_repl_history 38 | 39 | dist 40 | 41 | config.js 42 | 43 | semantic 44 | 45 | semantic.json 46 | 47 | backup_db.sh 48 | -------------------------------------------------------------------------------- /App/prd/redux/app.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-15 22:44:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 23:27:53 6 | */ 7 | .mod-prd { 8 | .prd-nav { 9 | margin: 0 -16px 16px; 10 | padding: 0 16px 16px; 11 | border-bottom: 1px solid #e9e9e9; 12 | } 13 | .prd-add { 14 | cursor: pointer; 15 | text-align: center; 16 | .anticon-plus { 17 | font-size: 100px; 18 | } 19 | } 20 | .ant-card-body { 21 | height: 150px; 22 | } 23 | .gutter-row { 24 | margin-bottom: 16px; 25 | } 26 | } 27 | .prd { 28 | .description { 29 | height: 70px; 30 | } 31 | .operators { 32 | display: none; 33 | float: right; 34 | } 35 | &:hover .operators { 36 | display: block; 37 | } 38 | } 39 | .ant-form-item { 40 | margin-bottom: 10px; 41 | } 42 | -------------------------------------------------------------------------------- /App/api/vuex/getters.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: geyuanjun 3 | * @Date: 2016-07-11 17:42:28 4 | * @Email: geyuanjun.sh@superjia.com 5 | * @Last modified by: geyuanjun 6 | * @Last modified time: 2016-07-12 10:54:0 7 | */ 8 | export const list = state => state.list 9 | export const listActive = state => state.list_active 10 | export const userId = state => state.userId 11 | export const prdId = state => state.prdId 12 | export const projectId = state => state.projectId 13 | export const teamId = state => state.teamId 14 | export const listIndex = state => state.list.indexOf(state.list_active) 15 | export const apiRoot = state => state.apiRoot 16 | export const prdList = state => state.prdList 17 | export const categories = state => state.categories 18 | export const cateActive = state => state.cate_active 19 | export const originPrd = state => state.originPrd 20 | export const exceptMePrdData = state => state.exceptMePrdData 21 | -------------------------------------------------------------------------------- /socket/client.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-27 17:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-20 14:07:34 7 | */ 8 | const socket = require('socket.io-client')(pageConfig.socketConnection); 9 | 10 | socket.on('getUserId', (data) => { 11 | socket.emit('addToUserSocketMap', pageConfig.me.username); 12 | }); 13 | socket.on('updateMsgs', (data) => { 14 | $('.msg-count').html(data.count); 15 | if (data.isToastr) { 16 | toastr.success('您有新的消息!', '', { 17 | onclick: function () { 18 | location.href = '/message'; 19 | } 20 | }); 21 | } 22 | }); 23 | 24 | 25 | // let msgSocketTime = null; 26 | // const getMsgTask = () => { 27 | // if (!msgSocketTime) clearTimeout(msgSocketTime); 28 | // socket.emit('sendMsgs', pageConfig.me.username); 29 | // msgSocketTime = setTimeout(getMsgTask, 5000); 30 | // }; 31 | // getMsgTask(); 32 | -------------------------------------------------------------------------------- /App/prd/redux/configure_store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:51:22 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:38:11 6 | */ 7 | 8 | 'use strict'; 9 | import { createStore, applyMiddleware } from 'redux' 10 | import thunkMiddleware from 'redux-thunk' 11 | import createLogger from 'redux-logger' 12 | import rootReducer from './reducer' 13 | 14 | const createStoreWithMiddleware = applyMiddleware( 15 | thunkMiddleware, 16 | createLogger() 17 | )(createStore) 18 | 19 | export default function configureStore(initialState) { 20 | const store = createStoreWithMiddleware(rootReducer, initialState) 21 | 22 | // if (module.hot) { 23 | // // Enable Webpack hot module replacement for reducers 24 | // module.hot.accept('../reducers', () => { 25 | // const nextRootReducer = require('../reducers') 26 | // store.replaceReducer(nextRootReducer) 27 | // }) 28 | // } 29 | 30 | return store 31 | } 32 | -------------------------------------------------------------------------------- /App/project/redux/configure_store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:51:22 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:38:11 6 | */ 7 | 8 | 'use strict'; 9 | import { createStore, applyMiddleware } from 'redux' 10 | import thunkMiddleware from 'redux-thunk' 11 | import createLogger from 'redux-logger' 12 | import rootReducer from './reducer' 13 | 14 | const createStoreWithMiddleware = applyMiddleware( 15 | thunkMiddleware, 16 | createLogger() 17 | )(createStore) 18 | 19 | export default function configureStore(initialState) { 20 | const store = createStoreWithMiddleware(rootReducer, initialState) 21 | 22 | // if (module.hot) { 23 | // // Enable Webpack hot module replacement for reducers 24 | // module.hot.accept('../reducers', () => { 25 | // const nextRootReducer = require('../reducers') 26 | // store.replaceReducer(nextRootReducer) 27 | // }) 28 | // } 29 | 30 | return store 31 | } 32 | -------------------------------------------------------------------------------- /App/api/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-22 14:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-08-18 13:08:12 7 | */ 8 | 9 | 10 | const router = vueCommon.createRouter(); 11 | router.map({ 12 | '': { 13 | name: 'main', 14 | component: require('./main.vue'), 15 | subRoutes: { 16 | '/api': { 17 | name: 'list', 18 | component: require('./main_list.vue') 19 | }, 20 | '/api/all': { 21 | name: 'list', 22 | component: require('./main_list.vue') 23 | }, 24 | '/api/j2j': { 25 | name: 'java2json', 26 | component: require('./java2json.vue') 27 | }, 28 | '/api/detail_preview': { 29 | name: 'detail_preview', 30 | component: require('./detail_preview.vue') 31 | } 32 | } 33 | } 34 | }); 35 | 36 | document.getElementById('fete').innerHTML = ''; 37 | const app = Vue.extend(); 38 | router.start(app, '#fete'); 39 | -------------------------------------------------------------------------------- /App/api/detail_preview.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 41 | -------------------------------------------------------------------------------- /App/prd/bean.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-06 17:04:44 4 | * @Last modified by: lancui 5 | * @Last modified time: 2016-08-15 11:08:76 6 | */ 7 | 8 | 'use strict'; 9 | const prd = { 10 | _id: '', 11 | id: '', //自己生成的prdid 12 | name: '', //prd名称 13 | description: '', //prd描述 14 | pm: '', //产品经理 15 | developer: '', // 开发人员 16 | type: '', //prd类型 17 | selfTest: false,//是否自测 18 | jira: '',//jira地址 19 | svn: '',//svn地址 20 | comment: '',//备注 21 | mrdTime: '',//mrd时间 22 | prdTime: '',//prd时间 23 | devTime: '', //开发时间 24 | apiTime: '', //联调时间 25 | testTime: '', //提测时间 26 | betaTime: '', //beta环境时间 27 | onlineTime: '', //上线时间 28 | projectId: '',//项目id 29 | teamId: '', //团队id 30 | createUser: '', //创建人 31 | createTime: '', //创建时间 32 | updateTime: '', //更新时间 33 | mergeMaster: false // 是否合到 master分支 1:是,2:否 34 | /* 35 | prjName: '', 36 | prjDir: '', //目录名,英文名 37 | members: [{ 38 | username: '', 39 | startTime: '', 40 | endTime: '' 41 | }], 42 | isTest: '', //是否提测 43 | isBeta: '', 44 | isOnline: '', 45 | isMaster: '' 46 | } 47 | 48 | */ 49 | } 50 | -------------------------------------------------------------------------------- /App/team/redux/configure_store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:51:22 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:38:11 6 | */ 7 | 8 | 'use strict'; 9 | import { createStore, compose, applyMiddleware } from 'redux'; 10 | import { 11 | reduxReactRouter, 12 | } from 'redux-router'; 13 | import { createHistory } from 'history'; 14 | import thunkMiddleware from 'redux-thunk' 15 | import createLogger from 'redux-logger' 16 | import rootReducer from './reducer'; 17 | 18 | 19 | const createStoreWithMiddleware = compose( 20 | applyMiddleware( 21 | thunkMiddleware, 22 | createLogger() 23 | ), 24 | reduxReactRouter({ createHistory }) 25 | // devTools() 26 | )(createStore); 27 | 28 | export default function configureStore(initialState) { 29 | const store = createStoreWithMiddleware(rootReducer, initialState) 30 | 31 | // if (module.hot) { 32 | // // Enable Webpack hot module replacement for reducers 33 | // module.hot.accept('../reducers', () => { 34 | // const nextRootReducer = require('../reducers') 35 | // store.replaceReducer(nextRootReducer) 36 | // }) 37 | // } 38 | 39 | return store 40 | } 41 | -------------------------------------------------------------------------------- /App/iconfont/redux/configure_store.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:51:22 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 22:38:11 6 | */ 7 | 8 | 'use strict'; 9 | import { createStore, compose, applyMiddleware } from 'redux'; 10 | import { 11 | reduxReactRouter, 12 | } from 'redux-router'; 13 | import { createHistory } from 'history'; 14 | import thunkMiddleware from 'redux-thunk' 15 | import createLogger from 'redux-logger' 16 | import rootReducer from './reducer'; 17 | 18 | 19 | const createStoreWithMiddleware = compose( 20 | applyMiddleware( 21 | thunkMiddleware, 22 | createLogger() 23 | ), 24 | reduxReactRouter({ createHistory }) 25 | // devTools() 26 | )(createStore); 27 | 28 | export default function configureStore(initialState) { 29 | const store = createStoreWithMiddleware(rootReducer, initialState) 30 | 31 | // if (module.hot) { 32 | // // Enable Webpack hot module replacement for reducers 33 | // module.hot.accept('../reducers', () => { 34 | // const nextRootReducer = require('../reducers') 35 | // store.replaceReducer(nextRootReducer) 36 | // }) 37 | // } 38 | 39 | return store 40 | } 41 | -------------------------------------------------------------------------------- /App/iconfont/redux/app.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-15 22:44:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 23:27:53 6 | */ 7 | .mod-iconfont { 8 | .iconfont-add { 9 | cursor: pointer; 10 | text-align: center; 11 | .anticon-plus { 12 | font-size: 100px; 13 | } 14 | } 15 | .ant-card-body { 16 | height: 150px; 17 | } 18 | .gutter-row { 19 | margin-bottom: 16px; 20 | } 21 | } 22 | 23 | .iconfont { 24 | .description { 25 | height: 70px; 26 | } 27 | .operators { 28 | display: none; 29 | float: right; 30 | } 31 | &:hover .operators { 32 | display: block; 33 | } 34 | } 35 | 36 | .mod-detail { 37 | .detail-title { 38 | margin: 0 -16px 16px; 39 | padding: 0 16px 16px; 40 | border-bottom: 1px solid #e9e9e9; 41 | } 42 | .member { 43 | position: relative; 44 | .operators { 45 | display: none; 46 | float: right; 47 | } 48 | &:hover .operators { 49 | display: block; 50 | } 51 | } 52 | .member-invite { 53 | cursor: pointer; 54 | text-align: center; 55 | } 56 | .anticon-plus { 57 | font-size: 40px; 58 | } 59 | .gutter-row { 60 | margin-bottom: 16px; 61 | } 62 | .ant-tag{ 63 | position: absolute; 64 | top: 10px; 65 | right: 10px; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /App/main/upload.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component, PropTypes} from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | import classNames from 'classnames'; 5 | import {Link} from 'react-router'; 6 | import Upload from 'antd/lib/upload'; 7 | import Button from 'antd/lib/button'; 8 | import Message from 'antd/lib/message'; 9 | import Icon from 'antd/lib/icon'; 10 | 11 | class UploadV extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | } 15 | 16 | static propTypes = { 17 | } 18 | 19 | render() { 20 | const props = { 21 | name: 'file', 22 | action: '/upload', 23 | onChange(info) { 24 | console.log(info) 25 | if (info.file.status !== 'uploading') { 26 | console.log(info.file, info.fileList); 27 | } 28 | if (info.file.status === 'done') { 29 | Message.success(`${info.file.name} 上传成功。`); 30 | } else if (info.file.status === 'error') { 31 | Message.error(`${info.file.name} 上传失败。`); 32 | } 33 | }, 34 | }; 35 | return ( 36 |
37 | 38 | 41 | 42 |
43 | ) 44 | } 45 | } 46 | 47 | ReactDOM.render( 48 | 49 | 50 | , document.getElementById('main')); 51 | -------------------------------------------------------------------------------- /App/team/components/Member.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/7/4. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import React, {Component, PropTypes} from 'react'; 6 | import classNames from 'classnames'; 7 | import Card from 'antd/lib/card'; 8 | import Button from 'antd/lib/button'; 9 | import Tag from 'antd/lib/tag'; 10 | 11 | export default class Team extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | } 15 | 16 | render() { 17 | const {member, actions} = this.props; 18 | return ( 19 |
24 | 25 |
30 | { 31 | member.team.status === 'invite' ? 待加入 : null 32 | } 33 |

{member.username}

34 |
35 | 36 |
37 |
38 |
39 |
40 | ) 41 | } 42 | } 43 | 44 | Team.propTypes = { 45 | member: PropTypes.object.isRequired, 46 | } 47 | -------------------------------------------------------------------------------- /App/iconfont/components/Member.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/7/4. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import React, {Component, PropTypes} from 'react'; 6 | import classNames from 'classnames'; 7 | import Card from 'antd/lib/card'; 8 | import Button from 'antd/lib/button'; 9 | import Tag from 'antd/lib/tag'; 10 | 11 | export default class Iconfont extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | } 15 | 16 | render() { 17 | const {member, actions} = this.props; 18 | return ( 19 |
24 | 25 |
30 | { 31 | member.iconfont.status === 'invite' ? 待加入 : null 32 | } 33 |

{member.username}

34 |
35 | 36 |
37 |
38 |
39 |
40 | ) 41 | } 42 | } 43 | 44 | Iconfont.propTypes = { 45 | member: PropTypes.object.isRequired, 46 | } 47 | -------------------------------------------------------------------------------- /App/api/editor/directive.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: chenjiangsong 3 | * @Date: 2016-07-05 22:07:00 4 | * @Email: chenjiangsong.sh@superjia.com 5 | * @Last modified by: chenjiangsong 6 | * @Last modified time: 2016-07-06 14:07:08 7 | */ 8 | const CodeMirror = require('codemirror/lib/codemirror.js'); 9 | require('codemirror/lib/codemirror.css'); 10 | require('codemirror/addon/lint/lint.css'); 11 | require('codemirror/mode/javascript/javascript.js'); 12 | require('codemirror/mode/css/css.js'); 13 | require('jsonlint/lib/jsonlint.js'); 14 | require('codemirror/addon/lint/lint.js'); 15 | require('codemirror/addon/lint/json-lint.js'); 16 | 17 | Vue.directive('codemirror', { 18 | twoWay: true, 19 | bind: function () { 20 | // 请自行初始化CodeMirror, 可使用 $(this.el).data('codemirror') 21 | let readOnly = false; 22 | if (this.arg === 'readonly') { 23 | readOnly = true; 24 | } 25 | console.log(11); 26 | const editor = CodeMirror.fromTextArea(this.el, { 27 | lineNumbers: true, 28 | mode: 'application/json', 29 | gutters: ['CodeMirror-lint-markers'], 30 | lint: true, 31 | readOnly: readOnly 32 | }) 33 | this.editor = editor; 34 | 35 | this.editor.on('change', () => { 36 | this.set(this.editor.getValue()); 37 | }); 38 | }, 39 | update: function (value, oldValue) { 40 | // this.silent = true; 41 | // this.editor.setValue(this.vm.$data[this.raw]); 42 | // this.silent = false; 43 | this.editor.setValue(value || this.el.value); 44 | } 45 | }); 46 | -------------------------------------------------------------------------------- /App/main_vue/index.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 25 | 26 | 66 | -------------------------------------------------------------------------------- /App/main_vue/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:02:08 4 | // @Last modified by: chenjiangsong 5 | // @Last modified time: 2016-07-01 01:07:52 6 | */ 7 | #main{ 8 | margin: 18px auto 0; 9 | padding: 10px 0; 10 | background:#fff; 11 | font-size:14px; 12 | color:#666; 13 | border-radius:5px; 14 | box-shadow:2px 2px 2px #ccc; 15 | } 16 | @font-face { 17 | font-family: 'Icons'; 18 | src: url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.eot"); 19 | src: url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.eot?#iefix") format('embedded-opentype'), url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.woff2") format('woff2'), url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.woff") format('woff'), url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.ttf") format('truetype'), url("../../node_modules/semantic-ui/dist/themes/default/assets/fonts/icons.svg#icons") format('svg'); 20 | font-style: normal; 21 | font-weight: normal; 22 | font-variant: normal; 23 | text-decoration: inherit; 24 | text-transform: none; 25 | } 26 | 27 | html, 28 | body, 29 | #fete { 30 | height: 100%; 31 | font-family: Arial; 32 | } 33 | ol,ul{ 34 | list-style: none; 35 | margin:0; 36 | padding:0; 37 | border: 0; 38 | vertical-align: baseline; 39 | font: inherit; 40 | font-size: 100%; 41 | } 42 | 43 | @mixin clearfix { 44 | *zoom: 1; 45 | &:before, 46 | &:after { 47 | display: table; 48 | content: ""; 49 | line-height: 0; 50 | } 51 | &:after { 52 | clear: both; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | # fete 12 | 运行准备: 13 | 14 | 1, 已经安装好了远程的Mongodb,可以直接连接 15 | 如果自己本地想安装可以参考如下: 16 | mac安装mongodb,参考地址:http://www.jianshu.com/p/dd0c39bf7be4 17 | 1)brew install mongodb 18 | 2)mongod --config /usr/local/etc/mongod.conf 19 | 20 | centeros安装mongodb参考:http://www.jianshu.com/p/0a4f9acf811d 21 | 22 | 新增一个用户:db.user.insert({username:'jade',password:'d04a711547058ed0efe27d8c5203f58f'}) 23 | 24 | 登录测试用户: 25 | 用户名:jade 26 | 密码:111111 27 | 28 | 29 | 30 | 2,node环境:5.11.1 31 | 32 | 3,npm install 33 | 34 | 4,在根目录新建config.js,然后把config_template.js内容拷贝到config.js中,然后自己设置下具体参数 35 | 36 | 5,全局安装supervisor,启动命令:supervisor --harmony server.js 37 | 38 | 39 | Q&A: 40 | 1, 如果出现 Cannot find module 'mongodb' 41 | http://www.jianshu.com/p/0ea7ba2bc4a1 42 | 43 | 2, 如果安装node-sass好,后使用不了,可能是需要rebuild下,该问题出现在node 6.x版本 44 | `npm rebuild node-sass` 45 | 46 | 3, 在运行启动命令:supervisor --harmony server.js后,如果出现类似以下报错 47 | ``` 48 | assert.js:89 49 | throw new assert.AssertionError({ 50 | ^ 51 | AssertionError: app.use() requires a generator function 52 | at Application.app.use (E:\workspace\node_modules\koa\lib\application.js:105 53 | :5) 54 | at Object. (E:\workspace\iForm\index.js:13:5) 55 | ``` 56 | 检查node_modules文件夹下安装的`koa-views`包的版本,本项目所依赖的koa-views版本为4.1.0 57 | 解决方案: 58 | `npm install koa-views@4.1.0` 59 | 60 | 4,遇到安装semantic-ui,直接ctr+c就可以了 61 | 62 | 63 | 启动: pm2 start server.js -i max 64 | 65 | 整体架构介绍: 66 | -------------------------------------------------------------------------------- /App/api/vuex/action.js: -------------------------------------------------------------------------------- 1 | const makeAction = type => ({ dispatch }, ...args) => dispatch(type, ...args) 2 | export const tog = makeAction('TOG_LIST') 3 | export const changeFilter = makeAction('CHANGE_FILTER') 4 | export const emptyList = makeAction('EMPTY_LIST') 5 | export const delByIndex = makeAction('DEL_BY_INDEX') 6 | export const blurList = makeAction('BLUR_LIST') 7 | export const del = makeAction('DEL_LIST') 8 | export const setPrdList = makeAction('SET_PRDlIST') 9 | export const setCategories = makeAction('SET_CATEGORIES') 10 | export const addCategory = makeAction('ADD_CATEGORY') 11 | export const setCateActive = makeAction('SET_CATEACTIVE') 12 | export const add = ({ dispatch }, callback) => { 13 | dispatch('ADD_LIST', callback) 14 | } 15 | export const handler = e => { 16 | e.returnValue = '你确定要离开' 17 | } 18 | export const setList = makeAction('SET_LIST') 19 | export const setOriginPrd = makeAction('SET_ORIGIN_PRD') 20 | export const setExceptPrd = makeAction('SET_EXCEPT_PRD') 21 | export const addEvent = () => { 22 | window.addEventListener('beforeunload', handler) 23 | } 24 | export const removeEvent = () => { 25 | window.removeEventListener('beforeunload', handler) 26 | } 27 | export const copy = e => { 28 | const t = e.target 29 | const c = t.dataset.copytarget 30 | const inp = (c ? document.querySelector(c) : null) 31 | if (inp && inp.select) { 32 | inp.select() 33 | try { 34 | document.execCommand('copy') 35 | } catch (err) { 36 | alert('please press Ctrl/Cmd+C to copy') 37 | } 38 | document.activeElement.blur() 39 | toastr.info('已复制到剪贴板!', '', { positionClass: 'toast-top-center' }) 40 | } 41 | } 42 | document.body.addEventListener('click', copy, true) 43 | -------------------------------------------------------------------------------- /App/team/redux/app.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-15 22:44:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-27 23:27:53 6 | */ 7 | .mod-team { 8 | .team-add { 9 | cursor: pointer; 10 | text-align: center; 11 | .anticon-plus { 12 | font-size: 100px; 13 | } 14 | } 15 | .ant-card-body { 16 | height: 150px; 17 | } 18 | .gutter-row { 19 | margin-bottom: 16px; 20 | } 21 | } 22 | 23 | .team { 24 | .description { 25 | height: 70px; 26 | } 27 | .operators { 28 | display: none; 29 | float: right; 30 | } 31 | &:hover .operators { 32 | display: block; 33 | } 34 | } 35 | 36 | .mod-detail { 37 | .detail-title { 38 | margin: 0 -16px 16px; 39 | padding: 0 16px 16px; 40 | border-bottom: 1px solid #e9e9e9; 41 | } 42 | .member { 43 | position: relative; 44 | .operators { 45 | display: none; 46 | float: right; 47 | } 48 | &:hover .operators { 49 | display: block; 50 | } 51 | } 52 | .member-invite { 53 | cursor: pointer; 54 | text-align: center; 55 | } 56 | .anticon-plus { 57 | font-size: 40px; 58 | } 59 | .gutter-row { 60 | margin-bottom: 16px; 61 | } 62 | .ant-tag{ 63 | position: absolute; 64 | top: 10px; 65 | right: 10px; 66 | } 67 | } 68 | 69 | /*未开始,mrd,prd*/ 70 | .phase-bg0, .phase-bg1, .phase-bg2 { 71 | background: #c3f4cb; 72 | } 73 | /*开发,连调*/ 74 | .phase-bg3, .phase-bg4{ 75 | background: #f6ecb8; 76 | } 77 | /*test测试,beta测试*/ 78 | .phase-bg5, .phase-bg6 { 79 | background: #f2b48f; 80 | } 81 | /*上线*/ 82 | .phase-bg7{ 83 | background: #c298c4; 84 | } 85 | /*已合master*/ 86 | .phase-bg8 { 87 | background: #a980b0; 88 | } 89 | -------------------------------------------------------------------------------- /App/project/components/Nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/6/30. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import React, {Component, PropTypes} from 'react'; 6 | import Menu from 'antd/lib/menu'; 7 | import Icon from 'antd/lib/icon'; 8 | import Dropdown from 'antd/lib/dropdown'; 9 | import Breadcrumb from 'antd/lib/breadcrumb'; 10 | 11 | export default class Nav extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | this.state = { 15 | teams: [], 16 | } 17 | } 18 | 19 | //初始化渲染后触发 20 | componentDidMount() { 21 | const self = this; 22 | fetch('/team/data').then(res => self.setState({ 23 | teams: res.data 24 | })) 25 | } 26 | 27 | render() { 28 | const {team} = this.props; 29 | const {teams} = this.state; 30 | const menu = 31 | 32 | { 33 | teams.map(item => 34 | item.id === team.id ? null : 35 | 36 | {item.name} 37 | 38 | ) 39 | } 40 | 41 | return ( 42 |
43 | 44 | 45 | 全部团队 46 | 47 | 48 | 49 | 50 | {team.name} 51 | 52 | 53 | 54 | 我的项目 55 | 56 |
57 | ) 58 | } 59 | } 60 | 61 | Nav.propTypes = {} 62 | -------------------------------------------------------------------------------- /view/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title #{title || '前端协作痛点'} - 尽在FETE 5 | meta(http-equiv='X-UA-Compatible' content="IE=edge") 6 | meta(charset='utf-8') 7 | meta(name='Keywords' content='FETE') 8 | meta(name='Description' content='FETE') 9 | meta(name="viewport" content="width=device-width,target-densitydpi=high-dpi,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no") 10 | //- link(rel='shortcut icon' type='image/x-icon' href='/static/images/favicon.ico') 11 | link(rel='stylesheet', href='/static/#{commonTag}common.css') 12 | link(rel='stylesheet', href='/static/#{staticTag}.css') 13 | //- link(rel='stylesheet', href='#{staticFilter("/static/css/common/module.common.css")}') 14 | //- link(rel='stylesheet', href='/static/layout.css') 15 | block css 16 | body 17 | div#fete 18 | -if(commonTag=='vue_') 19 | div 20 | -else 21 | header#header !{header} 22 | block content 23 | section#main !{html} 24 | //- script(src='#{staticFilter("/static/js/common/module.layout.js")}') 25 | 26 | -var user = JSON.stringify(_user); 27 | -var staticTagMapJson = JSON.stringify(staticTagMap); 28 | script. 29 | window.pageConfig = { 30 | me: !{user} || {}, 31 | staticTag: '#{staticTag}', 32 | host: '#{host}', 33 | socketConnection: '#{socketConnection}', 34 | #{staticTag}: !{staticTagMapJson} 35 | }; 36 | script(src='/static/#{commonTag}common.js') 37 | script(src='/static/#{staticTag}.js') 38 | -------------------------------------------------------------------------------- /App/api/bean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-23 17:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-08-24 11:08:36 7 | */ 8 | 9 | 'use strict'; 10 | /* 11 | input和outputJson是编辑框里的真实输入值,皆为json对象 12 | inputModel和ouput是通过json转换后的数组对象 13 | */ 14 | const api = { 15 | _id: '', 16 | id: '', 17 | userId: '', // 操作用户ID 18 | userName: '', // 操作用户名 19 | title: '', // 标题 20 | url: '', // 请求url 21 | method: '', //提交方式 (get post) 22 | input:{}, 23 | inputModel: [ 24 | { 25 | key: '', 26 | dataType: '', 27 | comment: '', 28 | require: true, 29 | children: [] 30 | } 31 | ], // 入参 32 | output: [ // 出参 33 | { 34 | key: 'status', // 属性名 35 | dataType: 'Integer', // 属性类型 (Integer,Float, String, Boolean, Array,Object) 36 | comment: '状态', // 说明 37 | mock: '', // mock规则 38 | isSelect: false, // 是否支持页面选择 39 | selectItems: [], // 如果isSelect=true,则设置选项值,如:[1, 2, 3] 40 | children: [ // 子元素 (可选,只有dataType是Array,Object才会有) 41 | { 42 | key: 'type', 43 | dataType: 'Integer', 44 | comment: '', 45 | mock: '', 46 | } 47 | ] 48 | } 49 | ], 50 | outputJson: {}, //保存outputJson格式 51 | useOutputJson: false, // 使用 outputJson 还是 使用 mock 52 | status: 1, // 1 新建, 2 修改 53 | updateDescList: [{ // 修改说明list 54 | updateTime: '', // 更新时间 55 | userName: '', // 用户名 56 | updateDesc: '' // 更新说明 57 | }], 58 | prdId: '', // PrdID 59 | projectId: '', // 项目Id 60 | root: '', // 如 weixinEnt 61 | teamId: '', // 项目组Id 62 | updateTime: '', // 更新时间 63 | createTime: '', // 创建时间 64 | categories: [], // 所有已有的分类(从后台返回的数据,前端不做处理) 65 | category: '' // 这个接口的分类 66 | } 67 | -------------------------------------------------------------------------------- /App/main/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-24 23:35:15 4 | * @Last modified by: lancui 5 | * @Last modified time: 2016-07-20 11:07:27 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | 12 | var Index = React.createClass({ 13 | getInitialState: function(){ 14 | return { 15 | val: 0 16 | } 17 | }, 18 | 19 | handleClick: function(){ 20 | var self = this; 21 | self.setState({ 22 | number: Math.random() 23 | }); 24 | }, 25 | 26 | componentWillMont: function() { 27 | console.log('wiimont'); 28 | }, 29 | 30 | componentDidMount: function() { 31 | return; 32 | console.log('add_listen'); 33 | var self = this; 34 | 35 | self.setState({val: self.state.val + 1}); 36 | console.log(self.state.val); 37 | 38 | self.setState({val: self.state.val + 1}); 39 | console.log(self.state.val); 40 | 41 | setTimeout(function(){ 42 | console.log(self.state.val); 43 | self.setState({val: self.state.val + 1}); 44 | console.log(self.state.val); 45 | 46 | self.setState({val: self.state.val + 1}); 47 | console.log(self.state.val); 48 | },0) 49 | }, 50 | 51 | componentWillUnmout: function() { 52 | console.log('remove_listen'); 53 | }, 54 | 55 | render: function() { 56 | return ( 57 |
58 | 很遗憾让你看到这么丑的首页,请点击上面的团队开始使用吧^_^。 59 |
60 | ) 61 | } 62 | }); 63 | 64 | if(typeof document != 'undefined'){ 65 | ReactDOM.render(, document.getElementById('main')); 66 | } 67 | 68 | export default Index; 69 | -------------------------------------------------------------------------------- /App/api/components/sub_slide_menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 45 | 46 | 71 | -------------------------------------------------------------------------------- /App/api/comment_input.vue: -------------------------------------------------------------------------------- 1 | 8 | 31 | 73 | -------------------------------------------------------------------------------- /App/api/editor/comment_input.vue: -------------------------------------------------------------------------------- 1 | 8 | 37 | 79 | -------------------------------------------------------------------------------- /App/project/components/Project.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-28 23:37:31 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import Card from 'antd/lib/card'; 11 | import Button from 'antd/lib/button'; 12 | import AddProject from './AddProject'; 13 | import Modal from 'antd/lib/modal'; 14 | 15 | export default class Project extends Component { 16 | constructor(props, context) { 17 | super(props, context); 18 | } 19 | 20 | render() { 21 | const {project, updateShow, deleteShow, actions} = this.props; 22 | return ( 23 |
24 | {project.name}详情
}> 25 |

{project.description}

26 |
27 | 进入PRD 28 |   29 | 30 |   31 | 32 |
33 | 34 | actions.updateShow(false, project)} 39 | okCallback={project=>actions.updateProject(project)} 40 | /> 41 | actions.deleteShow(false, project)} 44 | onOk={() => actions.deleteProject(project)} 45 | > 46 |

您确定要删除该"{project.name}"团队吗?

47 |
48 | 49 | ) 50 | } 51 | } 52 | 53 | Project.propTypes = { 54 | project: PropTypes.object.isRequired, 55 | } 56 | -------------------------------------------------------------------------------- /App/api/main.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 78 | -------------------------------------------------------------------------------- /App/team/components/Team.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-28 23:37:31 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import classNames from 'classnames'; 11 | import Card from 'antd/lib/card'; 12 | import Button from 'antd/lib/button'; 13 | import AddTeam from './AddTeam'; 14 | import Modal from 'antd/lib/modal'; 15 | import { Link } from 'react-router'; 16 | 17 | export default class Team extends Component { 18 | constructor(props, context) { 19 | super(props, context); 20 | } 21 | 22 | render() { 23 | const {team, updateShow, deleteShow, actions} = this.props; 24 | return ( 25 |
26 | {team.name}详情
}> 27 |

{team.description}

28 |
29 | 进入项目 30 |   31 | 32 |   33 | 34 |
35 | 36 | actions.updateShow(false, team)} 41 | okCallback={team=>actions.updateTeam(team)} 42 | /> 43 | actions.deleteShow(false, team)} 46 | onOk={() => actions.deleteTeam(team)} 47 | > 48 |

您确定要删除该"{team.name}"团队吗?

49 |
50 | 51 | ) 52 | } 53 | } 54 | 55 | Team.propTypes = { 56 | team: PropTypes.object.isRequired, 57 | } 58 | -------------------------------------------------------------------------------- /App/api/components/slide_menu.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 52 | 53 | 84 | -------------------------------------------------------------------------------- /App/prd/redux/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:50:10 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:59:05 6 | */ 7 | 8 | 'use strict'; 9 | import {combineReducers} from 'redux' 10 | import { 11 | GET, 12 | ADD, 13 | ADD_SHOW, 14 | UPDATE, 15 | UPDATE_SHOW, 16 | DELETE, 17 | DELETE_SHOW, 18 | } from './Actions' 19 | 20 | function prds(state = [], action) { 21 | switch (action.type) { 22 | case GET: 23 | return action.prds 24 | case ADD: 25 | return [action.prd, ...state] 26 | case UPDATE: 27 | return _.map(state, item => item.id == action.prd.id ? action.prd : item) 28 | case DELETE: 29 | return _.filter(state, item => item.id != action.prd.id) 30 | default: 31 | return state 32 | } 33 | } 34 | 35 | function addShow(state = false, action) { 36 | switch (action.type) { 37 | case ADD_SHOW: 38 | return action.addShow; 39 | case ADD: 40 | return false; 41 | default: 42 | return state 43 | } 44 | } 45 | 46 | function updateShow(state = {updateShow: false, prd: {}}, action) { 47 | switch (action.type) { 48 | case UPDATE_SHOW: 49 | return Object.assign({}, action); 50 | case UPDATE: 51 | return {updateShow: false, prd: action.prd}; 52 | default: 53 | return state 54 | } 55 | } 56 | 57 | function deleteShow(state = {deleteShow: false, prd: {}}, action) { 58 | switch (action.type) { 59 | case DELETE_SHOW: 60 | return Object.assign({}, action); 61 | case DELETE: 62 | return {deleteShow: false, prd: action.prd};; 63 | default: 64 | return state 65 | } 66 | } 67 | 68 | //将两个reducer合并成一个reducer,也就将全局的state加上postsByReddit,selectedReddit两个属性,每个属性都有自己的state 69 | const rootReducer = combineReducers({ 70 | prds, 71 | addShow, 72 | updateShow, 73 | deleteShow, 74 | // selectedReddit 75 | }) 76 | 77 | export default rootReducer 78 | -------------------------------------------------------------------------------- /App/iconfont/components/Team.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-12 00:03:50 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-28 23:37:31 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import classNames from 'classnames'; 11 | import Card from 'antd/lib/card'; 12 | import Button from 'antd/lib/button'; 13 | import AddIconfont from './AddIconfont'; 14 | import Modal from 'antd/lib/modal'; 15 | import { Link } from 'react-router'; 16 | 17 | export default class Iconfont extends Component { 18 | constructor(props, context) { 19 | super(props, context); 20 | } 21 | 22 | render() { 23 | const {iconfont, updateShow, deleteShow, actions} = this.props; 24 | return ( 25 |
26 | {iconfont.name}详情
}> 27 |

{iconfont.description}

28 |
29 | 进入项目 30 |   31 | 32 |   33 | 34 |
35 | 36 | actions.updateShow(false, iconfont)} 41 | okCallback={iconfont=>actions.updateIconfont(iconfont)} 42 | /> 43 | actions.deleteShow(false, iconfont)} 46 | onOk={() => actions.deleteIconfont(iconfont)} 47 | > 48 |

您确定要删除该"{iconfont.name}"团队吗?

49 |
50 | 51 | ) 52 | } 53 | } 54 | 55 | Iconfont.propTypes = { 56 | iconfont: PropTypes.object.isRequired, 57 | } 58 | -------------------------------------------------------------------------------- /App/message/bean.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-07-01 11:07:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-20 11:07:47 7 | */ 8 | 9 | 10 | // 消息表 11 | const message = { 12 | _id: '', 13 | id: '', 14 | userName: '', // 操作人姓名 15 | msgType: '', // 消息类型:系统(0),提醒(1) 16 | platform: '', // 平台类型(team, project, prd, api) 17 | platformId: '', // 平台Id 18 | action: '', // 操作 (如:add, update, delete,invited) 19 | actionDetail: { 20 | message: '', 21 | btns: [{ 22 | text: '', 23 | style: '',//样式,primary|warning|danger 24 | type: 'link|ajax', //link:在新窗口打开的链接,ajax,发送ajax接口 25 | resultText: '已接受', // 操作后返回的中文显示 26 | ajax: { 27 | url: '', 28 | method: '', 29 | body: { 30 | 31 | } 32 | }, //ajax参数 33 | linkUrl: '' 34 | }] 35 | }, // 操作描述 36 | createTime: '', // 创建时间 37 | toUsers: [{ 38 | userId: '', // 提醒用户ID 39 | status: 0, // 0未读, 1已读, 2已操作 40 | resultText: '' // 操作后返回的中文显示 41 | }] 42 | } 43 | 44 | //邀请成员相关消息 45 | const invited ={ 46 | userName: '', // 操作人姓名 47 | msgType: '0', // 消息类型:系统(0),提醒(1) 48 | platform: 'team', // 平台类型(team, project, prd, api) 49 | platformId: 'xxx', // 平台Id 50 | action: 'invited', // 操作 (如:add, update, delete,invited) 51 | actionDetail: { 52 | message: '', 53 | btns: [{ 54 | text: '接受', 55 | style: 'primary', 56 | type: 'ajax', 57 | ajax: { 58 | url: '/team/member/invited/accept', 59 | method: 'post', 60 | body: { 61 | teamId: 'xxx', 62 | } 63 | } 64 | },{ 65 | text: '拒绝', 66 | type: 'ajax', 67 | style: 'danger', 68 | ajax: { 69 | url: '/team/member/invited/reject', 70 | method: 'post', 71 | body: { 72 | teamId: 'xxx', 73 | } 74 | } 75 | }] 76 | }, // 操作描述 77 | createTime: '', // 创建时间 78 | toUsers: [{ 79 | userId: '', // 提醒用户ID 80 | status: 0 // 0未读, 1已读 81 | }] 82 | } 83 | 84 | 85 | // db.message.insert({userName: 'lancui', msgType: '1', platform: 'api', platformId: '576b401056e121e6c9ef082b',action: 'add', actionDetail: '新增消息接口', createTime: new Date}) 86 | -------------------------------------------------------------------------------- /App/api/vuex/mutations.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: geyuanjun 3 | * @Date: 2016-07-11 17:33:0 4 | * @Email: geyuanjun.sh@superjia.com 5 | * @Last modified by: geyuanjun 6 | * @Last modified time: 2016-07-12 11:02:52 7 | */ 8 | 9 | export default { 10 | SET_LIST(state, list) { 11 | state.list = list 12 | }, 13 | SET_PRDlIST(state, arr) { 14 | state.prdList = arr 15 | }, 16 | ADD_LIST(state, list) { 17 | if (!list || !list.url) { 18 | list = { 19 | url: '/', 20 | title: '-', 21 | method: '-', 22 | lastModify: '-' 23 | } 24 | } 25 | state.list.unshift(list) 26 | state.list_active = list 27 | }, 28 | DEL_LIST(state) { 29 | if (state.list_active) { 30 | state.list.splice(state.list.indexOf(state.list_active), 1); 31 | } 32 | }, 33 | TOG_LIST(state, list) { 34 | state.list_active = list; 35 | }, 36 | BLUR_LIST(state) { 37 | state.list_active = {}; 38 | }, 39 | // change prdId, projectId, teamId, apiRoot 40 | CHANGE_FILTER(state, params) { 41 | state = _.extend(state, params) 42 | }, 43 | EMPTY_LIST(state) { 44 | state.list = [] 45 | state.list_active = {} 46 | }, 47 | DEL_BY_INDEX(state, index) { 48 | state.list.splice(index, 1) 49 | }, 50 | SET_DEFAULT_URL(state, url) { 51 | state.defaultUrl = url 52 | }, 53 | SET_DEFAULT_TITLE(state, title) { 54 | state.defaultTitle = title 55 | }, 56 | SET_DEFAULT_METHOD(state, method) { 57 | state.defaultMethod = method 58 | }, 59 | SET_DEFAULT_DATA(state, data) { 60 | state.defaultData = data 61 | }, 62 | SET_CATEGORIES(state, list) { 63 | state.categories = list 64 | }, 65 | ADD_CATEGORY(state, list) { 66 | if (_.indexOf(state.categories, list) === -1) { 67 | state.categories.push(list) 68 | } 69 | }, 70 | SET_CATEACTIVE(state, cate) { 71 | state.cate_active = cate 72 | }, 73 | SET_ORIGIN_PRD(state, prd) { 74 | state.originPrd = prd 75 | }, 76 | SET_EXCEPT_PRD(state, prd, key, value) { 77 | const ret = [] 78 | for (let i = 0, len = prd.length; i < len; i++) { 79 | const item = prd[i] 80 | if (item[key] !== value) { 81 | ret.push(item) 82 | } 83 | } 84 | state.exceptMePrdData = ret 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /App/api/category.vue: -------------------------------------------------------------------------------- 1 | 12 | 49 | 94 | -------------------------------------------------------------------------------- /App/prd/redux/Actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:49:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:55:07 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // export const REQUEST = 'REQUEST'; 11 | export const GET = 'GET'; 12 | export const ADD_SHOW = 'ADD_SHOW'; 13 | export const ADD = 'ADD'; 14 | export const UPDATE = 'UPDATE'; 15 | export const UPDATE_SHOW = 'UPDATE_SHOW'; 16 | export const DELETE = 'DELETE'; 17 | export const DELETE_SHOW = 'DELETE_SHOW'; 18 | 19 | const {team, project} = pageConfig.me; 20 | 21 | //获取文章,先触发requestPosts开始获取action,完成后触发receivePosts获取成功的action 22 | export function getPrds() { 23 | return dispatch => { 24 | // dispatch(request()) 25 | return fetch('/prd/data', { 26 | body: { 27 | projectId: project.id 28 | } 29 | }) 30 | .then(json => dispatch({ 31 | type: GET, 32 | prds: json.data 33 | })) 34 | } 35 | } 36 | 37 | export function addShow(addShow) { 38 | return { 39 | type: ADD_SHOW, 40 | addShow: addShow 41 | } 42 | } 43 | 44 | export function addPrd(prd) { 45 | return dispatch => { 46 | return fetch('/prd/data', { 47 | method: 'post', 48 | body: Object.assign({projectId: project.id}, prd) 49 | }) 50 | .then(json => dispatch({ 51 | type: ADD, 52 | prd: json.data 53 | })) 54 | } 55 | } 56 | 57 | export function updateShow(updateShow, prd) { 58 | return { 59 | type: UPDATE_SHOW, 60 | updateShow: updateShow, 61 | prd: prd 62 | } 63 | } 64 | 65 | export function updatePrd(prd) { 66 | return dispatch => { 67 | // dispatch(request()) 68 | return fetch('/prd/data', { 69 | method: 'put', 70 | body: Object.assign({projectId: project.id}, prd) 71 | }) 72 | .then(json => dispatch({ 73 | type: UPDATE, 74 | prd: json.data 75 | })) 76 | } 77 | } 78 | 79 | export function deletePrd(prd) { 80 | return dispatch => { 81 | // dispatch(request()) 82 | return fetch('/prd/data', { 83 | method: 'delete', 84 | body: { 85 | id: prd.id, 86 | projectId: project.id 87 | } 88 | }) 89 | .then(json => dispatch({ 90 | type: DELETE, 91 | prd: prd 92 | })) 93 | } 94 | } 95 | 96 | export function deleteShow(deleteShow, prd) { 97 | return { 98 | type: DELETE_SHOW, 99 | deleteShow: deleteShow, 100 | prd: prd 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /App/user/redux/Actions.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:49:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:55:07 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // export const REQUEST = 'REQUEST'; 11 | export const GET = 'GET'; 12 | export const ADD_SHOW = 'ADD_SHOW'; 13 | export const ADD = 'ADD'; 14 | export const UPDATE = 'UPDATE'; 15 | export const UPDATE_SHOW = 'UPDATE_SHOW'; 16 | export const DELETE = 'DELETE'; 17 | export const DELETE_SHOW = 'DELETE_SHOW'; 18 | 19 | const {team, project} = pageConfig.me; 20 | 21 | //获取文章,先触发requestPosts开始获取action,完成后触发receivePosts获取成功的action 22 | export function getPrds() { 23 | return dispatch => { 24 | // dispatch(request()) 25 | return fetch('/prd/data', { 26 | body: { 27 | projectId: project.id 28 | } 29 | }) 30 | .then(json => dispatch({ 31 | type: GET, 32 | prds: json.data 33 | })) 34 | } 35 | } 36 | 37 | export function addShow(addShow) { 38 | return { 39 | type: ADD_SHOW, 40 | addShow: addShow 41 | } 42 | } 43 | 44 | export function addPrd(prd) { 45 | return dispatch => { 46 | return fetch('/prd/data', { 47 | method: 'post', 48 | body: Object.assign({projectId: project.id}, prd) 49 | }) 50 | .then(json => dispatch({ 51 | type: ADD, 52 | prd: json.data 53 | })) 54 | } 55 | } 56 | 57 | export function updateShow(updateShow, prd) { 58 | return { 59 | type: UPDATE_SHOW, 60 | updateShow: updateShow, 61 | prd: prd 62 | } 63 | } 64 | 65 | export function updatePrd(prd) { 66 | return dispatch => { 67 | // dispatch(request()) 68 | return fetch('/prd/data', { 69 | method: 'put', 70 | body: Object.assign({projectId: project.id}, prd) 71 | }) 72 | .then(json => dispatch({ 73 | type: UPDATE, 74 | prd: json.data 75 | })) 76 | } 77 | } 78 | 79 | export function deletePrd(prd) { 80 | return dispatch => { 81 | // dispatch(request()) 82 | return fetch('/prd/data', { 83 | method: 'delete', 84 | body: { 85 | id: prd.id, 86 | projectId: project.id 87 | } 88 | }) 89 | .then(json => dispatch({ 90 | type: DELETE, 91 | prd: prd 92 | })) 93 | } 94 | } 95 | 96 | export function deleteShow(deleteShow, prd) { 97 | return { 98 | type: DELETE_SHOW, 99 | deleteShow: deleteShow, 100 | prd: prd 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /App/project/redux/action.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:49:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:55:07 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // export const REQUEST = 'REQUEST'; 11 | export const GET = 'GET'; 12 | export const ADD_SHOW = 'ADD_SHOW'; 13 | export const ADD = 'ADD'; 14 | export const UPDATE = 'UPDATE'; 15 | export const UPDATE_SHOW = 'UPDATE_SHOW'; 16 | export const DELETE = 'DELETE'; 17 | export const DELETE_SHOW = 'DELETE_SHOW'; 18 | 19 | const team = pageConfig.me.team; 20 | 21 | //获取文章,先触发requestPosts开始获取action,完成后触发receivePosts获取成功的action 22 | export function getProjects() { 23 | return dispatch => { 24 | // dispatch(request()) 25 | return fetch('/project/data', { 26 | body: { 27 | teamId: team.id 28 | } 29 | }) 30 | .then(json => dispatch({ 31 | type: GET, 32 | projects: json.data 33 | })) 34 | } 35 | } 36 | 37 | export function addShow(addShow) { 38 | return { 39 | type: ADD_SHOW, 40 | addShow: addShow 41 | } 42 | } 43 | 44 | export function addProject(project) { 45 | return dispatch => { 46 | return fetch('/project/data', { 47 | method: 'post', 48 | body: Object.assign({teamId: team.id}, project) 49 | }) 50 | .then(json => dispatch({ 51 | type: ADD, 52 | project: json.data 53 | })) 54 | } 55 | } 56 | 57 | export function updateShow(updateShow, project) { 58 | return { 59 | type: UPDATE_SHOW, 60 | updateShow: updateShow, 61 | project: project 62 | } 63 | } 64 | 65 | export function updateProject(project) { 66 | return dispatch => { 67 | // dispatch(request()) 68 | return fetch('/project/data', { 69 | method: 'put', 70 | body: Object.assign({teamId: team.id}, project) 71 | }) 72 | .then(json => dispatch({ 73 | type: UPDATE, 74 | project: json.data 75 | })) 76 | } 77 | } 78 | 79 | export function deleteProject(project) { 80 | return dispatch => { 81 | // dispatch(request()) 82 | return fetch('/project/data', { 83 | method: 'delete', 84 | body: { 85 | id: project.id, 86 | teamId: team.id 87 | } 88 | }) 89 | .then(json => dispatch({ 90 | type: DELETE, 91 | project: project 92 | })) 93 | } 94 | } 95 | 96 | export function deleteShow(deleteShow, project) { 97 | return { 98 | type: DELETE_SHOW, 99 | deleteShow: deleteShow, 100 | project: project 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /App/prd/components/Nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/6/30. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import React, {Component, PropTypes} from 'react'; 6 | import Menu from 'antd/lib/menu'; 7 | import Icon from 'antd/lib/icon'; 8 | import Dropdown from 'antd/lib/dropdown'; 9 | import Breadcrumb from 'antd/lib/breadcrumb'; 10 | 11 | export default class Nav extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | this.state = { 15 | teams: [], 16 | projects: [], 17 | } 18 | } 19 | 20 | //初始化渲染后触发 21 | componentDidMount() { 22 | const self = this; 23 | const {team, project} = this.props; 24 | fetch('/team/data').then(res => self.setState({ 25 | teams: res.data 26 | })) 27 | fetch('/project/data', { 28 | body: { 29 | teamId: team.id 30 | } 31 | }).then(res => self.setState({ 32 | projects: res.data 33 | })) 34 | } 35 | 36 | render() { 37 | const {team, project} = this.props; 38 | const {teams, projects} = this.state; 39 | const teamMenu = 40 | 41 | { 42 | teams.map(item => 43 | item.id === team.id ? null : 44 | 45 | {item.name} 46 | 47 | ) 48 | } 49 | 50 | const projectMenu = 51 | 52 | { 53 | projects.map(item => 54 | item.id === project.id ? null : 55 | 56 | {item.name} 57 | 58 | ) 59 | } 60 | 61 | return ( 62 |
63 | 64 | 65 | 全部团队 66 | 67 | 68 | 69 | 70 | {team.name} 71 | 72 | 73 | 74 | 75 | 76 | 77 | {project.name} 78 | 79 | 80 | 81 | 我的PRD 82 | 83 |
84 | ) 85 | } 86 | } 87 | 88 | Nav.propTypes = {} 89 | -------------------------------------------------------------------------------- /App/user/components/Nav.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 16/6/30. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import React, {Component, PropTypes} from 'react'; 6 | import Menu from 'antd/lib/menu'; 7 | import Icon from 'antd/lib/icon'; 8 | import Dropdown from 'antd/lib/dropdown'; 9 | import Breadcrumb from 'antd/lib/breadcrumb'; 10 | 11 | export default class Nav extends Component { 12 | constructor(props, context) { 13 | super(props, context); 14 | this.state = { 15 | teams: [], 16 | projects: [], 17 | } 18 | } 19 | 20 | //初始化渲染后触发 21 | componentDidMount() { 22 | const self = this; 23 | const {team, project} = this.props; 24 | fetch('/team/data').then(res => self.setState({ 25 | teams: res.data 26 | })) 27 | fetch('/project/data', { 28 | body: { 29 | teamId: team.id 30 | } 31 | }).then(res => self.setState({ 32 | projects: res.data 33 | })) 34 | } 35 | 36 | render() { 37 | const {team, project} = this.props; 38 | const {teams, projects} = this.state; 39 | const teamMenu = 40 | 41 | { 42 | teams.map(item => 43 | item.id === team.id ? null : 44 | 45 | {item.name} 46 | 47 | ) 48 | } 49 | 50 | const projectMenu = 51 | 52 | { 53 | projects.map(item => 54 | item.id === project.id ? null : 55 | 56 | {item.name} 57 | 58 | ) 59 | } 60 | 61 | return ( 62 |
63 | 64 | 65 | 全部团队 66 | 67 | 68 | 69 | 70 | {team.name} 71 | 72 | 73 | 74 | 75 | 76 | 77 | {project.name} 78 | 79 | 80 | 81 | 我的PRD 82 | 83 |
84 | ) 85 | } 86 | } 87 | 88 | Nav.propTypes = {} 89 | -------------------------------------------------------------------------------- /App/team/components/InviteMember.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-28 13:20:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:53:54 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import Modal from 'antd/lib/modal'; 11 | import Form from 'antd/lib/form'; 12 | import Input from 'antd/lib/input'; 13 | 14 | const FormItem = Form.Item; 15 | 16 | class InviteMember extends Component { 17 | constructor(props, context) { 18 | super(props, context) 19 | } 20 | 21 | handleOk() { 22 | const {form, okCallback, team} = this.props; 23 | const result = form.validateFields((errors, values) => { 24 | if (!!errors) { 25 | return false; 26 | } else { 27 | okCallback && okCallback(Object.assign({}, form.getFieldsValue(['username']))); 28 | } 29 | }); 30 | } 31 | 32 | handleCancel() { 33 | const {cancelCallback} = this.props; 34 | cancelCallback && cancelCallback(); 35 | } 36 | 37 | render() { 38 | const self = this; 39 | const {visible, form} = this.props; 40 | const {getFieldProps, getFieldError, isFieldValidating} = form; 41 | const formItemLayout = { 42 | labelCol: {span: 6}, 43 | wrapperCol: {span: 14}, 44 | }; 45 | const usernameProps = getFieldProps('username', { 46 | // value: team && team.name, 47 | rules: [ 48 | {required: true, message: '请输入用户名'}, 49 | { 50 | validator: (rule, value, callback) => { 51 | if (!value) { 52 | callback(); 53 | } else { 54 | fetch('/user/data', { 55 | body: { 56 | username: value 57 | } 58 | }).then(res => { 59 | callback(); 60 | }).catch(err => { 61 | callback(err.response.message); 62 | }) 63 | } 64 | } 65 | } 66 | ] 67 | }); 68 | 69 | return ( 70 | 76 |
77 | 83 | 89 | 90 |
91 |
92 | ) 93 | } 94 | } 95 | 96 | InviteMember = Form.create()(InviteMember); 97 | 98 | export default InviteMember; 99 | -------------------------------------------------------------------------------- /App/iconfont/components/InviteMember.jsx: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-28 13:20:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:53:54 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import Modal from 'antd/lib/modal'; 11 | import Form from 'antd/lib/form'; 12 | import Input from 'antd/lib/input'; 13 | 14 | const FormItem = Form.Item; 15 | 16 | class InviteMember extends Component { 17 | constructor(props, context) { 18 | super(props, context) 19 | } 20 | 21 | handleOk() { 22 | const {form, okCallback, iconfont} = this.props; 23 | const result = form.validateFields((errors, values) => { 24 | if (!!errors) { 25 | return false; 26 | } else { 27 | okCallback && okCallback(Object.assign({}, form.getFieldsValue(['username']))); 28 | } 29 | }); 30 | } 31 | 32 | handleCancel() { 33 | const {cancelCallback} = this.props; 34 | cancelCallback && cancelCallback(); 35 | } 36 | 37 | render() { 38 | const self = this; 39 | const {visible, form} = this.props; 40 | const {getFieldProps, getFieldError, isFieldValidating} = form; 41 | const formItemLayout = { 42 | labelCol: {span: 6}, 43 | wrapperCol: {span: 14}, 44 | }; 45 | const usernameProps = getFieldProps('username', { 46 | // value: iconfont && iconfont.name, 47 | rules: [ 48 | {required: true, message: '请输入用户名'}, 49 | { 50 | validator: (rule, value, callback) => { 51 | if (!value) { 52 | callback(); 53 | } else { 54 | fetch('/user/data', { 55 | body: { 56 | username: value 57 | } 58 | }).then(res => { 59 | callback(); 60 | }).catch(err => { 61 | callback(err.response.message); 62 | }) 63 | } 64 | } 65 | } 66 | ] 67 | }); 68 | 69 | return ( 70 | 76 |
77 | 83 | 89 | 90 |
91 |
92 | ) 93 | } 94 | } 95 | 96 | InviteMember = Form.create()(InviteMember); 97 | 98 | export default InviteMember; 99 | -------------------------------------------------------------------------------- /App/calendar/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-24 15:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-08-09 11:08:20 7 | */ 8 | 9 | const wrap = require('co-monk') 10 | const db = require('../../common/db') 11 | import sutil from '../../common/sutil' 12 | import Router from 'koa-router' 13 | 14 | const router = new Router({ 15 | prefix: '/calendar' 16 | }) 17 | 18 | // CURD 19 | const userDao = wrap(db.get('user')) 20 | const prdDao = wrap(db.get('prd')) 21 | const projectDao = wrap(db.get('project')); 22 | 23 | router.get('/', sutil.login, function*(next) { 24 | yield sutil.render(this, { 25 | commonTag: 'vue', 26 | html: '', 27 | staticTag: 'calendar', 28 | noHeader: true 29 | }) 30 | }) 31 | 32 | router.get('/events', sutil.login, function* (next) { 33 | let [user, prjMap] = [this.locals._user, {}] 34 | const teamIds = user.teams.map((val) => { 35 | return val.id 36 | }) 37 | // 查找用户相关的prd 38 | const prds = yield prdDao.find({ 39 | teamId: {'$in': teamIds} 40 | }) 41 | // 查找用户相关的项目 42 | const prjs = yield projectDao.find({ 43 | teamId: {'$in': teamIds} 44 | }) 45 | prjs.forEach((prj) => { 46 | prjMap[prj.id] = prj.name 47 | }) 48 | 49 | // 设置项目名称 50 | prds.forEach((prd) => { 51 | const prjName = prjMap[prd.projectId] 52 | prd.projectName = !prjName ? '' : prjName 53 | }) 54 | 55 | const res = { 56 | status: 0, 57 | data: prds 58 | } 59 | sutil.success(this, res) 60 | }) 61 | 62 | // // 获取用户的所有消息 63 | // router.get('/messages', sutil.login, function* (next) { 64 | // /** 65 | // * userId: 需要推送过去的人的userId 66 | // * pageSize: 每一页所需要的消息条数 67 | // * pageIndex: 需要的是第几页的数据 68 | // */ 69 | // let [uid, pageSize, pageIndex] = [this.parse.userId, this.parse.pageSize, this.parse.pageIndex] 70 | // if (!uid) { 71 | // sutil.failed(this, 1003) 72 | // } 73 | // let msgs = yield msgDao.find({'toUsers.userId': uid}, {sort: {createTime:-1, status:1}, limit: pageSize, skip: (pageIndex - 1)*pageSize}) 74 | // let count = yield msgDao.count({'toUsers.userId': uid}) 75 | // 76 | // for(let j in msgs) { 77 | // let status = 0, toUsers = msgs[j].toUsers 78 | // for(let i in toUsers){ 79 | // let tu = toUsers[i] 80 | // if(tu.userId === uid){ 81 | // msgs[j].status = tu.status 82 | // const statusMap = { 0: '未读', 1: '已读', 2: (!tu.resultText ? '已操作' : tu.resultText) } 83 | // // console.log(`tu.resultText===${tu.resultText}`) 84 | // msgs[j].resultText = statusMap[tu.status] 85 | // break 86 | // } 87 | // } 88 | // } 89 | // let message = { 90 | // msgs, 91 | // count 92 | // } 93 | // sutil.success(this, message) 94 | // }) 95 | 96 | export default router 97 | -------------------------------------------------------------------------------- /socket/server.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-27 17:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-08 15:07:37 7 | */ 8 | 9 | import config from '../config' 10 | /** 监听message **/ 11 | const wrap = require('co-monk'); 12 | const db = require('../common/db'); 13 | const msgDao = wrap(db.get('message')); 14 | 15 | // const server = require('http').createServer(app.callback()); 16 | // const io = require('socket.io')(server); 17 | // let usersSocketMap = {}; 18 | 19 | let serverSocket = { 20 | init(app) { 21 | var self = this; 22 | const server = require('http').createServer(app.callback()); 23 | const io = require('socket.io')(server); 24 | 25 | self.usersSocketMap = {}; 26 | 27 | io.on('connection', function(socket){ 28 | console.log(`===socket connection====${socket.id}`); 29 | // 用户名添加到Map 30 | socket.on('addToUserSocketMap', function(username){ 31 | self.usersSocketMap[username] = socket; 32 | socket.username = username; 33 | self.sendMsg([username], false); 34 | }); 35 | // test 打印io.sockets 36 | // for(let i in io.sockets){ 37 | // try{ 38 | // let obj = io.sockets[i]; 39 | // console.log(`===${i} ${typeof obj === 'object' ? JSON.stringify(obj) : obj}`); 40 | // }catch(e) { 41 | // } 42 | // } 43 | 44 | // 解除连接 45 | socket.on('disconnect', function () { 46 | io.sockets.emit(`==disconnected==${socket.id} ${socket.username}`); 47 | }); 48 | 49 | // 获得Socket用户的ID 50 | socket.emit('getUserId'); 51 | }); 52 | server.listen(config.socketPort); 53 | }, 54 | /** 55 | * @param remindUsers 提醒的用户名数组 56 | * @param isToastr 是否显示提示文字,默认true 57 | */ 58 | sendMsg(remindUsers, isToastr) { 59 | isToastr = isToastr === undefined ? true : isToastr; 60 | for(let i in remindUsers) { 61 | let uid = remindUsers[i], 62 | socket = this.usersSocketMap[uid]; 63 | // 获得数据 64 | if(!socket) continue; 65 | msgDao.count({toUsers: {'$elemMatch': {'userId': uid, 'status': 0}}}, (err, res) => { 66 | if(!!err) { 67 | console.log('ERROR: getMsgs has error!'); return; 68 | } 69 | socket.emit('updateMsgs', {count:res, isToastr: isToastr}); 70 | }); 71 | } 72 | } 73 | } 74 | module.exports = serverSocket; 75 | 76 | /** end 监听message **/ 77 | 78 | // io.on('connection', function(socket){ 79 | // console.log('===socket connection====' + socket); 80 | // // 获得消息个数 81 | // socket.on('getMsgs', function(userId) { 82 | // // msgDaoCC.find({}).then((res) => { 83 | // // socket.emit('updateMsgs', res); 84 | // // }); 85 | // msgDaoCC.count({toUsers: userId, status: 0}, (err, res) => { 86 | // if(!!err) { 87 | // console.log('ERROR: getMsgs has error!'); 88 | // return; 89 | // } 90 | // socket.emit('updateMsgs', res); 91 | // }); 92 | // }); 93 | // }); 94 | -------------------------------------------------------------------------------- /App/project/redux/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:50:10 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:59:05 6 | */ 7 | 8 | 'use strict'; 9 | import {combineReducers} from 'redux' 10 | import { 11 | GET, 12 | ADD, 13 | ADD_SHOW, 14 | UPDATE, 15 | UPDATE_SHOW, 16 | DELETE, 17 | DELETE_SHOW, 18 | } from './action' 19 | 20 | //选择新闻后,将state.selectedReddit设为所选选项 21 | // function selectedReddit(state = 'reactjs', action) { 22 | // switch (action.type) { 23 | // case SELECT_REDDIT: 24 | // return action.reddit 25 | // default: 26 | // return state 27 | // } 28 | // } 29 | 30 | function projects(state = [], action) { 31 | switch (action.type) { 32 | case GET: 33 | return action.projects 34 | case ADD: 35 | return [action.project, ...state] 36 | case UPDATE: 37 | return _.map(state, item => item.id == action.project.id ? action.project : item) 38 | case DELETE: 39 | return _.filter(state, item => item.id != action.project.id) 40 | default: 41 | return state 42 | } 43 | } 44 | 45 | function addShow(state = false, action) { 46 | switch (action.type) { 47 | case ADD_SHOW: 48 | return action.addShow; 49 | case ADD: 50 | return false; 51 | default: 52 | return state 53 | } 54 | } 55 | 56 | function updateShow(state = {updateShow: false, project: {}}, action) { 57 | switch (action.type) { 58 | case UPDATE_SHOW: 59 | return Object.assign({}, action); 60 | case UPDATE: 61 | return {updateShow: false, project: action.project}; 62 | default: 63 | return state 64 | } 65 | } 66 | 67 | function deleteShow(state = {deleteShow: false, project: {}}, action) { 68 | switch (action.type) { 69 | case DELETE_SHOW: 70 | return Object.assign({}, action); 71 | case DELETE: 72 | return {deleteShow: false, project: action.project};; 73 | default: 74 | return state 75 | } 76 | } 77 | //废弃、接收到、开始接受新闻后,将state.postsByReddit设为相关参数 78 | // function postsByReddit(state = {}, action) { 79 | // switch (action.type) { 80 | // case INVALIDATE_REDDIT: 81 | // case RECEIVE_POSTS: 82 | // case REQUEST_POSTS: 83 | // return Object.assign({}, state, { 84 | // [action.reddit]: posts(state[action.reddit], action) 85 | // }) 86 | // default: 87 | // return state 88 | // } 89 | // } 90 | //将两个reducer合并成一个reducer,也就将全局的state加上postsByReddit,selectedReddit两个属性,每个属性都有自己的state 91 | const rootReducer = combineReducers({ 92 | projects, 93 | addShow, 94 | updateShow, 95 | deleteShow, 96 | // selectedReddit 97 | }) 98 | 99 | export default rootReducer 100 | -------------------------------------------------------------------------------- /App/main_vue/top_nav.vue: -------------------------------------------------------------------------------- 1 | 30 | 31 | 50 | 51 | 88 | -------------------------------------------------------------------------------- /App/prd/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 15/7/7. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import Router from 'koa-router'; 6 | const router = new Router({ 7 | prefix: '/prd' 8 | }); 9 | const wrap = require('co-monk'); 10 | const db = require('../../common/db'); 11 | const userDao = wrap(db.get('user')); 12 | const prdDao = wrap(db.get('prd')); 13 | 14 | import sutil from '../../common/sutil'; 15 | 16 | // PRD 17 | router.get('/data/first', sutil.login, function*(next) { 18 | sutil.success(this, yield prdDao.findOne({}, { 19 | sort: {createTime: -1} 20 | })); 21 | }); 22 | 23 | router.get('/', sutil.projectLogin, function*(next) { 24 | yield sutil.render(this, {}); 25 | }); 26 | 27 | // 所有数据,不受权限限制 28 | router.get('/alldata', sutil.login, function*(next) { 29 | sutil.success(this, yield prdDao.find({ 30 | projectId: this.parse.projectId 31 | }, { 32 | sort: {createTime: -1} 33 | })); 34 | }); 35 | 36 | router.get('/data', sutil.projectLogin, function*(next) { 37 | sutil.success(this, yield prdDao.find({ 38 | projectId: this.parse.projectId 39 | }, { 40 | sort: {createTime: -1} 41 | })); 42 | }); 43 | 44 | router.post('/data', sutil.projectLogin, function*(next) { 45 | const parse = this.parse; 46 | const user = this.locals._user; 47 | const {project} = user; 48 | const id = yield sutil.genId(prdDao); 49 | 50 | const oldPrd = yield prdDao.findOne({ 51 | projectId: project.id 52 | },{ 53 | sort: {createTime: -1} 54 | }); 55 | 56 | const prd = yield prdDao.insert(Object.assign({}, { 57 | id: id, 58 | teamId: project.teamId, 59 | projectId: project.id, 60 | createUser: user.username, 61 | createTime: Date.now(), 62 | updateTime: Date.now() 63 | }, parse)); 64 | if(oldPrd) yield sutil.copyApisByPrd(oldPrd.id, prd.id); 65 | sutil.success(this, prd); 66 | }); 67 | 68 | router.put('/data', sutil.projectLogin, function*(next) { 69 | const parse = this.parse; 70 | const id = parse.id; 71 | const user = this.locals._user; 72 | 73 | let prd = yield prdDao.findOne({ 74 | id: id 75 | }); 76 | 77 | if (!prd) { 78 | sutil.failed(this, 13002); 79 | return false; 80 | } 81 | 82 | // if (prd.createUser !== user.username) { 83 | // sutil.failed(this, 13001); 84 | // return false; 85 | // } 86 | 87 | 88 | yield prdDao.update({ 89 | id: parse.id 90 | }, { 91 | $set: Object.assign({ 92 | updateTime: Date.now() 93 | }, 94 | parse) 95 | }); 96 | sutil.success(this, Object.assign({}, prd, parse)); 97 | }); 98 | 99 | router.del('/data', sutil.projectLogin, function*(next) { 100 | const parse = this.parse; 101 | const id = parse.id; 102 | const user = this.locals._user; 103 | 104 | const prd = yield prdDao.findOne({ 105 | id: id 106 | }); 107 | 108 | if (!prd) { 109 | sutil.failed(this, 12002); 110 | return false; 111 | } 112 | 113 | if (prd.createUser !== user.username) { 114 | sutil.failed(this, 12001); 115 | return false; 116 | } 117 | 118 | yield prdDao.remove({ 119 | id: id 120 | }); 121 | 122 | sutil.success(this, prd); 123 | }); 124 | 125 | export default router; 126 | -------------------------------------------------------------------------------- /App/prd/redux/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import {bindActionCreators} from 'redux' 11 | import {connect} from 'react-redux'; 12 | import * as actions from './Actions' 13 | import Modal from 'antd/lib/modal'; 14 | import Button from 'antd/lib/button'; 15 | import AddPrd from '../components/AddPrd'; 16 | import PrdList from '../components/PrdList.jsx'; 17 | import Nav from '../components/Nav'; 18 | import './app.scss'; 19 | const {team, project} = pageConfig.me; 20 | 21 | class App extends Component { 22 | constructor(props) { 23 | super(props) 24 | // this.handleChange = this.handleChange.bind(this) 25 | // this.handleRefreshClick = this.handleRefreshClick.bind(this) 26 | } 27 | 28 | //初始化渲染后触发 29 | componentDidMount() { 30 | const {actions} = this.props 31 | actions.getPrds(); 32 | } 33 | 34 | addOk(prd) { 35 | const {actions} = this.props; 36 | actions.addPrd(prd); 37 | } 38 | 39 | addCancel() { 40 | const {actions} = this.props; 41 | actions.addShow(false); 42 | } 43 | 44 | render() { 45 | const self = this; 46 | const {prds, actions, addShow, updateShow, deleteShow} = this.props; 47 | 48 | 49 | return ( 50 |
51 |
79 | ) 80 | } 81 | } 82 | 83 | App.propTypes = { 84 | prds: PropTypes.array.isRequired, 85 | addShow: PropTypes.bool, 86 | updateShow: PropTypes.object 87 | } 88 | 89 | function mapDispatchToProps(dispatch) { 90 | return { 91 | actions: bindActionCreators(actions, dispatch) 92 | } 93 | } 94 | 95 | export default connect(state=> { 96 | return { 97 | prds: state.prds, 98 | addShow: state.addShow, 99 | confirmLoading: state.confirmLoading, 100 | updateShow: state.updateShow, 101 | deleteShow: state.deleteShow, 102 | } 103 | }, mapDispatchToProps)(App); 104 | 105 | /** 106 | * 107 | */ 108 | -------------------------------------------------------------------------------- /App/main/register.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-24 23:35:15 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 15:20:58 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Button from 'antd/lib/button'; 12 | import Form from 'antd/lib/form'; 13 | import Input from 'antd/lib/input'; 14 | import Alert from 'antd/lib/alert'; 15 | 16 | const FormItem = Form.Item; 17 | 18 | let Register = React.createClass ({ 19 | getInitialState: function(){ 20 | return { 21 | error: this.props.error 22 | } 23 | }, 24 | componentDidMount() { 25 | this.props.form.setFieldsValue({ 26 | username: this.props.username 27 | }); 28 | }, 29 | handleChange(date) { 30 | // this.setState({ date }); 31 | }, 32 | handleSubmit(e){ 33 | const form = this.props.form; 34 | 35 | const result = form.validateFields((errors, values) => { 36 | if (!!errors) { 37 | e.preventDefault(); 38 | return false; 39 | } 40 | 41 | }); 42 | }, 43 | handleKeyPress() { 44 | this.state.error = ''; 45 | }, 46 | render() { 47 | const form = this.props.form; 48 | const formItemLayout = { 49 | labelCol: { span: 6 }, 50 | wrapperCol: { span: 14 }, 51 | }; 52 | const { getFieldProps, getFieldError, isFieldValidating } = form; 53 | const usernameProps = getFieldProps('username', { 54 | rules: [ 55 | { required: true, message: '请输入用户名' } 56 | ] 57 | }); 58 | 59 | const passwordProps = getFieldProps('password', { 60 | rules: [ 61 | { required: true, message: '请输入密码' } 62 | ] 63 | }); 64 | return ( 65 |
66 |

注册

67 |
68 | 71 | 76 | 77 | 81 | 82 | 83 | {this.state.error ? : ''} 84 | 85 | 88 | 89 | 90 | 91 |
92 | ); 93 | } 94 | }) 95 | 96 | Register = Form.create()(Register); 97 | 98 | if (typeof document != 'undefined') { 99 | ReactDOM.render( 100 | , document.getElementById('main') 101 | ); 102 | } 103 | 104 | export default Register; 105 | -------------------------------------------------------------------------------- /App/project/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by zyy on 15/7/7. 3 | * zhangyuyu@superjia.com 4 | */ 5 | import Router from 'koa-router'; 6 | import _ from 'underscore'; 7 | const router = new Router({ 8 | prefix: '/project' 9 | }); 10 | const wrap = require('co-monk'); 11 | const db = require('../../common/db'); 12 | const userDao = wrap(db.get('user')); 13 | const projectDao = wrap(db.get('project')); 14 | 15 | import sutil from '../../common/sutil'; 16 | 17 | // 项目 18 | router.get('/', sutil.teamLogin(), function*(next) { 19 | yield sutil.render(this, {}); 20 | }); 21 | 22 | //项目详情 23 | router.get('/:projectId', sutil.setRouterParams, sutil.projectLogin, function*(next) { 24 | yield sutil.render(this, { 25 | project: yield projectDao.findOne({ 26 | id: this.parse.projectId 27 | }) 28 | }); 29 | }); 30 | 31 | // 所有数据,不受权限限制 32 | router.get('/alldata', sutil.login, function*(next) { 33 | sutil.success(this, yield projectDao.find({ 34 | teamId: this.parse.teamId 35 | }, { 36 | sort: {createTime: -1} 37 | })); 38 | }); 39 | 40 | router.get('/data', sutil.teamLogin(), function*(next) { 41 | sutil.success(this, yield projectDao.find({ 42 | teamId: this.parse.teamId 43 | }, { 44 | sort: {createTime: -1} 45 | })); 46 | }); 47 | 48 | router.post('/data', sutil.teamLogin(), function*(next) { 49 | const parse = this.parse; 50 | const user = this.locals._user; 51 | const id = yield sutil.genId(projectDao); 52 | 53 | const project = yield projectDao.insert({ 54 | id: id, 55 | name: parse.name, 56 | createUser: user.username, 57 | teamId: parse.teamId, 58 | description: parse.description, 59 | createTime: Date.now(), 60 | updateTime: Date.now() 61 | }); 62 | sutil.success(this, project); 63 | }); 64 | 65 | router.put('/data', sutil.teamLogin(), function*(next) { 66 | const parse = this.parse; 67 | const id = parse.id; 68 | const user = this.locals._user; 69 | 70 | let project = yield projectDao.findOne({ 71 | id: id 72 | }); 73 | 74 | if (!project) { 75 | sutil.failed(this, 12002); 76 | return false; 77 | } 78 | 79 | if (project.createUser !== user.username) { 80 | sutil.failed(this, 12001); 81 | return false; 82 | } 83 | 84 | 85 | yield projectDao.update({ 86 | id: parse.id 87 | }, { 88 | $set: Object.assign({ 89 | updateTime: Date.now() 90 | }, 91 | parse) 92 | }); 93 | sutil.success(this, Object.assign({}, project, parse)); 94 | }); 95 | 96 | router.del('/data', sutil.teamLogin(), function*(next) { 97 | const parse = this.parse; 98 | const id = parse.id; 99 | const user = this.locals._user; 100 | 101 | const project = yield projectDao.findOne({ 102 | id: id 103 | }); 104 | 105 | if (!project) { 106 | sutil.failed(this, 12002); 107 | return false; 108 | } 109 | 110 | if (project.createUser !== user.username) { 111 | sutil.failed(this, 12001); 112 | return false; 113 | } 114 | 115 | yield projectDao.remove({ 116 | id: id 117 | }); 118 | 119 | sutil.success(this, project); 120 | }); 121 | 122 | export default router; 123 | -------------------------------------------------------------------------------- /App/team/redux/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:50:10 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:59:05 6 | */ 7 | 8 | 'use strict'; 9 | import {combineReducers} from 'redux'; 10 | import { 11 | routerStateReducer, 12 | } from 'redux-router'; 13 | import { 14 | GET, 15 | ADD, 16 | ADD_SHOW, 17 | UPDATE, 18 | UPDATE_SHOW, 19 | DELETE, 20 | DELETE_SHOW, 21 | GET_MEMBER, 22 | INVITE_MEMBER, 23 | INVITE_MEMBER_SHOW, 24 | DELETE_MEMBER, 25 | DELETE_MEMBER_SHOW, 26 | GET_PRD, 27 | } from './action'; 28 | 29 | function teams(state = [], action) { 30 | switch (action.type) { 31 | case GET: 32 | return action.teams 33 | case ADD: 34 | return [action.team, ...state] 35 | case UPDATE: 36 | return _.map(state, item => item.id == action.team.id ? action.team : item) 37 | case DELETE: 38 | return _.filter(state, item => item.id != action.team.id) 39 | default: 40 | return state 41 | } 42 | } 43 | 44 | function addShow(state = false, action) { 45 | switch (action.type) { 46 | case ADD_SHOW: 47 | return action.addShow; 48 | case ADD: 49 | return false; 50 | default: 51 | return state 52 | } 53 | } 54 | 55 | function updateShow(state = {updateShow: false, team: {}}, action) { 56 | switch (action.type) { 57 | case UPDATE_SHOW: 58 | return Object.assign({}, action); 59 | case UPDATE: 60 | return {updateShow: false, team: action.team}; 61 | default: 62 | return state 63 | } 64 | } 65 | 66 | function deleteShow(state = {deleteShow: false, team: {}}, action) { 67 | switch (action.type) { 68 | case DELETE_SHOW: 69 | return Object.assign({}, action); 70 | case DELETE: 71 | return {deleteShow: false, team: action.team}; 72 | default: 73 | return state 74 | } 75 | } 76 | 77 | function members(state = [], action) { 78 | switch (action.type) { 79 | case GET_MEMBER: 80 | return action.members 81 | case INVITE_MEMBER: 82 | return [action.member, ...state] 83 | case DELETE_MEMBER: 84 | return _.filter(state, item => item.username != action.member.username) 85 | default: 86 | return state 87 | } 88 | } 89 | 90 | function inviteMemberShow(state = false, action) { 91 | switch (action.type) { 92 | case INVITE_MEMBER_SHOW: 93 | return action.show; 94 | case INVITE_MEMBER: 95 | return false; 96 | default: 97 | return state 98 | } 99 | } 100 | 101 | function deleteMemberShow(state = {show: false, member: {}}, action) { 102 | switch (action.type) { 103 | case DELETE_MEMBER_SHOW: 104 | return Object.assign({}, action); 105 | case DELETE_MEMBER: 106 | return {show: false, member: action.member}; 107 | default: 108 | return state 109 | } 110 | } 111 | 112 | function prds(state = [], action) { 113 | switch (action.type) { 114 | case GET_PRD: 115 | return action.prds 116 | default: 117 | return state 118 | } 119 | } 120 | 121 | const rootReducer = combineReducers({ 122 | router: routerStateReducer, 123 | teams, 124 | addShow, 125 | updateShow, 126 | deleteShow, 127 | members, 128 | inviteMemberShow, 129 | deleteMemberShow, 130 | prds 131 | }) 132 | 133 | export default rootReducer 134 | -------------------------------------------------------------------------------- /App/iconfont/redux/reducer.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:50:10 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:59:05 6 | */ 7 | 8 | 'use strict'; 9 | import {combineReducers} from 'redux'; 10 | import { 11 | routerStateReducer, 12 | } from 'redux-router'; 13 | import { 14 | GET, 15 | ADD, 16 | ADD_SHOW, 17 | UPDATE, 18 | UPDATE_SHOW, 19 | DELETE, 20 | DELETE_SHOW, 21 | GET_MEMBER, 22 | INVITE_MEMBER, 23 | INVITE_MEMBER_SHOW, 24 | DELETE_MEMBER, 25 | DELETE_MEMBER_SHOW, 26 | GET_PRD, 27 | } from './action'; 28 | 29 | function iconfonts(state = [], action) { 30 | switch (action.type) { 31 | case GET: 32 | return action.iconfonts 33 | case ADD: 34 | return [action.iconfont, ...state] 35 | case UPDATE: 36 | return _.map(state, item => item.id == action.iconfont.id ? action.iconfont : item) 37 | case DELETE: 38 | return _.filter(state, item => item.id != action.iconfont.id) 39 | default: 40 | return state 41 | } 42 | } 43 | 44 | function addShow(state = false, action) { 45 | switch (action.type) { 46 | case ADD_SHOW: 47 | return action.addShow; 48 | case ADD: 49 | return false; 50 | default: 51 | return state 52 | } 53 | } 54 | 55 | function updateShow(state = {updateShow: false, iconfont: {}}, action) { 56 | switch (action.type) { 57 | case UPDATE_SHOW: 58 | return Object.assign({}, action); 59 | case UPDATE: 60 | return {updateShow: false, iconfont: action.iconfont}; 61 | default: 62 | return state 63 | } 64 | } 65 | 66 | function deleteShow(state = {deleteShow: false, iconfont: {}}, action) { 67 | switch (action.type) { 68 | case DELETE_SHOW: 69 | return Object.assign({}, action); 70 | case DELETE: 71 | return {deleteShow: false, iconfont: action.iconfont}; 72 | default: 73 | return state 74 | } 75 | } 76 | 77 | function members(state = [], action) { 78 | switch (action.type) { 79 | case GET_MEMBER: 80 | return action.members 81 | case INVITE_MEMBER: 82 | return [action.member, ...state] 83 | case DELETE_MEMBER: 84 | return _.filter(state, item => item.username != action.member.username) 85 | default: 86 | return state 87 | } 88 | } 89 | 90 | function inviteMemberShow(state = false, action) { 91 | switch (action.type) { 92 | case INVITE_MEMBER_SHOW: 93 | return action.show; 94 | case INVITE_MEMBER: 95 | return false; 96 | default: 97 | return state 98 | } 99 | } 100 | 101 | function deleteMemberShow(state = {show: false, member: {}}, action) { 102 | switch (action.type) { 103 | case DELETE_MEMBER_SHOW: 104 | return Object.assign({}, action); 105 | case DELETE_MEMBER: 106 | return {show: false, member: action.member}; 107 | default: 108 | return state 109 | } 110 | } 111 | 112 | function prds(state = [], action) { 113 | switch (action.type) { 114 | case GET_PRD: 115 | return action.prds 116 | default: 117 | return state 118 | } 119 | } 120 | 121 | const rootReducer = combineReducers({ 122 | router: routerStateReducer, 123 | iconfonts, 124 | addShow, 125 | updateShow, 126 | deleteShow, 127 | members, 128 | inviteMemberShow, 129 | deleteMemberShow, 130 | prds 131 | }) 132 | 133 | export default rootReducer 134 | -------------------------------------------------------------------------------- /App/layout/layout.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-31 14:07:40 4 | * @Last modified by: lancui 5 | * @Last modified time: 2016-07-08 17:07:86 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Menu from 'antd/lib/menu'; 12 | import Icon from 'antd/lib/icon'; 13 | import menus from './menu'; 14 | import util from '../../common/util'; 15 | import _ from 'underscore'; 16 | 17 | const SubMenu = Menu.SubMenu; 18 | const MenuItemGroup = Menu.ItemGroup; 19 | 20 | class Header extends React.Component { 21 | constructor(props) { 22 | super(props); 23 | } 24 | render() { 25 | const user = this.props.user; 26 | return ( 27 |
28 |
29 | 30 | { 31 | this.props.menus.map( menu => 32 | util.showIf(menu.subMenus, 33 | 34 | { 35 | menu.subMenus && menu.subMenus.map(subMenu => 36 | 37 | {subMenu.text} 38 | 39 | ) 40 | } 41 | , 42 | 43 | {menu.text} 44 | ) 45 | ) 46 | } 47 | 48 |
49 |
50 | 51 | 52 | 消息 (0) 53 | 54 | { 55 | util.showIf(user.username, 56 | 57 | 个人设置 58 | 退出 59 | , 60 | 61 | 登录 62 | ) 63 | } 64 | 65 |
66 |
67 | ); 68 | } 69 | } 70 | 71 | if (typeof document != 'undefined') { 72 | const staticTag = pageConfig.staticTag; 73 | const currentPageConfig = pageConfig[staticTag]; 74 | if (!currentPageConfig.noHeader) { 75 | ReactDOM.render( 76 |
, document.getElementById('header') 77 | ); 78 | } 79 | 80 | } 81 | 82 | export { 83 | Header 84 | }; 85 | -------------------------------------------------------------------------------- /App/user/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-08-29 10:08:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-08-29 19:08:86 7 | */ 8 | 9 | 10 | 11 | /** 12 | * Created by zyy on 15/7/7. 13 | * zhangyuyu@superjia.com 14 | */ 15 | import fs from 'fs' 16 | import path from 'path' 17 | import _ from 'underscore' 18 | import busBoy from 'co-busboy' 19 | import Router from 'koa-router'; 20 | import sutil from '../../common/sutil' 21 | const router = new Router({ prefix: '/user'}) 22 | const wrap = require('co-monk') 23 | const db = require('../../common/db') 24 | const userDao = wrap(db.get('user')) 25 | 26 | // PRD 27 | router.get('/profile', sutil.login, function*(next) { 28 | yield sutil.render(this, {}) 29 | }) 30 | 31 | router.get('/data', sutil.login, function*(next) { 32 | let name = this.locals._user.username 33 | let data = yield userDao.findOne({username: name}) 34 | sutil.success(this, { 35 | sex: data.sex, 36 | role: data.role, 37 | phone: data.phone, 38 | username: data.username, 39 | message: data.message, 40 | remarks: data.remarks 41 | }) 42 | // if(user) { 43 | // sutil.success(this, sutil.wrapUser(user, ['teams'])); 44 | // }else { 45 | // sutil.failed(this, 10004); 46 | // } 47 | }) 48 | router.post('/upload', sutil.login, function*(next) { 49 | const {sex, name, phone, role, message, remarks} = this.parse 50 | if (!name) { 51 | sutil.failed(this, 1003) 52 | } else { 53 | const data = { 54 | sex: sex, 55 | role: role, 56 | phone: phone, 57 | message: message, 58 | remarks: remarks 59 | } 60 | let updateResult = yield userDao.update({ username: name }, { 61 | $set: data 62 | }) 63 | if (updateResult) { 64 | sutil.success(this) 65 | } 66 | } 67 | }) 68 | 69 | router.post('/uploadPassword', sutil.login, function*(next) { 70 | const { oldPassword, newPassword, username } = this.parse 71 | if (!oldPassword || !newPassword || !username) { 72 | sutil.failed(this) 73 | } else { 74 | const password = sutil.wrapUserPass(oldPassword) 75 | const user = yield userDao.findOne({ username: username }) 76 | if (user.password === password) { 77 | const newPassworddate = sutil.wrapUserPass(newPassword) 78 | const result = yield userDao.update({ username: username }, { 79 | $set: {password: newPassworddate} 80 | }) 81 | sutil.success(this) 82 | } else { 83 | sutil.failed(this) 84 | } 85 | } 86 | }) 87 | 88 | router.post('/uploadPic', sutil.login, function*(next) { 89 | const parts = busBoy(this) 90 | const username = this.locals._user.username 91 | let part 92 | while (part = yield parts) { 93 | const type = part.filename.substr(part.filename.lastIndexOf('.')) 94 | const filePath = path.join(__dirname, '../../assets/avatar/' + username + type) 95 | if (type === '.jpg' || type === '.png' || type ==='gif' || type === '.jpeg') { 96 | const stream = fs.createWriteStream(filePath) 97 | part.pipe(stream) 98 | const result = yield userDao.update({ username: username }, { 99 | $set: {img: username + type} 100 | }) 101 | if (result) sutil.success(this, username + type) 102 | // console.log('uploading %s -> %s', part.filename, stream.path) 103 | } else { 104 | sutil.failed(this) 105 | } 106 | } 107 | }) 108 | 109 | //.replace(/[^ \n.,]+/g, m=>m.length > 5 ? m.replace(/./g, '?'): m) 110 | 111 | export default router; 112 | -------------------------------------------------------------------------------- /App/message/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-24 15:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-08-09 11:08:20 7 | */ 8 | 9 | const wrap = require('co-monk') 10 | const db = require('../../common/db') 11 | import sutil from '../../common/sutil' 12 | import Router from 'koa-router' 13 | 14 | const router = new Router({ 15 | prefix: '/message' 16 | }) 17 | 18 | // CURD for message 19 | const msgDao = wrap(db.get('message')) 20 | const userDao = wrap(db.get('user')) 21 | router.get('/', sutil.login, function*(next) { 22 | yield sutil.render(this, { 23 | commonTag: 'vue', 24 | html: '', 25 | staticTag: 'message', 26 | noHeader: true 27 | }) 28 | }) 29 | 30 | // 获取用户的所有消息 31 | router.get('/messages', sutil.login, function* (next) { 32 | /** 33 | * userId: 需要推送过去的人的userId 34 | * pageSize: 每一页所需要的消息条数 35 | * pageIndex: 需要的是第几页的数据 36 | */ 37 | let [uid, pageSize, pageIndex] = [this.parse.userId, this.parse.pageSize, this.parse.pageIndex] 38 | if (!uid) { 39 | sutil.failed(this, 1003) 40 | } 41 | let msgs = yield msgDao.find({'toUsers.userId': uid}, {sort: {createTime:-1, status:1}, limit: pageSize, skip: (pageIndex - 1)*pageSize}) 42 | let count = yield msgDao.count({'toUsers.userId': uid}) 43 | 44 | for(let j in msgs) { 45 | let status = 0, toUsers = msgs[j].toUsers 46 | for(let i in toUsers){ 47 | let tu = toUsers[i] 48 | if(tu.userId === uid){ 49 | msgs[j].status = tu.status 50 | const statusMap = { 0: '未读', 1: '已读', 2: (!tu.resultText ? '已操作' : tu.resultText) } 51 | // console.log(`tu.resultText===${tu.resultText}`) 52 | msgs[j].resultText = statusMap[tu.status] 53 | break 54 | } 55 | } 56 | } 57 | let message = { 58 | msgs, 59 | count 60 | } 61 | sutil.success(this, message) 62 | }) 63 | 64 | // 更新用户消息状态为已读,如果没有传msgId,则更新所有消息状态为已读 65 | router.put('/messages', sutil.login, function* (next) { 66 | let _parse = this.parse, uid = _parse.userId 67 | if (!_parse.userId) { 68 | sutil.failed(this, 1003) 69 | } 70 | let {msgId, status, resultText} = _parse 71 | // 如果msgId=null,则只更新action不等于'invited'的状态为已读 72 | let query = !msgId ? {'toUsers.userId': uid, action: {$nin: ['invited']}} : {'toUsers.userId': uid, id: msgId} 73 | let multi = !msgId ? true : false //批量修改 74 | let updateRes = yield msgDao.update(query, { $set: {'toUsers.$.status': status, 'toUsers.$.resultText': resultText}}, {multi: multi}) 75 | 76 | //更新消息 77 | sutil.updateClientMsg([uid]) 78 | 79 | sutil.success(this, updateRes) 80 | }) 81 | 82 | // 新增消息 83 | router.post('/messages', sutil.login, function* (next) { 84 | let _parse = this.parse 85 | if (!_parse.userId) { 86 | sutil.failed(this, 1003) 87 | } 88 | // 添加消息,并提醒客户端 89 | let insertResult = yield sutil.addMessage(_parse.msgData, null, ['lancui']) 90 | 91 | if (insertResult) { 92 | sutil.success(this, insertResult) 93 | } else { 94 | sutil.failed(this, 150001) 95 | } 96 | }) 97 | // 删除消息 98 | router.delete('/messages', sutil.login, function* (next) { 99 | let _parse = this.parse, userIds = _parse.userIds 100 | if (!userIds) { 101 | sutil.failed(this, 1003) 102 | } 103 | let delResult = yield msgDao.remove({id: {$in: userIds.split(',')}}) 104 | 105 | if (delResult) { 106 | sutil.success(this, delResult) 107 | } else { 108 | sutil.failed(this, 150001) 109 | } 110 | }) 111 | 112 | 113 | export default router 114 | -------------------------------------------------------------------------------- /App/importdb/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-24 15:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-17 09:07:25 7 | */ 8 | 9 | // old database 10 | import config from '../../config'; 11 | var monk = require('monk'); 12 | var old_db = monk('localhost/iwfe'); 13 | 14 | const wrap = require('co-monk'); 15 | const db = require('../../common/db'); 16 | import sutil from '../../common/sutil'; 17 | import util from '../../common/util'; 18 | import Router from 'koa-router'; 19 | 20 | const router = new Router({ 21 | prefix: '/importdb' 22 | }); 23 | 24 | // CURD for message 25 | const old_apiDao = wrap(old_db.get('api-api')); 26 | const old_prdDao = wrap(old_db.get('api-prd')); 27 | const apiDao = wrap(db.get('api')); 28 | router.get('/', sutil.login, function*(next) { 29 | yield sutil.render(this, { 30 | commonTag: 'vue', 31 | html: '', 32 | staticTag: 'importdb', 33 | noHeader: true 34 | }); 35 | }); 36 | 37 | // 更新用户消息状态为已读,如果没有传msgId,则更新所有消息状态为已读 38 | router.put('/importdata', sutil.login, function* (next) { 39 | let self = this, _parse = this.parse, uid = _parse.userId; 40 | if (!_parse.userId) { 41 | sutil.failed(this, 1003); 42 | } 43 | 44 | function* initApi(oldApi, root, teamId, projectId, prdId) { 45 | let old_output = JSON.parse(oldApi.output), 46 | new_output = util.Json2MockTree(old_output); 47 | 48 | let newApi = { 49 | id: yield sutil.genId(apiDao, 8), 50 | createTime: new Date(), 51 | updateTime: new Date(), 52 | userId: self.locals._user._id, 53 | userName: self.locals._user.username, 54 | 55 | title: oldApi.desc, // 标题 56 | url: oldApi.url, // 请求url 57 | method: oldApi.method.toLocaleUpperCase(), //提交方式 (get post) 58 | input: JSON.parse(oldApi.input), // 入参 59 | output: new_output, 60 | outputJson: old_output, 61 | status: 1, // 1 新建, 2 修改 62 | updateDescList: [{ // 修改说明list 63 | updateTime: new Date, // 更新时间 64 | userName: self.locals._user.username, // 用户名 65 | updateDesc: '迁移接口' // 更新说明 66 | }], 67 | root: root, // 如 weixinEnt 68 | prdId: prdId, // PrdID 69 | projectId: projectId, // 项目Id 70 | teamId: teamId // 项目组Id 71 | }; 72 | return newApi; 73 | } 74 | 75 | let root = _parse.root, teamId = _parse.teamId, projectId = _parse.projectId, prdId = _parse.prdId, oldPrdId = _parse.oldPrdId; 76 | console.log(`${root} ${teamId} ${projectId} ${prdId} ${oldPrdId}`); 77 | 78 | let opidArr = oldPrdId.replace(/ /g, '').replace(/,/g, ',').split(","); 79 | let count = 0, resApis=[]; 80 | for (let j in opidArr) { 81 | let oldpid = opidArr[j]; 82 | let opid = yield old_prdDao.find({'uuid': oldpid}); 83 | if(!opid || opid.length == 0) break; 84 | let oldApis = yield old_apiDao.find({'prdId': opid[0]._id.toString()}); 85 | 86 | for(let i in oldApis) { 87 | let oapi = oldApis[i]; 88 | 89 | // 如果已经存在api,则不新增 90 | let eapi = yield apiDao.find({prdId: prdId, url: oapi.url, method: oapi.method.toLocaleUpperCase()}); 91 | if(eapi.length && eapi.length > 0) continue; 92 | 93 | let napi = yield* initApi(oapi, root, teamId, projectId, prdId); 94 | let resApi = yield apiDao.insert(napi); 95 | console.log(`===新增成功一条API:prdId:${prdId}`); 96 | count++; 97 | resApis.push(resApi); 98 | } 99 | } 100 | 101 | sutil.success(this, { 102 | code: 200, 103 | count: count, 104 | data: resApis 105 | }); 106 | }); 107 | 108 | export default router; 109 | -------------------------------------------------------------------------------- /App/main/login.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-05-24 23:35:15 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-06 15:20:58 6 | */ 7 | 8 | 'use strict'; 9 | import React from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Button from 'antd/lib/button'; 12 | import Form from 'antd/lib/form'; 13 | import Input from 'antd/lib/input'; 14 | import Alert from 'antd/lib/alert'; 15 | 16 | const FormItem = Form.Item; 17 | 18 | let Login = React.createClass ({ 19 | getInitialState: function(){ 20 | return { 21 | error: this.props.error 22 | } 23 | }, 24 | componentDidMount() { 25 | this.props.form.setFieldsValue({ 26 | username: this.props.username 27 | }); 28 | }, 29 | handleChange(date) { 30 | // this.setState({ date }); 31 | }, 32 | handleSubmit(e){ 33 | const form = this.props.form; 34 | 35 | const result = form.validateFields((errors, values) => { 36 | if (!!errors) { 37 | e.preventDefault(); 38 | return false; 39 | } 40 | 41 | }); 42 | console.log(result); 43 | }, 44 | handleKeyPress() { 45 | this.state.error = ''; 46 | }, 47 | render() { 48 | const form = this.props.form; 49 | const formItemLayout = { 50 | labelCol: { span: 6 }, 51 | wrapperCol: { span: 14 }, 52 | }; 53 | const { getFieldProps, getFieldError, isFieldValidating } = form; 54 | const usernameProps = getFieldProps('username', { 55 | rules: [ 56 | { required: true, message: '请输入用户名' } 57 | ] 58 | }); 59 | 60 | const passwordProps = getFieldProps('password', { 61 | rules: [ 62 | { required: true, message: '请输入密码' } 63 | ] 64 | }); 65 | return ( 66 |
67 |

登录

68 |
69 | 72 | 77 | 78 | 82 | 83 | 84 | {this.state.error ? : ''} 85 | 86 | 89 | 90 | 91 | 92 |
93 | ); 94 | } 95 | }) 96 | 97 | Login = Form.create()(Login); 98 | 99 | if (typeof document != 'undefined') { 100 | ReactDOM.render( 101 | , document.getElementById('main') 102 | ); 103 | } 104 | 105 | export default Login; 106 | -------------------------------------------------------------------------------- /App/project/redux/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, { Component, PropTypes } from 'react'; 10 | import { bindActionCreators } from 'redux' 11 | import { connect } from 'react-redux'; 12 | import * as actions from './action' 13 | import Icon from 'antd/lib/icon'; 14 | import Card from 'antd/lib/card'; 15 | import Col from 'antd/lib/col'; 16 | import Row from 'antd/lib/row'; 17 | import Project from '../components/Project'; 18 | import AddProject from '../components/AddProject'; 19 | import Nav from '../components/Nav'; 20 | import './app.scss'; 21 | const team = pageConfig.me.team; 22 | 23 | class App extends Component { 24 | constructor(props) { 25 | super(props) 26 | // this.handleChange = this.handleChange.bind(this) 27 | // this.handleRefreshClick = this.handleRefreshClick.bind(this) 28 | } 29 | 30 | //初始化渲染后触发 31 | componentDidMount() { 32 | const { actions } = this.props 33 | actions.getProjects(); 34 | } 35 | 36 | addOk(project) { 37 | const {actions} = this.props; 38 | actions.addProject(project); 39 | } 40 | 41 | addCancel() { 42 | const {actions} = this.props; 43 | actions.addShow(false); 44 | } 45 | 46 | render() { 47 | const self = this; 48 | const { projects, actions, addShow, updateShow, deleteShow } = this.props; 49 | return ( 50 |
51 |
80 | ) 81 | } 82 | } 83 | 84 | App.propTypes = { 85 | projects: PropTypes.array.isRequired, 86 | addShow: PropTypes.bool, 87 | updateShow: PropTypes.object 88 | } 89 | 90 | function mapDispatchToProps(dispatch) { 91 | return { 92 | actions: bindActionCreators(actions, dispatch) 93 | } 94 | } 95 | 96 | export default connect(state=>{ 97 | return { 98 | projects:state.projects, 99 | addShow: state.addShow, 100 | confirmLoading: state.confirmLoading, 101 | updateShow: state.updateShow, 102 | deleteShow: state.deleteShow, 103 | } 104 | }, mapDispatchToProps)(App); 105 | 106 | /** 107 | * 108 | */ 109 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sri-node", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "start": "node ./bin/server", 7 | "dev-vue": "webpack-dev-server --port=3811 --config=build/webpack.vue.dev.config.js --inline --hot", 8 | "build": "gulp --product && gulp --zip", 9 | "server": "supervisor --harmony server.js" 10 | }, 11 | "dependencies": { 12 | "adm-zip": "*", 13 | "antd": "*", 14 | "arale": "^0.2.0", 15 | "babel-core": "^6.7.4", 16 | "babel-loader": "^6.2.4", 17 | "babel-plugin-transform-async-to-generator": "^6.0.14", 18 | "babel-plugin-transform-runtime": "^6.6.0", 19 | "babel-preset-es2015": "^6.6.0", 20 | "babel-preset-es2015-node5": "^1.2.0", 21 | "babel-preset-react": "^6.5.0", 22 | "babel-preset-stage-0": "^6.5.0", 23 | "babel-preset-stage-3": "^6.5.0", 24 | "babel-runtime": "^5.8.38", 25 | "bootstrap": "*", 26 | "bootstrap-webpack": "0.0.5", 27 | "bundle-loader": "^0.5.4", 28 | "classnames": "*", 29 | "clipboard": "^1.5.12", 30 | "co": "*", 31 | "co-body": "*", 32 | "co-busboy": "*", 33 | "co-fs": "*", 34 | "co-monk": "*", 35 | "co-views": "*", 36 | "codemirror": "^5.16.0", 37 | "components-jqueryui": "github:components/jqueryui", 38 | "css-loader": "^0.16.0", 39 | "emit": "0.0.2", 40 | "exports-loader": "^0.6.2", 41 | "extract-text-webpack-plugin": "^0.8.2", 42 | "file": "^0.2.2", 43 | "file-loader": "~0.8.4", 44 | "fs": "0.0.2", 45 | "fs-extra": "*", 46 | "fullcalendar": "*", 47 | "gulp-minify-css": "*", 48 | "gulp-rename": "*", 49 | "gulp-uglify": "*", 50 | "gulp-zip": "*", 51 | "history": "^1.13.0", 52 | "imports-loader": "^0.6.5", 53 | "jade": "~1.11.0", 54 | "jquery": "~2.1.4", 55 | "jsonlint": "^1.6.2", 56 | "jsx-loader": "~0.13.2", 57 | "koa": "*", 58 | "koa-bodyparser": "*", 59 | "koa-convert": "^1.2.0", 60 | "koa-generic-session": "*", 61 | "koa-json": "*", 62 | "koa-logger": "*", 63 | "koa-mount": "*", 64 | "koa-onerror": "^1.2.1", 65 | "koa-router": "*", 66 | "koa-static": "*", 67 | "koa-views": "^4.1.0", 68 | "less": "^2.5.3", 69 | "less-loader": "^2.2.1", 70 | "mockjs": "^1.0.1-beta2", 71 | "monk": "*", 72 | "node-sass": "^3.4.2", 73 | "node-uuid": "*", 74 | "nodemailer": "^1.2.1", 75 | "react": "^15.0.1", 76 | "react-dom": "^15.0.1", 77 | "react-hot-loader": "^1.2.8", 78 | "react-redux": "^4.0.0", 79 | "react-router": "*", 80 | "redux": "^3.0.0", 81 | "redux-devtools": "*", 82 | "redux-logger": "^2.0.2", 83 | "redux-router": "^2.0.0", 84 | "redux-thunk": "^0.1.0", 85 | "sass-loader": "^3.1.2", 86 | "semantic-ui": "~2.1.8", 87 | "set-iterm2-badge": "*", 88 | "socket.io": "*", 89 | "style-loader": "~0.12.3", 90 | "swig": "*", 91 | "system": "^1.0.6", 92 | "through2": "*", 93 | "toastr": "^2.1.2", 94 | "underscore": "*", 95 | "url-loader": "~0.5.6", 96 | "vue": "^1.0.25", 97 | "vue-router": "^0.7.13", 98 | "vuex": "^0.8.0", 99 | "webpack": "~1.11.0", 100 | "webpack-dev-server": "^1.10.1", 101 | "whatwg-fetch": "*" 102 | }, 103 | "devDependencies": { 104 | "eslint": "^2.13.1", 105 | "eslint-config-airbnb": "^9.0.1", 106 | "eslint-loader": "^1.3.0", 107 | "eslint-plugin-import": "^1.9.2", 108 | "eslint-plugin-jsx-a11y": "^1.5.3", 109 | "eslint-plugin-react": "^5.2.2", 110 | "gulp": "^3.8.11", 111 | "gulp-nodemon": "^2.1.0", 112 | "gulp-util": "^3.0.7", 113 | "vue-hot-reload-api": "^1.3.3", 114 | "vue-html-loader": "^1.2.2", 115 | "vue-loader": "^8.5.2", 116 | "vue-style-loader": "^1.0.0", 117 | "webpack-notifier": "^1.3.0" 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /App/team/redux/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import {bindActionCreators} from 'redux' 11 | import {connect} from 'react-redux'; 12 | import * as actions from './action' 13 | import Icon from 'antd/lib/icon'; 14 | import Card from 'antd/lib/card'; 15 | import Col from 'antd/lib/col'; 16 | import Row from 'antd/lib/row'; 17 | import Team from '../components/Team'; 18 | import AddTeam from '../components/AddTeam'; 19 | import './app.scss'; 20 | 21 | class App extends Component { 22 | constructor(props) { 23 | super(props) 24 | // this.handleChange = this.handleChange.bind(this) 25 | // this.handleRefreshClick = this.handleRefreshClick.bind(this) 26 | } 27 | 28 | //初始化渲染后触发 29 | componentDidMount() { 30 | console.log('执行componentDidMount'); 31 | const {actions} = this.props 32 | actions.getTeams(); 33 | } 34 | 35 | //每次接受新的props触发 36 | componentWillReceiveProps(nextProps) { 37 | // console.log('执行componentWillReceiveProps', nextProps); 38 | // if (nextProps.selectedReddit !== this.props.selectedReddit) { 39 | // const { dispatch, selectedReddit } = nextProps 40 | // dispatch(fetchPostsIfNeeded(selectedReddit)) 41 | // } 42 | } 43 | 44 | handleChange(nextReddit) { 45 | // this.props.dispatch(selectReddit(nextReddit)) 46 | } 47 | 48 | handleRefreshClick(e) { 49 | // e.preventDefault() 50 | 51 | // const { dispatch, selectedReddit } = this.props 52 | // dispatch(invalidateReddit(selectedReddit)) 53 | // dispatch(fetchPostsIfNeeded(selectedReddit)) 54 | } 55 | 56 | addOk(team) { 57 | const {actions} = this.props; 58 | actions.addTeam(team); 59 | } 60 | 61 | addCancel() { 62 | const {actions} = this.props; 63 | actions.addShow(false); 64 | } 65 | 66 | render() { 67 | const self = this; 68 | const {teams, actions, addShow, updateShow, deleteShow} = this.props; 69 | return ( 70 |
71 | 72 | { 73 | teams.map(item=> 74 | 79 | 85 | 86 | ) 87 | } 88 | 94 | actions.addShow(true)}> 95 | 96 | 97 | 98 |
99 | ) 100 | } 101 | } 102 | 103 | App.propTypes = { 104 | teams: PropTypes.array.isRequired, 105 | addShow: PropTypes.bool, 106 | updateShow: PropTypes.object 107 | } 108 | 109 | function mapDispatchToProps(dispatch) { 110 | return { 111 | actions: bindActionCreators(actions, dispatch) 112 | } 113 | } 114 | 115 | export default connect(state=> { 116 | return { 117 | teams: state.teams, 118 | addShow: state.addShow, 119 | confirmLoading: state.confirmLoading, 120 | updateShow: state.updateShow, 121 | deleteShow: state.deleteShow, 122 | } 123 | }, mapDispatchToProps)(App); 124 | -------------------------------------------------------------------------------- /App/project/components/AddProject.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-28 13:20:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:53:54 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Modal from 'antd/lib/modal'; 12 | import Form from 'antd/lib/form'; 13 | import Input from 'antd/lib/input'; 14 | import Alert from 'antd/lib/alert'; 15 | 16 | const FormItem = Form.Item; 17 | 18 | class AddProject extends Component { 19 | constructor(props, context) { 20 | super(props, context) 21 | } 22 | 23 | handleProject(type = 'add', project = {}) { 24 | return evt => { 25 | if (type == 'add') { 26 | //添加 27 | } 28 | } 29 | } 30 | 31 | componentDidMount() { 32 | const {project, form} = this.props; 33 | form.setFieldsValue({ 34 | name: project && project.name, 35 | description: project && project.description 36 | }); 37 | } 38 | 39 | handleOk() { 40 | const {form, okCallback, project} = this.props; 41 | const result = form.validateFields((errors, values) => { 42 | if (!!errors) { 43 | return false; 44 | } else { 45 | okCallback && okCallback(Object.assign({}, form.getFieldsValue(['name', 'description']), { 46 | id: project && project.id 47 | })); 48 | } 49 | }); 50 | } 51 | 52 | handleCancel() { 53 | const {cancelCallback} = this.props; 54 | cancelCallback && cancelCallback(); 55 | } 56 | 57 | render() { 58 | const self = this; 59 | const {project, type, visible, form, error} = this.props; 60 | const {getFieldProps, setFieldsValue, isFieldValidating} = form; 61 | const formItemLayout = { 62 | labelCol: {span: 6}, 63 | wrapperCol: {span: 14}, 64 | }; 65 | const nameProps = getFieldProps('name', { 66 | // value: project && project.name, 67 | rules: [ 68 | {required: true, message: '请输入项目名称'} 69 | ] 70 | }); 71 | 72 | const descriptionProps = getFieldProps('description', { 73 | }); 74 | 75 | return ( 76 | 82 |
83 | 86 | 93 | 94 | 98 | 103 | 104 | {error ? : null} 105 | 106 |
107 | ) 108 | } 109 | } 110 | 111 | AddProject = Form.create()(AddProject); 112 | 113 | export default AddProject; 114 | -------------------------------------------------------------------------------- /App/iconfont/redux/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import {bindActionCreators} from 'redux' 11 | import {connect} from 'react-redux'; 12 | import * as actions from './action' 13 | import Icon from 'antd/lib/icon'; 14 | import Card from 'antd/lib/card'; 15 | import Col from 'antd/lib/col'; 16 | import Row from 'antd/lib/row'; 17 | import Iconfont from '../components/Iconfont'; 18 | import AddIconfont from '../components/AddIconfont'; 19 | import './app.scss'; 20 | 21 | class App extends Component { 22 | constructor(props) { 23 | super(props) 24 | // this.handleChange = this.handleChange.bind(this) 25 | // this.handleRefreshClick = this.handleRefreshClick.bind(this) 26 | } 27 | 28 | //初始化渲染后触发 29 | componentDidMount() { 30 | console.log('执行componentDidMount'); 31 | const {actions} = this.props 32 | actions.getIconfonts(); 33 | } 34 | 35 | //每次接受新的props触发 36 | componentWillReceiveProps(nextProps) { 37 | // console.log('执行componentWillReceiveProps', nextProps); 38 | // if (nextProps.selectedReddit !== this.props.selectedReddit) { 39 | // const { dispatch, selectedReddit } = nextProps 40 | // dispatch(fetchPostsIfNeeded(selectedReddit)) 41 | // } 42 | } 43 | 44 | handleChange(nextReddit) { 45 | // this.props.dispatch(selectReddit(nextReddit)) 46 | } 47 | 48 | handleRefreshClick(e) { 49 | // e.preventDefault() 50 | 51 | // const { dispatch, selectedReddit } = this.props 52 | // dispatch(invalidateReddit(selectedReddit)) 53 | // dispatch(fetchPostsIfNeeded(selectedReddit)) 54 | } 55 | 56 | addOk(iconfont) { 57 | const {actions} = this.props; 58 | actions.addIconfont(iconfont); 59 | } 60 | 61 | addCancel() { 62 | const {actions} = this.props; 63 | actions.addShow(false); 64 | } 65 | 66 | render() { 67 | const self = this; 68 | const {iconfonts, actions, addShow, updateShow, deleteShow} = this.props; 69 | return ( 70 |
71 | 72 | { 73 | iconfonts.map(item=> 74 | 79 | 85 | 86 | ) 87 | } 88 | 94 | actions.addShow(true)}> 95 | 96 | 97 | 98 |
99 | ) 100 | } 101 | } 102 | 103 | App.propTypes = { 104 | iconfonts: PropTypes.array.isRequired, 105 | addShow: PropTypes.bool, 106 | updateShow: PropTypes.object 107 | } 108 | 109 | function mapDispatchToProps(dispatch) { 110 | return { 111 | actions: bindActionCreators(actions, dispatch) 112 | } 113 | } 114 | 115 | export default connect(state=> { 116 | return { 117 | iconfonts: state.iconfonts, 118 | addShow: state.addShow, 119 | confirmLoading: state.confirmLoading, 120 | updateShow: state.updateShow, 121 | deleteShow: state.deleteShow, 122 | } 123 | }, mapDispatchToProps)(App); 124 | -------------------------------------------------------------------------------- /App/team/components/AddTeam.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-28 13:20:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:53:54 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Modal from 'antd/lib/modal'; 12 | import Form from 'antd/lib/form'; 13 | import Input from 'antd/lib/input'; 14 | import Alert from 'antd/lib/alert'; 15 | 16 | const FormItem = Form.Item; 17 | 18 | class AddTeam extends Component { 19 | constructor(props, context) { 20 | super(props, context) 21 | } 22 | 23 | handleTeam(type = 'add', team = {}) { 24 | return evt => { 25 | if (type == 'add') { 26 | //添加 27 | } 28 | } 29 | } 30 | 31 | componentDidMount() { 32 | const {team, form} = this.props; 33 | form.setFieldsValue({ 34 | name: team && team.name, 35 | description: team && team.description 36 | }); 37 | } 38 | 39 | handleOk() { 40 | const {form, okCallback, team} = this.props; 41 | const result = form.validateFields((errors, values) => { 42 | if (!!errors) { 43 | return false; 44 | } else { 45 | okCallback && okCallback(Object.assign({}, form.getFieldsValue(['name', 'description']), { 46 | id: team && team.id 47 | })); 48 | } 49 | }); 50 | } 51 | 52 | handleCancel() { 53 | const {cancelCallback} = this.props; 54 | cancelCallback && cancelCallback(); 55 | } 56 | 57 | render() { 58 | const self = this; 59 | const {team, type, visible, form, error} = this.props; 60 | const {getFieldProps, setFieldsValue, isFieldValidating} = form; 61 | const formItemLayout = { 62 | labelCol: {span: 6}, 63 | wrapperCol: {span: 14}, 64 | }; 65 | const nameProps = getFieldProps('name', { 66 | // value: team && team.name, 67 | rules: [ 68 | {required: true, message: '请输入团队名称'} 69 | ] 70 | }); 71 | 72 | const descriptionProps = getFieldProps('description', { 73 | // rules: [ 74 | // { required: true, message: '请输入团队描述' } 75 | // ] 76 | }); 77 | 78 | return ( 79 | 85 |
86 | 89 | 96 | 97 | 101 | 106 | 107 | {error ? : null} 108 | 109 |
110 | ) 111 | } 112 | } 113 | 114 | AddTeam = Form.create()(AddTeam); 115 | 116 | export default AddTeam; 117 | -------------------------------------------------------------------------------- /App/iconfont/components/AddTeam.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-28 13:20:29 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:53:54 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import ReactDOM from 'react-dom'; 11 | import Modal from 'antd/lib/modal'; 12 | import Form from 'antd/lib/form'; 13 | import Input from 'antd/lib/input'; 14 | import Alert from 'antd/lib/alert'; 15 | 16 | const FormItem = Form.Item; 17 | 18 | class AddIconfont extends Component { 19 | constructor(props, context) { 20 | super(props, context) 21 | } 22 | 23 | handleIconfont(type = 'add', iconfont = {}) { 24 | return evt => { 25 | if (type == 'add') { 26 | //添加 27 | } 28 | } 29 | } 30 | 31 | componentDidMount() { 32 | const {iconfont, form} = this.props; 33 | form.setFieldsValue({ 34 | name: iconfont && iconfont.name, 35 | description: iconfont && iconfont.description 36 | }); 37 | } 38 | 39 | handleOk() { 40 | const {form, okCallback, iconfont} = this.props; 41 | const result = form.validateFields((errors, values) => { 42 | if (!!errors) { 43 | return false; 44 | } else { 45 | okCallback && okCallback(Object.assign({}, form.getFieldsValue(['name', 'description']), { 46 | id: iconfont && iconfont.id 47 | })); 48 | } 49 | }); 50 | } 51 | 52 | handleCancel() { 53 | const {cancelCallback} = this.props; 54 | cancelCallback && cancelCallback(); 55 | } 56 | 57 | render() { 58 | const self = this; 59 | const {iconfont, type, visible, form, error} = this.props; 60 | const {getFieldProps, setFieldsValue, isFieldValidating} = form; 61 | const formItemLayout = { 62 | labelCol: {span: 6}, 63 | wrapperCol: {span: 14}, 64 | }; 65 | const nameProps = getFieldProps('name', { 66 | // value: iconfont && iconfont.name, 67 | rules: [ 68 | {required: true, message: '请输入团队名称'} 69 | ] 70 | }); 71 | 72 | const descriptionProps = getFieldProps('description', { 73 | // rules: [ 74 | // { required: true, message: '请输入团队描述' } 75 | // ] 76 | }); 77 | 78 | return ( 79 | 85 |
86 | 89 | 96 | 97 | 101 | 106 | 107 | {error ? : null} 108 | 109 |
110 | ) 111 | } 112 | } 113 | 114 | AddIconfont = Form.create()(AddIconfont); 115 | 116 | export default AddIconfont; 117 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-27 13:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-07-14 17:07:11 7 | */ 8 | import Koa from 'koa'; 9 | import bodyParser from 'koa-bodyparser'; 10 | import logger from 'koa-logger'; 11 | import views from 'koa-views'; 12 | import mount from 'koa-mount'; 13 | import serve from 'koa-static'; 14 | import config from './config'; 15 | import sutil from './common/sutil'; 16 | import _ from 'underscore'; 17 | // import parse from 'co-body'; 18 | // var parse = require('co-body') 19 | // import finalHandler from './lib/finalHandler'; 20 | // import router from './router'; 21 | 22 | const app = new Koa(); 23 | 24 | // var redisStore = require('koa-redis'); 25 | 26 | // middleware 27 | app.use(views(`${__dirname}/view`, { 28 | extension: 'jade' 29 | })); 30 | app.use(logger()); 31 | 32 | app.use(mount('/static', serve('dist'))); 33 | app.use(mount('/assets/avatar', serve('assets/avatar'))); 34 | app.use(bodyParser()); 35 | 36 | app.proxy = true; 37 | 38 | app.keys = ['fete']; 39 | // app.use(session({ 40 | // key: 'iwfe', 41 | // prefix: 'user-', 42 | // rolling: true, 43 | // cookie: { 44 | // path: '/', 45 | // httpOnly: true, 46 | // maxage: 1000 * 60 * 60 * 24 * 7, 47 | // rewrite: true, 48 | // signed: false 49 | // } 50 | // // store: redisStore(config.redis) 51 | // })); 52 | // 53 | // 54 | // 55 | 56 | // 监听message 57 | sutil.initSocketServer(app); 58 | 59 | app.use(function*(next) { 60 | this.locals = {} 61 | 62 | this.set({ 63 | 'Pragma': 'No-cache', 64 | 'Cache-Control': 'no-cache' 65 | }); 66 | // yield* sutil.setLoginUser(this, 'jade1', '111111'); 67 | 68 | //sutil.getLoginUser(this); 69 | 70 | this.locals.host = config.host; 71 | this.locals.socketConnection = `${config.hostIp}:${config.socketPort}`; 72 | this.locals._now = new Date().getTime(); 73 | let p = this.query; 74 | try { 75 | p = _.extend(p, this.request.body, this.params); 76 | } catch (e) { 77 | 78 | } 79 | 80 | this.parse = p; 81 | var user = _.extend({}, yield * sutil.getLoginUser(this)); 82 | delete user.password; 83 | this.locals._user = user; 84 | try { 85 | yield next; 86 | // Handle 404 upstream. 87 | var status = this.status || 404; 88 | if (status === 404) this.throw(404); 89 | 90 | 91 | // // 新增消息提醒 92 | // if(this.path === '/message/messages' && this.method === 'POST'){ 93 | // let remindUsers = this.query.msgData.toUsers; 94 | // serverSocket.sendMsg(remindUsers); 95 | // } 96 | // //修改消息状态提醒 97 | // if(this.path === '/message/messages' && this.method === 'PUT'){ 98 | // serverSocket.sendMsg([user.username]); 99 | // } 100 | 101 | } catch (error) { 102 | this.status = error.status || 500; 103 | console.log(error.stack); 104 | if (this.status === 404) { 105 | yield this.render('error', { 106 | error 107 | }); 108 | } else { 109 | yield this.render('error', { 110 | error 111 | }); 112 | } 113 | } 114 | }); 115 | 116 | import main from './App/main/router'; 117 | import team from './App/team/router'; 118 | import project from './App/project/router'; 119 | import prd from './App/prd/router'; 120 | import apiModule from './App/api/router'; 121 | import msgModule from './App/message/router'; 122 | import importdb from './App/importdb/router'; 123 | import calendarModule from './App/calendar/router'; 124 | import user from './App/user/router'; 125 | app.use(main.routes()); 126 | app.use(team.routes()); 127 | app.use(project.routes()); 128 | app.use(prd.routes()); 129 | app.use(apiModule.routes()); 130 | app.use(msgModule.routes()); 131 | app.use(importdb.routes()); 132 | app.use(calendarModule.routes()); 133 | app.use(user.routes()); 134 | 135 | app.on('error', function(err) { 136 | console.log('sent error %s to the cloud', err.message); 137 | console.log(err); 138 | }); 139 | 140 | module.exports = app; 141 | -------------------------------------------------------------------------------- /App/user/components/PrdList.jsx: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | import React, {Component, PropTypes} from 'react'; 3 | import classNames from 'classnames'; 4 | import {Link} from 'react-router'; 5 | import Table from 'antd/lib/table'; 6 | import util from '../../../common/util'; 7 | 8 | export default class PrdList extends Component { 9 | constructor(props, context) { 10 | super(props, context); 11 | }; 12 | 13 | static propTypes = { 14 | prds: PropTypes.array.isRequired, 15 | }; 16 | 17 | static defaultProps = { 18 | prds: [] 19 | }; 20 | 21 | render() { 22 | const {prds, showProject, actions} = this.props; 23 | const columns = [ 24 | { 25 | title: '版本号', 26 | dataIndex: 'name', 27 | key: 'name', 28 | width: 60, 29 | }, { 30 | title: '主要功能', 31 | dataIndex: 'description', 32 | key: 'description', 33 | width: 150, 34 | }, { 35 | title: '项目类型', 36 | dataIndex: 'type', 37 | key: 'type', 38 | width: 70, 39 | }, { 40 | title: '阶段', 41 | dataIndex: 'phase', 42 | key: 'phase', 43 | width: 65, 44 | sorter: (a, b) => a.phaseOrder - b.phaseOrder, 45 | }, { 46 | title: '提测时间', 47 | dataIndex: 'testTimeUI', 48 | key: 'testTimeUI', 49 | width: 80, 50 | }, { 51 | title: '上线时间', 52 | dataIndex: 'onlineTimeUI', 53 | key: 'onlineTimeUI', 54 | width: 80, 55 | }, { 56 | title: '产品', 57 | dataIndex: 'pm', 58 | key: 'pm', 59 | width: 100, 60 | }, { 61 | title: '自测', 62 | dataIndex: 'selfTest', 63 | key: 'selfTest', 64 | width: 40, 65 | }, { 66 | title: 'JIRA地址', 67 | dataIndex: 'jira', 68 | key: 'jira', 69 | width: 100, 70 | render: (text, prd) => 71 | {prd.jira} 72 | }, { 73 | title: 'svn地址', 74 | dataIndex: 'svn', 75 | key: 'svn', 76 | width: 150, 77 | render: (text, prd) => 78 | {prd.svn} 79 | }, { 80 | title: '备注', 81 | dataIndex: 'comment', 82 | key: 'comment', 83 | }]; 84 | if (showProject) { 85 | columns.splice(0, 0, { 86 | title: '所属项目', 87 | dataIndex: 'project', 88 | key: 'project', 89 | width: 70, 90 | render: (text, prd) => 91 |
92 | {prd.project.name} 93 |
94 | }) 95 | } 96 | if (actions) { 97 | columns.splice(0, 0, { 98 | title: '操作', 99 | dataIndex: '', 100 | key: 'opts', 101 | render: (text, prd) => 102 | , 108 | width: 120 109 | }) 110 | } 111 | prds.map(item=> { 112 | const {mrdTime, prdTime, devTime, apiTime, testTime, betaTime, onlineTime} = item; 113 | item.testTimeUI = testTime && util.formateDate(testTime, '%F'); 114 | item.onlineTimeUI = onlineTime && util.formateDate(onlineTime, '%F'); 115 | const now = Date.now(); 116 | let phase = '未开始'; 117 | let phaseOrder = 0; 118 | if (now >= onlineTime) { 119 | phase = '已上线'; 120 | phaseOrder = 7; 121 | } else if (now >= betaTime) { 122 | phase = 'beta测试'; 123 | phaseOrder = 6; 124 | } else if (now >= testTime) { 125 | phase = 'test测试'; 126 | phaseOrder = 5; 127 | } else if (now >= apiTime) { 128 | phase = '联调'; 129 | phaseOrder = 4; 130 | } else if (now >= devTime) { 131 | phase = '开发' 132 | phaseOrder = 3; 133 | } else if (now >= prdTime) { 134 | phase = 'prd阶段' 135 | phaseOrder = 2; 136 | } else if (now >= mrdTime) { 137 | phase = 'mrd阶段' 138 | phaseOrder = 1; 139 | } 140 | item.phase = phase; 141 | item.phaseOrder = phaseOrder; 142 | }) 143 | return ( 144 | prd.id}/> 145 | ) 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /App/importdb/import.vue: -------------------------------------------------------------------------------- 1 | /* 导入数据 */ 2 | 49 | 50 | 133 | 134 | 137 | -------------------------------------------------------------------------------- /App/calendar/calendar.vue: -------------------------------------------------------------------------------- 1 | /* 日历 */ 2 | 5 | 6 | 111 | 112 | 136 | -------------------------------------------------------------------------------- /App/team/redux/Detail.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import {bindActionCreators} from 'redux' 11 | import {connect} from 'react-redux'; 12 | import * as actions from './action' 13 | import Button from 'antd/lib/button'; 14 | import Icon from 'antd/lib/icon'; 15 | import Card from 'antd/lib/card'; 16 | import Col from 'antd/lib/col'; 17 | import Row from 'antd/lib/row'; 18 | import Modal from 'antd/lib/modal'; 19 | import Tabs from 'antd/lib/tabs'; 20 | import Checkbox from 'antd/lib/checkbox'; 21 | import Member from '../components/Member'; 22 | import InviteMember from '../components/InviteMember.jsx'; 23 | import PrdList from '../../prd/components/PrdList.jsx'; 24 | import './app.scss'; 25 | const TabPane = Tabs.TabPane; 26 | const CheckboxGroup = Checkbox.Group; 27 | 28 | class Detail extends Component { 29 | constructor(props) { 30 | super(props) 31 | } 32 | 33 | static propTypes = { 34 | team: PropTypes.object.isRequired, 35 | }; 36 | 37 | //初始化渲染后触发 38 | componentDidMount() { 39 | const {team, actions} = this.props; 40 | actions.getMembers(team); 41 | actions.getPrds(team, 'all'); 42 | } 43 | 44 | render() { 45 | const {team, members, prds, actions, inviteMemberShow, deleteMemberShow} = this.props; 46 | const options = [ 47 | { label: 'mrd', value: 'mrd' }, 48 | { label: 'prd', value: 'prd' }, 49 | { label: '开发', value: 'dev' }, 50 | { label: '联调', value: 'api' }, 51 | { label: '提测', value: 'test' }, 52 | { label: 'beta', value: 'beta' }, 53 | { label: '上线', value: 'online' }, 54 | ]; 55 | return ( 56 |
57 |

{team.name}

58 | 59 | 60 | 61 | { 62 | members.map(item=> 63 |
68 | 72 | 73 | ) 74 | } 75 | actions.inviteMemberShow(true)}> 76 | 77 | 78 | 79 | { 80 | deleteMemberShow && deleteMemberShow.show ? 81 | actions.deleteMemberShow(false, deleteMemberShow.member)} 84 | onOk={() => actions.deleteMember(team, deleteMemberShow.member)} 85 | > 86 |

您确定要删除该"{deleteMemberShow.member.username}"成员吗?

87 |
88 | : null 89 | } 90 | { 91 | inviteMemberShow ? 92 | actions.inviteMember(team, member)} 96 | cancelCallback={() => actions.inviteMemberShow(false)} 97 | /> 98 | : null 99 | } 100 | 101 | 102 | actions.getPrds(team, value)} /> 103 |
104 | { 105 | prds ? : null 106 | } 107 |
108 | 109 | 110 | ) 111 | } 112 | } 113 | 114 | 115 | function mapDispatchToProps(dispatch) { 116 | return { 117 | actions: bindActionCreators(actions, dispatch) 118 | } 119 | } 120 | 121 | export default connect(state=> { 122 | return { 123 | team: state.teams.find(item=>item.id == state.router.params.teamId) || pageConfig.team.team, 124 | members: state.members, 125 | prds: state.prds, 126 | inviteMemberShow: state.inviteMemberShow, 127 | deleteMemberShow: state.deleteMemberShow, 128 | } 129 | }, mapDispatchToProps)(Detail); 130 | -------------------------------------------------------------------------------- /App/main/router.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @Author: lancui 3 | * @Date: 2016-06-22 14:06:00 4 | * @Email: lancui@superjia.com 5 | * @Last modified by: lancui 6 | * @Last modified time: 2016-06-29 11:06:89 7 | */ 8 | 9 | 10 | 11 | /** 12 | * Created by zyy on 15/7/7. 13 | * zhangyuyu@superjia.com 14 | */ 15 | import Router from 'koa-router'; 16 | const router = new Router(); 17 | 18 | var wrap = require('co-monk'); 19 | // var parse = require('co-body'); 20 | // import convert from 'koa-convert'; 21 | 22 | var wrap = require('co-monk') 23 | var db = require('../../common/db') 24 | var userDao = wrap(db.get('user')); 25 | 26 | import sutil from '../../common/sutil'; 27 | import util from '../../common/util'; 28 | import busBoy from 'co-busboy'; 29 | import fs from 'fs'; 30 | import path from 'path'; 31 | import AdmZip from 'adm-zip'; 32 | import fse from 'fs-extra'; 33 | 34 | import Index from './index'; 35 | import Login from './login'; 36 | import Register from './register'; 37 | 38 | // 首页 39 | router.get('/', function*(next) { 40 | var html = sutil.reactRender(Index, { 41 | number: 2 42 | }); 43 | // let user = yield userDao.findOne({ 44 | // username: 'jade' 45 | // }) 46 | yield sutil.render(this, { 47 | html: html, 48 | number: 2 49 | }); 50 | }); 51 | 52 | router.all('/login', function*(next) { 53 | // let user = yield userDao.findOne({ 54 | // username: 'jade' 55 | // }) 56 | const parse = this.parse; 57 | 58 | const username = util.trim(parse.username); 59 | const html = sutil.reactRender(Login, { 60 | username: username 61 | }); 62 | if (this.locals._user.username) { 63 | this.redirect('/'); 64 | return; 65 | } 66 | if (sutil.isGet(this.method)) { 67 | yield sutil.render(this, { 68 | html: html, 69 | noHeader: true 70 | }); 71 | } else { 72 | const password = sutil.wrapUserPass(parse.password); 73 | const user = yield userDao.findOne({ 74 | username: username, 75 | password: password 76 | }); 77 | 78 | if (!user) { 79 | yield sutil.render(this, { 80 | html: html, 81 | noHeader: true, 82 | username: username, 83 | error: '用户名或密码不正确' 84 | }); 85 | } else { 86 | const refer = parse.next || this.get('Referrer') || '/'; 87 | yield sutil.setLoginUser(this, username, parse.password); 88 | this.redirect(refer); 89 | } 90 | } 91 | }); 92 | 93 | router.all('/register', function*(next) { 94 | // let user = yield userDao.findOne({ 95 | // username: 'jade' 96 | // }) 97 | const parse = this.parse; 98 | const username = util.trim(parse.username); 99 | const html = sutil.reactRender(Register, { 100 | username: username 101 | }); 102 | if (this.locals._user.username) { 103 | this.redirect('/'); 104 | return; 105 | } 106 | if (sutil.isGet(this.method)) { 107 | yield sutil.render(this, { 108 | html: html, 109 | noHeader: true 110 | }); 111 | } else { 112 | const user = yield userDao.findOne({ 113 | username: username, 114 | }); 115 | 116 | if (user) { 117 | yield sutil.render(this, { 118 | html: html, 119 | noHeader: true, 120 | username: username, 121 | error: '用户名已经存在' 122 | }); 123 | return false; 124 | } 125 | 126 | const password = sutil.wrapUserPass(parse.password); 127 | yield userDao.insert({ 128 | username: username, 129 | password: password, 130 | teams: [] 131 | }) 132 | 133 | this.redirect('/login'); 134 | } 135 | }); 136 | 137 | router.get('/logout', function*(next) { 138 | this.cookies.set('feteauth', null); 139 | this.redirect('/login'); 140 | }); 141 | 142 | router.all('/upload', sutil.login, function *() { 143 | if(sutil.isGet(this.method)){ 144 | yield sutil.render(this , { 145 | 146 | }) 147 | }else{ 148 | var parts = busBoy(this); 149 | var part; 150 | const filePath = path.join(__dirname, '../../static.zip'); 151 | while (part = yield parts) { 152 | // var stream = fs.createWriteStream(path.join(os.tmpdir(), Math.random().toString())); 153 | var stream = fs.createWriteStream(filePath); 154 | part.pipe(stream); 155 | console.log('uploading %s -> %s', part.filename, stream.path); 156 | } 157 | var zip = new AdmZip(filePath); 158 | zip.extractAllTo(/*target path*/path.join(__dirname, '../../'), /*overwrite*/true); 159 | fse.removeSync(path.join(__dirname, '../../static.zip')) 160 | sutil.success(this, 'done') 161 | 162 | } 163 | }); 164 | 165 | 166 | export default router; 167 | -------------------------------------------------------------------------------- /App/api/java2json.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 86 | 87 | 174 | -------------------------------------------------------------------------------- /App/team/redux/action.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:49:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:55:07 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // export const REQUEST = 'REQUEST'; 11 | export const GET = 'GET'; 12 | export const ADD_SHOW = 'ADD_SHOW'; 13 | export const ADD = 'ADD'; 14 | export const UPDATE = 'UPDATE'; 15 | export const UPDATE_SHOW = 'UPDATE_SHOW'; 16 | export const DELETE = 'DELETE'; 17 | export const DELETE_SHOW = 'DELETE_SHOW'; 18 | export const GET_MEMBER= 'GET_MEMBER'; 19 | export const INVITE_MEMBER= 'INVITE_MEMBER'; 20 | export const INVITE_MEMBER_SHOW= 'INVITE_MEMBER_SHOW'; 21 | export const DELETE_MEMBER= 'DELETE_MEMBER'; 22 | export const DELETE_MEMBER_SHOW= 'DELETE_MEMBER_SHOW'; 23 | export const GET_PRD = 'GET_PRD'; 24 | 25 | export function getTeams() { 26 | return dispatch => { 27 | // dispatch(request()) 28 | return fetch('/team/data') 29 | .then(json => dispatch({ 30 | type: GET, 31 | teams: json.data 32 | })) 33 | } 34 | } 35 | 36 | export function addShow(addShow) { 37 | return { 38 | type: ADD_SHOW, 39 | addShow: addShow 40 | } 41 | } 42 | 43 | export function addTeam(team) { 44 | return dispatch => { 45 | return fetch('/team/data', { 46 | method: 'post', 47 | body: team 48 | }) 49 | .then(json => dispatch({ 50 | type: ADD, 51 | team: json.data 52 | })) 53 | } 54 | } 55 | 56 | export function updateShow(updateShow, team) { 57 | return { 58 | type: UPDATE_SHOW, 59 | updateShow: updateShow, 60 | team: team 61 | } 62 | } 63 | 64 | export function updateTeam(team) { 65 | return dispatch => { 66 | // dispatch(request()) 67 | return fetch('/team/data', { 68 | method: 'put', 69 | body: team 70 | }) 71 | .then(json => dispatch({ 72 | type: UPDATE, 73 | team: json.data 74 | })) 75 | } 76 | } 77 | 78 | export function deleteTeam(team) { 79 | return dispatch => { 80 | // dispatch(request()) 81 | return fetch('/team/data', { 82 | method: 'delete', 83 | body: { 84 | id: team.id 85 | } 86 | }) 87 | .then(json => dispatch({ 88 | type: DELETE, 89 | team: team 90 | })) 91 | } 92 | } 93 | 94 | export function deleteShow(deleteShow, team) { 95 | return { 96 | type: DELETE_SHOW, 97 | deleteShow: deleteShow, 98 | team: team 99 | } 100 | } 101 | 102 | /*********member相关*********/ 103 | export function getMembers(team) { 104 | return dispatch => { 105 | // dispatch(request()) 106 | return fetch('/team/member', 107 | { 108 | body: { 109 | teamId: team.id 110 | } 111 | }) 112 | .then(json => dispatch({ 113 | type: GET_MEMBER, 114 | members: json.data 115 | })) 116 | } 117 | } 118 | 119 | export function inviteMemberShow(show) { 120 | return { 121 | type: INVITE_MEMBER_SHOW, 122 | show: show 123 | } 124 | } 125 | 126 | export function inviteMember(team, member) { 127 | return dispatch => { 128 | return fetch('/team/member/invited', { 129 | method: 'post', 130 | body: Object.assign({teamId: team.id}, member) 131 | }) 132 | .then(json => dispatch({ 133 | type: INVITE_MEMBER, 134 | member: json.data 135 | })) 136 | } 137 | } 138 | 139 | export function deleteMember(team, member) { 140 | return dispatch => { 141 | // dispatch(request()) 142 | return fetch('/team/member', { 143 | method: 'delete', 144 | body: { 145 | username: member.username, 146 | teamId: team.id 147 | } 148 | }) 149 | .then(json => dispatch({ 150 | type: DELETE_MEMBER, 151 | member: member 152 | })) 153 | } 154 | } 155 | 156 | export function deleteMemberShow(show, member) { 157 | return { 158 | type: DELETE_MEMBER_SHOW, 159 | show: show, 160 | member: member 161 | } 162 | } 163 | 164 | /**********prd相关**************/ 165 | export function getPrds(team, filter) { 166 | return dispatch => { 167 | return fetch('/team/prd', 168 | { 169 | body: { 170 | teamId: team.id, 171 | filter: filter 172 | } 173 | }) 174 | .then(json => dispatch({ 175 | type: GET_PRD, 176 | prds: json.data 177 | })) 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /App/iconfont/redux/Detail.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:57:58 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:58:51 6 | */ 7 | 8 | 'use strict'; 9 | import React, {Component, PropTypes} from 'react'; 10 | import {bindActionCreators} from 'redux' 11 | import {connect} from 'react-redux'; 12 | import * as actions from './action' 13 | import Button from 'antd/lib/button'; 14 | import Icon from 'antd/lib/icon'; 15 | import Card from 'antd/lib/card'; 16 | import Col from 'antd/lib/col'; 17 | import Row from 'antd/lib/row'; 18 | import Modal from 'antd/lib/modal'; 19 | import Tabs from 'antd/lib/tabs'; 20 | import Checkbox from 'antd/lib/checkbox'; 21 | import Member from '../components/Member'; 22 | import InviteMember from '../components/InviteMember.jsx'; 23 | import PrdList from '../../prd/components/PrdList.jsx'; 24 | import './app.scss'; 25 | const TabPane = Tabs.TabPane; 26 | const CheckboxGroup = Checkbox.Group; 27 | 28 | class Detail extends Component { 29 | constructor(props) { 30 | super(props) 31 | } 32 | 33 | static propTypes = { 34 | iconfont: PropTypes.object.isRequired, 35 | }; 36 | 37 | //初始化渲染后触发 38 | componentDidMount() { 39 | const {iconfont, actions} = this.props; 40 | actions.getMembers(iconfont); 41 | actions.getPrds(iconfont, 'all'); 42 | } 43 | 44 | render() { 45 | const {iconfont, members, prds, actions, inviteMemberShow, deleteMemberShow} = this.props; 46 | const options = [ 47 | { label: 'mrd', value: 'mrd' }, 48 | { label: 'prd', value: 'prd' }, 49 | { label: '开发', value: 'dev' }, 50 | { label: '联调', value: 'api' }, 51 | { label: '提测', value: 'test' }, 52 | { label: 'beta', value: 'beta' }, 53 | { label: '上线', value: 'online' }, 54 | ]; 55 | return ( 56 |
57 |

{iconfont.name}

58 | 59 | 60 | 61 | { 62 | members.map(item=> 63 |
68 | 72 | 73 | ) 74 | } 75 | actions.inviteMemberShow(true)}> 76 | 77 | 78 | 79 | { 80 | deleteMemberShow && deleteMemberShow.show ? 81 | actions.deleteMemberShow(false, deleteMemberShow.member)} 84 | onOk={() => actions.deleteMember(iconfont, deleteMemberShow.member)} 85 | > 86 |

您确定要删除该"{deleteMemberShow.member.username}"成员吗?

87 |
88 | : null 89 | } 90 | { 91 | inviteMemberShow ? 92 | actions.inviteMember(iconfont, member)} 96 | cancelCallback={() => actions.inviteMemberShow(false)} 97 | /> 98 | : null 99 | } 100 | 101 | 102 | actions.getPrds(iconfont, value)} /> 103 |
104 | { 105 | prds ? : null 106 | } 107 |
108 | 109 | 110 | ) 111 | } 112 | } 113 | 114 | 115 | function mapDispatchToProps(dispatch) { 116 | return { 117 | actions: bindActionCreators(actions, dispatch) 118 | } 119 | } 120 | 121 | export default connect(state=> { 122 | return { 123 | iconfont: state.iconfonts.find(item=>item.id == state.router.params.iconfontId) || pageConfig.iconfont.iconfont, 124 | members: state.members, 125 | prds: state.prds, 126 | inviteMemberShow: state.inviteMemberShow, 127 | deleteMemberShow: state.deleteMemberShow, 128 | } 129 | }, mapDispatchToProps)(Detail); 130 | -------------------------------------------------------------------------------- /App/iconfont/redux/action.js: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: jade 3 | * @Date: 2016-06-26 21:49:54 4 | * @Last Modified by: jade 5 | * @Last Modified time: 2016-06-29 11:55:07 6 | */ 7 | 8 | 'use strict'; 9 | 10 | // export const REQUEST = 'REQUEST'; 11 | export const GET = 'GET'; 12 | export const ADD_SHOW = 'ADD_SHOW'; 13 | export const ADD = 'ADD'; 14 | export const UPDATE = 'UPDATE'; 15 | export const UPDATE_SHOW = 'UPDATE_SHOW'; 16 | export const DELETE = 'DELETE'; 17 | export const DELETE_SHOW = 'DELETE_SHOW'; 18 | export const GET_MEMBER= 'GET_MEMBER'; 19 | export const INVITE_MEMBER= 'INVITE_MEMBER'; 20 | export const INVITE_MEMBER_SHOW= 'INVITE_MEMBER_SHOW'; 21 | export const DELETE_MEMBER= 'DELETE_MEMBER'; 22 | export const DELETE_MEMBER_SHOW= 'DELETE_MEMBER_SHOW'; 23 | export const GET_PRD = 'GET_PRD'; 24 | 25 | export function getIconfonts() { 26 | return dispatch => { 27 | // dispatch(request()) 28 | return fetch('/iconfont/data') 29 | .then(json => dispatch({ 30 | type: GET, 31 | iconfonts: json.data 32 | })) 33 | } 34 | } 35 | 36 | export function addShow(addShow) { 37 | return { 38 | type: ADD_SHOW, 39 | addShow: addShow 40 | } 41 | } 42 | 43 | export function addIconfont(iconfont) { 44 | return dispatch => { 45 | return fetch('/iconfont/data', { 46 | method: 'post', 47 | body: iconfont 48 | }) 49 | .then(json => dispatch({ 50 | type: ADD, 51 | iconfont: json.data 52 | })) 53 | } 54 | } 55 | 56 | export function updateShow(updateShow, iconfont) { 57 | return { 58 | type: UPDATE_SHOW, 59 | updateShow: updateShow, 60 | iconfont: iconfont 61 | } 62 | } 63 | 64 | export function updateIconfont(iconfont) { 65 | return dispatch => { 66 | // dispatch(request()) 67 | return fetch('/iconfont/data', { 68 | method: 'put', 69 | body: iconfont 70 | }) 71 | .then(json => dispatch({ 72 | type: UPDATE, 73 | iconfont: json.data 74 | })) 75 | } 76 | } 77 | 78 | export function deleteIconfont(iconfont) { 79 | return dispatch => { 80 | // dispatch(request()) 81 | return fetch('/iconfont/data', { 82 | method: 'delete', 83 | body: { 84 | id: iconfont.id 85 | } 86 | }) 87 | .then(json => dispatch({ 88 | type: DELETE, 89 | iconfont: iconfont 90 | })) 91 | } 92 | } 93 | 94 | export function deleteShow(deleteShow, iconfont) { 95 | return { 96 | type: DELETE_SHOW, 97 | deleteShow: deleteShow, 98 | iconfont: iconfont 99 | } 100 | } 101 | 102 | /*********member相关*********/ 103 | export function getMembers(iconfont) { 104 | return dispatch => { 105 | // dispatch(request()) 106 | return fetch('/iconfont/member', 107 | { 108 | body: { 109 | iconfontId: iconfont.id 110 | } 111 | }) 112 | .then(json => dispatch({ 113 | type: GET_MEMBER, 114 | members: json.data 115 | })) 116 | } 117 | } 118 | 119 | export function inviteMemberShow(show) { 120 | return { 121 | type: INVITE_MEMBER_SHOW, 122 | show: show 123 | } 124 | } 125 | 126 | export function inviteMember(iconfont, member) { 127 | return dispatch => { 128 | return fetch('/iconfont/member/invited', { 129 | method: 'post', 130 | body: Object.assign({iconfontId: iconfont.id}, member) 131 | }) 132 | .then(json => dispatch({ 133 | type: INVITE_MEMBER, 134 | member: json.data 135 | })) 136 | } 137 | } 138 | 139 | export function deleteMember(iconfont, member) { 140 | return dispatch => { 141 | // dispatch(request()) 142 | return fetch('/iconfont/member', { 143 | method: 'delete', 144 | body: { 145 | username: member.username, 146 | iconfontId: iconfont.id 147 | } 148 | }) 149 | .then(json => dispatch({ 150 | type: DELETE_MEMBER, 151 | member: member 152 | })) 153 | } 154 | } 155 | 156 | export function deleteMemberShow(show, member) { 157 | return { 158 | type: DELETE_MEMBER_SHOW, 159 | show: show, 160 | member: member 161 | } 162 | } 163 | 164 | /**********prd相关**************/ 165 | export function getPrds(iconfont, filter) { 166 | return dispatch => { 167 | return fetch('/iconfont/prd', 168 | { 169 | body: { 170 | iconfontId: iconfont.id, 171 | filter: filter 172 | } 173 | }) 174 | .then(json => dispatch({ 175 | type: GET_PRD, 176 | prds: json.data 177 | })) 178 | } 179 | } 180 | --------------------------------------------------------------------------------