├── 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 |
2 |
3 |
6 |
7 |
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 |
2 |
3 |
9 |
10 |
11 |
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 |
2 |
5 |
6 |
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 |
41 | return (
42 |
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 |
2 |
5 |
6 |
7 |
45 |
46 |
71 |
--------------------------------------------------------------------------------
/App/api/comment_input.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
31 |
73 |
--------------------------------------------------------------------------------
/App/api/editor/comment_input.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
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 | }>
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 |
2 |
5 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
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 |
2 |
5 |
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 |
2 |
11 |
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 |
50 | const projectMenu =
51 |
61 | return (
62 |
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 |
50 | const projectMenu =
51 |
61 | return (
62 |
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 |
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 |
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 |
2 |
29 |
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 |
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 |
48 |
49 |
50 |
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 |
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 |
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 |
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 |
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 |
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 |
3 |
48 |
49 |
50 |
133 |
134 |
137 |
--------------------------------------------------------------------------------
/App/calendar/calendar.vue:
--------------------------------------------------------------------------------
1 | /* 日历 */
2 |
3 |
4 |
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 |
2 |
15 |
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 |
--------------------------------------------------------------------------------