├── .babelrc
├── .gitignore
├── LICENSE
├── README.md
├── devServer.js
├── index.html
├── package.json
├── src
    ├── common
    │   └── utils.js
    ├── component
    │   ├── App.jsx
    │   ├── content
    │   │   ├── Form.jsx
    │   │   ├── PageIntro.jsx
    │   │   ├── PageTabs.jsx
    │   │   ├── Pane.jsx
    │   │   ├── Table.jsx
    │   │   └── Topbar.jsx
    │   ├── factory.js
    │   └── nav
    │   │   ├── NavGlobal.jsx
    │   │   └── NavMenu.jsx
    ├── data
    │   ├── api.js
    │   └── config.js
    ├── entry
    │   └── index.jsx
    ├── page
    │   ├── common
    │   │   ├── Blank.jsx
    │   │   ├── ChangePassword.jsx
    │   │   ├── EmailVerify.jsx
    │   │   ├── Err404.jsx
    │   │   ├── Signin.jsx
    │   │   └── Signup.jsx
    │   └── demo
    │   │   ├── FactoryForms.jsx
    │   │   ├── RawForms.jsx
    │   │   └── TestForms.jsx
    └── style
    │   ├── form.css
    │   ├── index.js
    │   ├── main.css
    │   └── table.css
├── static
    ├── config.js.jinja2
    └── logo48x48.png
├── webpack.config.dev.js
└── webpack.config.prod.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 |   "presets": ["react", "es2015", "stage-1"],
3 |   "env": {
4 |     "development": {
5 |       "presets": ["react-hmre"]
6 |     }
7 |   }
8 | }
9 | 
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | *~
 2 | \#*\#
 3 | .\#*
 4 | *.swp
 5 | 
 6 | node_modules
 7 | dist
 8 | 
 9 | npm-debug.log
10 | static/*.svg
11 | static/*.ttf
12 | static/bundle.js
13 | static/bundle.js.map
14 | static/config.js
15 | 
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | The MIT License (MIT)
 2 | 
 3 | Copyright (c) 2016 HuhuLab
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | ## What's this
 2 | React([ant-design](https://github.com/ant-design/ant-design)) based frontend boilerplate project.
 3 | 
 4 | ## How to run it
 5 | ``` bash
 6 | npm install
 7 | # More commands see: package.json
 8 | npm start
 9 | # Open http://localhost:3000/#/demo/test_forms
10 | ```
11 | 
--------------------------------------------------------------------------------
/devServer.js:
--------------------------------------------------------------------------------
 1 | var path = require('path');
 2 | var express = require('express');
 3 | var webpack = require('webpack');
 4 | var config = require('./webpack.config.dev');
 5 | 
 6 | var app = express();
 7 | var compiler = webpack(config);
 8 | 
 9 | app.use(require('webpack-dev-middleware')(compiler, {
10 |   noInfo: true,
11 |   publicPath: config.output.publicPath
12 | }));
13 | 
14 | app.use(require('webpack-hot-middleware')(compiler));
15 | 
16 | app.get('/static/config.js', function(req, res) {
17 |   res.sendFile(path.join(__dirname, 'static/config.js'));
18 | });
19 | 
20 | app.post('/upload', function(req, res) {
21 |   res.setHeader('Content-Type', 'application/json');
22 |   res.json({url: 'https://www.google.com'});
23 | });
24 | 
25 | app.get('*', function(req, res) {
26 |   res.sendFile(path.join(__dirname, 'index.html'));
27 | });
28 | 
29 | app.listen(3000, '0.0.0.0', function(err) {
30 |   if (err) {
31 |     console.log(err);
32 |     return;
33 |   }
34 | 
35 |   console.log('Listening at http://0.0.0.0:3000');
36 | });
37 | 
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |     
 6 |     
 7 |     
 8 |     
 9 |   
10 |   
11 |     
12 |   
13 |   
14 |   
15 | 
16 | 
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "react-frontend-boilerplate",
 3 |   "version": "1.0.0",
 4 |   "dependencies": {
 5 |     "react": "^0.14.0",
 6 |     "react-dom": "^0.14.0",
 7 |     "axios": "~0.7.0",
 8 |     "lodash": "~4.0.0",
 9 |     "moment": "~2.10.6",
10 |     "es6-promise": "~3.0.2",
11 |     "font-awesome": "4.4.0",
12 |     "react-router": "~1.0.0",
13 |     "react-cookie": "^0.4.2",
14 |     "react-highcharts": "^5.0.4",
15 |     "babel-polyfill": "^6.3.14",
16 |     "antd": "1.0.0-beta.2"
17 |   },
18 |   "devDependencies": {
19 |     "url-loader": "~0.5.6",
20 |     "style-loader": "~0.13.0",
21 |     "css-loader": "~0.22.0",
22 |     "less-loader": "~2.2.1",
23 |     "expect.js": "~0.3.1",
24 |     "babel-core": "^6.6.5",
25 |     "babel-eslint": "^5.0.0-beta4",
26 |     "babel-loader": "^6.2.4",
27 |     "babel-preset-es2015": "^6.3.13",
28 |     "babel-preset-stage-1": "^6.1.18",
29 |     "babel-preset-react": "^6.3.13",
30 |     "babel-preset-react-hmre": "^1.1.1",
31 |     "cross-env": "^1.0.7",
32 |     "eslint": "^1.10.3",
33 |     "eslint-plugin-babel": "^3.0.0",
34 |     "eslint-plugin-react": "^3.11.3",
35 |     "eventsource-polyfill": "^0.9.6",
36 |     "express": "^4.13.3",
37 |     "rimraf": "^2.4.3",
38 |     "webpack": "^1.12.9",
39 |     "webpack-dev-middleware": "^1.4.0",
40 |     "webpack-hot-middleware": "^2.9.1"
41 |   },
42 |   "pre-commit": [
43 |     "lint"
44 |   ],
45 |   "scripts": {
46 |     "clean": "rimraf dist",
47 |     "build:webpack": "NODE_ENV=production webpack --config webpack.config.prod.js",
48 |     "build": "npm run clean && npm run build:webpack",
49 |     "start": "node devServer.js",
50 |     "lint": "eslint src"
51 |   }
52 | }
53 | 
--------------------------------------------------------------------------------
/src/common/utils.js:
--------------------------------------------------------------------------------
 1 | function appendFiles(fd, name, files) {
 2 |   files.forEach(function(item) {
 3 |     if (item instanceof FileList) {
 4 |       var cnt = item.length;
 5 |       for (var i = 0; i < cnt; i++) {
 6 |         var file = item[i];
 7 |         fd.append(name, file);
 8 |       }
 9 |     } else if (item instanceof File) {
10 |       fd.append(name, item);
11 |     }
12 |   });
13 | }
14 | 
15 | export function makeFormData(obj) {
16 |   var fd = new FormData();
17 |   Object.keys(obj).forEach(function(key) {
18 |     var value = obj[key];
19 |     /* console.log('fd.k-v: ', key, value); */
20 |     if (value === undefined || value === null) {
21 |       fd.append(key, '');
22 |     } else if (value instanceof FileList) {
23 |       // Append files
24 |       appendFiles(fd, key, [value]);
25 |     } else if (value instanceof Array && value.length > 0 && (value[0] instanceof FileList || value[0] instanceof File)) {
26 |       // Append files
27 |       appendFiles(fd, key, value);
28 |     } else if (value === true || value === false) {
29 |       // Boolean value
30 |       fd.append(key, value ? 1 : 0);
31 |     } else if (value instanceof Array) {
32 |       value.forEach(function(item) {
33 |         fd.append(key, item);
34 |       });
35 |     } else if (value instanceof Object) {
36 |       fd.append(key, JSON.stringify(value));
37 |     } else {
38 |       fd.append(key, value);
39 |     }
40 |   });
41 |   return fd
42 | }
43 | 
44 | export function getStatusClassArray(status, originValue) {
45 |   const value = (originValue === undefined || originValue === null) ? originValue : String(originValue);
46 |   const obj = {
47 |     'error': status.errors,
48 |     'validating': status.isValidating,
49 |     'success': value && !status.errors && !status.isValidating
50 |   }
51 |   return Object.keys(obj).filter(function(key) {
52 |     return obj[key];
53 |   });
54 | }
55 | 
56 | export function getStatusClasses(status, originValue) {
57 |   return getStatusClassArray(status, originValue).join(' ');
58 | }
59 | 
60 | export function getStatusHelp(status) {
61 |     return status.isValidating ? "正在校验中.." : status.errors ? status.errors.join(',') : null
62 | }
63 | 
64 | export function updateFilters(oldFilters, name, operation, value) {
65 |   let newFilters = [];
66 |   let matched = false;
67 |   oldFilters.forEach(function(item) {
68 |     if (item[0] === name && item[1] === operation) {
69 |       if (value !== undefined) {
70 |         newFilters.push([name, operation, value]);
71 |       }
72 |       matched = true;
73 |     } else {
74 |       newFilters.push(item);
75 |     }
76 |   });
77 | 
78 |   if (!matched) {
79 |     newFilters.push([name, operation, value]);
80 |   }
81 |   return newFilters;
82 | }
83 | 
--------------------------------------------------------------------------------
/src/component/App.jsx:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 | import React from 'react';
  5 | import { History } from 'react-router'
  6 | import cookie from 'react-cookie';
  7 | import { message, Menu, Link } from 'antd';
  8 | 
  9 | import { tokenKey, sideMenus } from 'data/config';
 10 | import { httpGet } from 'data/api';
 11 | import NavGlobal from './nav/NavGlobal.jsx';
 12 | import NavMenu from './nav/NavMenu.jsx';
 13 | 
 14 | 
 15 | function signOut(e) {
 16 |   cookie.remove(tokenKey);
 17 | }
 18 | 
 19 | const topMenu = [
 20 |   {key: "2", href:"/user/change_password", label: "修改密码"},
 21 |   {key: "-1", divider: true},
 22 |   {key: "3", href:"/signin", onClick: signOut, iconClass: "fa fa-fw fa-sign-out", label: "注销"},
 23 | ];
 24 | 
 25 | 
 26 | const user = {
 27 |   name: '某某用户',
 28 |   role: {
 29 |     name: 'customer'
 30 |   }
 31 | };
 32 | 
 33 | const App = React.createClass({
 34 |   displayName: 'App',
 35 |   mixins: [ History ],
 36 | 
 37 |   getInitialState() {
 38 |     /* return {user: undefined}; */
 39 |     return {
 40 |       user: {
 41 |         name: 'Test user',
 42 |         role: {
 43 |           name: 'user',
 44 |           descr: 'Test role descr',
 45 |         }
 46 |       }
 47 |     };
 48 |   },
 49 | 
 50 |   componentDidMount(){
 51 |     console.log('NavGlobal:componentDidMount');
 52 |   },
 53 | 
 54 |   render() {
 55 |     console.log('App.props:', this.props);
 56 | 
 57 |     if (cookie.load(tokenKey) === undefined) {
 58 |       console.log('X-Token is missing!');
 59 |       /* message.error('请先登录系统!');
 60 |          this.history.pushState(null, '/signin'); */
 61 |     }
 62 | 
 63 |     console.log('current user:', this.state.user);
 64 |     const navMenu = this.state.user ?  : '';
 65 |     return (
 66 |       
 67 |         
 68 |           
 69 |           {navMenu}
 70 |           
 71 |             {this.props.children}
 72 |           
 73 |         
 74 |       
{ columns }
;
170 |     }
171 |   }
172 | }
173 | 
174 | 
175 | export class BaseForm extends Component {
176 | 
177 |   static propTypes = {
178 |     type       : PropTypes.oneOf(["create", "update"]).isRequired,
179 |     labelCol   : PropTypes.number,
180 |     wrapperCol : PropTypes.number,
181 |     formProps  : PropTypes.object,
182 |     fields     : PropTypes.oneOfType(
183 |       [PropTypes.array, PropTypes.func]).isRequired,
184 |     layout     : PropTypes.oneOfType(
185 |       [PropTypes.array, PropTypes.func]),
186 |     items      : PropTypes.object.isRequired,
187 |     object     : PropTypes.object,
188 |     onSubmit   : PropTypes.func, /* function(values, callback) or function(context, values, callback) */
189 |     onSuccess  : PropTypes.func, /* function(object) */
190 |   }
191 | 
192 |   static defaultProps = {
193 |     labelCol   : 6,
194 |     wrapperCol : 14,
195 |     formProps  : {horizontal: true},
196 |     layout     : null,
197 |     object     : {},
198 |     onSuccess  : function(object) {}
199 |   }
200 | 
201 | 
202 |   constructor(props) {
203 |     super(props);
204 |     console.log('BaseForm.constructor, props.object=',
205 |                 JSON.stringify(this.props.object));
206 |     this.state = {object: this.props.object};
207 |   }
208 | 
209 |   componentDidMount() {
210 |     this.resetForm();
211 |   }
212 | 
213 |   componentWillReceiveProps(newProps) {
214 |     console.log('componentWillReceiveProps', this.props.type, newProps);
215 |     if (!_.isEqual(newProps.object, this.props.object)) {
216 |       this.setState({object: newProps.object}, () => {
217 |         this.resetForm();
218 |       });
219 |     }
220 |   }
221 | 
222 |   getFields() {
223 |     const { fields } = this.props;
224 |     return _.isFunction(fields) ? fields.call(this) : fields;
225 |   }
226 | 
227 |   getLayout() {
228 |     const { layout } = this.props;
229 |     return _.isFunction(layout) ? layout.call(this) : (
230 |       !!layout ? layout : this.getFields());
231 |   }
232 | 
233 |   formatObject(object) {
234 |     console.log('formatObject:', object);
235 |     let result = {};
236 |     const { items } = this.props;
237 |     const fields = this.getFields();
238 |     fields.map(function(field) {
239 |       const item = items[field];
240 |       let value = object[field];
241 |       if (_.isBoolean(value)) {
242 |         value = value ? '1' : '0';
243 |       } else if (_.isNumber(value)) {
244 |         value = String(value);
245 |       } else if (_.isObject(item) && item.type === "file") {
246 |         value = undefined;
247 |       }
248 |       result[field] = value;
249 |     });
250 |     console.log('>>> fields:', fields, 'result', result);
251 |     return result;
252 |   }
253 | 
254 |   handleReset() {
255 |     const { type, object, form } = this.props
256 |     form.resetFields();
257 |     console.log('setFieldDefaults', type, object, this.state.object);
258 |     const targetObject = type === "create" ? object : this.state.object;
259 |     form.setFieldsValue(this.formatObject(targetObject));
260 |   }
261 |   resetForm() { this.handleReset() }
262 | 
263 |   handleSubmit(e) {
264 |     console.log('BaseForm.handleSubmit', e);
265 |     e.preventDefault();
266 |     const { type, items, form, onSuccess, object } = this.props;
267 |     const fields = this.getFields();
268 |     form.validateFieldsAndScroll((errors, values) => {
269 |       if (!!errors) {
270 |         console.log('Errors in form!!!', errors);
271 |         return;
272 |       }
273 | 
274 |       // preprocess values
275 |       if (object.id !== undefined) {
276 |         values.id = object.id;
277 |       }
278 |       fields.forEach(function(field) {
279 |         const item = items[field];
280 |         const value = values[field];
281 |         switch (item.type) {
282 |           case "date":
283 |             if (!!value ) {
284 |               values[field] = moment(value).format('YYYY-MM-DD');
285 |             }
286 |             break;
287 |           case "file":
288 |             if (_.isObject(item) && value) {
289 |               values[field] = value.target.files;
290 |             }
291 |             break;
292 |           default:
293 |             break;
294 |         }
295 |       });
296 | 
297 |       const callback = (newObject) => {
298 |         if (type === "create") {
299 |           this.handleReset();
300 |           onSuccess();
301 |         } else {
302 |           this.setState({object: newObject}, () => {
303 |             this.handleReset();
304 |             onSuccess(newObject);
305 |           });
306 |         }
307 |       };
308 | 
309 |       if (!!this.onSubmit) {
310 |         this.onSubmit(values, callback);
311 |       } else {
312 |         this.props.onSubmit(this, values, callback);
313 |       }
314 |       console.log('Submit!!!', values);
315 |     });
316 |   }
317 | 
318 |   renderFormBody() {
319 |     const { form, items, labelCol, wrapperCol } = this.props;
320 |     /* console.log('renderFormBody:', JSON.stringify([
321 |        this.props.type, this.state.object, this.props.object])); */
322 |     const rows = this.getLayout();  // rows == layout
323 |     /* console.log('renderFormBody.fields:', this.props.type, fields); */
324 |     return rows.map((row, index) => {
325 |       if (!(row instanceof FormRow)) {
326 |         row = new FormRow(row);
327 |       }
328 |       return row.render(this, `row-${index}`);
329 |     });
330 |   }
331 | 
332 |   render() {
333 |     const { form, formProps, labelCol, wrapperCol } = this.props;
334 |     const footerItem = 
335 |       
336 |       
337 |     ;
338 |     let formBody = this.renderFormBody();
339 |     formBody.push(footerItem);
340 | 
341 |     return (
342 |       
346 |     );
347 |   }
348 | }
349 | 
350 | 
351 | export class FormModal extends BaseForm {
352 | 
353 |   static propTypes = {
354 |     ...BaseForm.propTypes,
355 |     visible    : PropTypes.bool.isRequired,
356 |     title      : PropTypes.string,
357 |     modalProps : PropTypes.object,
358 |     onCancel   : PropTypes.func, /* function() */
359 |   }
360 | 
361 |   static defaultProps = {
362 |     ...BaseForm.defaultProps,
363 |     title: "表单",
364 |     modalProps: {}
365 |   }
366 | 
367 | 
368 |   render() {
369 |     const { form, formProps, modalProps } = this.props;
370 |     const formBody = this.renderFormBody();
371 |     const footer = 
372 |         
373 |         
374 |     
;
375 | 
376 |     return (
377 |        this.props.onCancel()}
381 |              {...modalProps}>
382 |         
386 |       
387 |     );
388 |   }
389 | }
390 | 
391 | 
392 | export class SearchForm extends BaseForm {
393 |   static propTypes = {
394 |     ...BaseForm.propTypes,
395 |     visible : PropTypes.bool.isRequired,
396 |   }
397 | 
398 |   static defaultProps = {
399 |     ...BaseForm.defaultProps,
400 |     type: "update",
401 |     labelCol: 10,
402 |     wrapperCol: 14,
403 |   }
404 | 
405 |   handleReset(e) {
406 |     const { form, object } = this.props;
407 |     let targetObject = object;
408 |     if (!e) {
409 |       targetObject = this.state.object;
410 |     }
411 |     form.setFieldsValue(this.formatObject(targetObject));
412 |   }
413 | 
414 |   render() {
415 |     if (!this.props.visible) {
416 |       return null;
417 |     }
418 | 
419 |     const { form, formProps, animProps } = this.props;
420 |     const footerItem = (
421 |       
422 |         
423 |           
425 |           
426 |         
427 |       
428 |     );
429 |     let formBody = this.renderFormBody();
430 |     formBody.push(footerItem);
431 | 
432 |     return (
433 |       
438 |     );
439 |   }
440 | }
441 | 
--------------------------------------------------------------------------------
/src/component/content/PageIntro.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | 
 3 | var PageIntro = React.createClass({
 4 |   render() {
 5 |     return (
 6 |       
 7 |         
{ this.props.children }
 8 |       
16 |         
17 |           
18 |             
19 |           
20 |           
21 |             
22 |           
23 |           
24 |             
25 |               
26 |             
27 |           
28 |         
29 |       
50 |         
51 |           选项卡说明 1
52 |         
53 |         
56 |       
 84 |         
 85 |       
);
 86 |   },
 87 | 
 88 |   //// Use in `render()`
 89 |   //////////////////////////////////////////////////
 90 |   handleModalDismiss(e, name) {
 91 |     const newState = {};
 92 |     newState[name] = false;
 93 |     this.setState(newState);
 94 |   },
 95 | 
 96 |   getColumns(key) {
 97 |     const columnNames = this.state.columnNames;
 98 |     console.log('getColumns.columnNames:', key, columnNames);
 99 |     const fields = columnNames.constructor === Array ? columnNames : columnNames[key];
100 |     return fields.map((field) => {
101 |       return this.state.columns[field];
102 |     });
103 |   },
104 | 
105 |   getPagination() {
106 |     const query = this.state.query;
107 |     return {
108 |       current: query.page,
109 |       pageSize: query.perpage,
110 |       total: this.state.total,
111 |     };
112 |   },
113 | 
114 |   getRowSelection() {
115 |     return {
116 |       onSelect: (record, selected, selectedRows) => {
117 |         console.log('onSelect:', record, selected, selectedRows);
118 |         this.setState({selectedRows: selectedRows});
119 |       },
120 |       onSelectAll: (selected, selectedRows) => {
121 |         console.log('onSelectAll:', selected, selectedRows);
122 |         this.setState({selectedRows: selectedRows});
123 |       }
124 |     }
125 |   },
126 | 
127 |   //// Table logic
128 |   _loadData(okCallback, errorCallback) {
129 |     const query = this.state.query;
130 |     this.state.Model.objects(query).then((resp) => {
131 |       okCallback(resp.data);
132 |     }).catch((resp) => {
133 |       errorCallback(resp);
134 |     });
135 |   },
136 | 
137 |   loadPage(e) {
138 |     this.setState({loading: true}, () => {
139 |       const loadDataFunc = this.loadData === undefined ? this._loadData : this.loadData;
140 |       loadDataFunc((data) => {
141 |         /// Success callback
142 |         this.setState({
143 |           loading: false,
144 |           total: data.total,
145 |           objects: data.objects
146 |         });
147 |         if (e !== undefined) {
148 |           message.success('刷新成功', 1);
149 |         }
150 |       }, (resp) => {
151 |         console.log('Error response:', resp);
152 |         /// Error callback
153 |         if (resp.status == 400) {
154 |           this.setState({loading: false}, () => {
155 |             let query = this.state.query;
156 |             query.page = 1;
157 |             this.setState({query: query}, () => {
158 |               this.loadPage();
159 |             });
160 |           });
161 |         } else {
162 |           message.error(`加载失败: ${resp.data.message}`);
163 |         }
164 |       });
165 |     });
166 |   },
167 | 
168 |   onTableChanged(pagination, filters, sorter){
169 |     console.log('onTableChanged', pagination, filters, sorter);
170 |     let query = this.state.query;
171 |     let sort = this.state.sort;
172 |     if (Object.keys(sorter).length > 0) {
173 |       const theOrder = {
174 |         'ascend': 'asc',
175 |         'descend': 'desc',
176 |       }[sorter.order];
177 |       sort = [[sorter.field, theOrder]];
178 |     }
179 |     query.page = pagination.current;
180 |     query.perpage = pagination.pageSize;
181 |     query.sort = sort;
182 |     this.setState({query: query}, () => {
183 |       this.loadPage();
184 |     });
185 |   },
186 | 
187 |   //// Unused
188 |   renderFilters() {
189 |     const filterConfig = this.state.filterConfig;
190 |     const items = filterConfig.items;
191 |     return filterConfig.names.map((name) => {
192 |       return renderInputItem(items[name], name);
193 |     });
194 |   },
195 | };
196 | 
--------------------------------------------------------------------------------
/src/component/content/Topbar.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import {Breadcrumb} from 'antd';
 3 | 
 4 | const Topbar = React.createClass({
 5 |   render() {
 6 |     const breadcrumb = this.props.breadcrumb.map(function(item) {
 7 |       return (
 8 |          { item } 
 9 |       )
10 |     })
11 |     return (
12 |       
13 |         
14 |           
15 |             
16 |           
17 |           { breadcrumb }
18 |         
19 |       
20 |     );
21 |   }
22 | });
23 | 
24 | export default Topbar;
25 | 
--------------------------------------------------------------------------------
/src/component/factory.js:
--------------------------------------------------------------------------------
 1 | 
 2 | import React from 'react';
 3 | import {
 4 |   /* 通用 */
 5 |   Col, Row, message, Button, Icon, Tabs,
 6 |   /* 表单 */
 7 |   Form,        // 表单
 8 |   Input,       // 普通输入框: 
 9 |   InputNumber, // 数字输入框
10 |   Checkbox,    // 多选框
11 |   Radio,       // 单选框
12 |   Cascader,    // 级联选择
13 |   Transfer,    // 穿梭框
14 |   Select,      // 选择器
15 |   TreeSelect,  // 树选择
16 |   Slider,      // 滑动输入条
17 |   Switch,      // 开关
18 |   DatePicker,  // 日期选择
19 |   TimePicker,  // 时间选择
20 |   Upload,      // 上传
21 |   /* 动画 */
22 |   QueueAnim,
23 | } from 'antd';
24 | 
25 | // import { getStatusClasses, getStatusHelp } from 'common/utils';
26 | // import * as _ from 'lodash';
27 | 
28 | 
29 | /* antd.Common */
30 | export const ButtonClass = React.createFactory(Button);
31 | export const IconClass = React.createFactory(Icon);
32 | export const TabsClass = React.createFactory(Tabs);
33 | export const TabPaneClass = React.createFactory(Tabs.TabPane);
34 | 
35 | 
36 | /* antd.Form */
37 | export const FormClass = React.createFactory(Form);
38 | export const FormItemClass = React.createFactory(Form.Item);
39 | 
40 | export const InputClass = React.createFactory(Input);
41 | 
42 | export const InputNumberClass = React.createFactory(InputNumber);
43 | 
44 | export const CheckboxClass = React.createFactory(Checkbox);
45 | 
46 | export const RadioClass = React.createFactory(Radio);
47 | export const RadioButtonClass = React.createFactory(Radio.Button);
48 | export const RadioGroupClass = React.createFactory(Radio.Group);
49 | 
50 | export const CascaderClass = React.createFactory(Cascader);
51 | 
52 | export const TransferClass = React.createFactory(Transfer);
53 | 
54 | export const SelectClass = React.createFactory(Select);
55 | export const OptionClass = React.createFactory(Select.Option);
56 | 
57 | export const TreeSelectClass = React.createFactory(TreeSelect)
58 | export const SliderClass = React.createFactory(Slider);
59 | export const SwitchClass = React.createFactory(Switch);
60 | export const DatePickerClass = React.createFactory(DatePicker);
61 | export const RangePickerClass = React.createFactory(DatePicker.RangePicker);
62 | export const TimePickerClass = React.createFactory(TimePicker);
63 | export const UploadClass = React.createFactory(Upload);
64 | 
65 | 
66 | export const div = React.createFactory('div');
67 | export const span = React.createFactory('span');
68 | export const img = React.createFactory('img');
69 | export const a = React.createFactory('a');
70 | 
--------------------------------------------------------------------------------
/src/component/nav/NavGlobal.jsx:
--------------------------------------------------------------------------------
 1 | 
 2 | import React from 'react';
 3 | import { Link } from 'react-router';
 4 | import cookie from 'react-cookie';
 5 | import { Menu, Dropdown } from 'antd';
 6 | 
 7 | import { siteTitle } from 'data/config';
 8 | 
 9 | 
10 | export default React.createClass({
11 |   displayName: 'NavGlobal',
12 | 
13 |   componentWillReceiveProps(newProps) {
14 |     this.setState({});
15 |   },
16 | 
17 |   render() {
18 |     const topMenu = this.props.topMenu;
19 |     let menu = (
20 |       
37 |     );
38 | 
39 |     const user = this.props.user;
40 |     const userName = user ? user.name : '';
41 |     const roleDescr = user ? user.role.descr : '';
42 |     return (
43 |       
44 |         
45 |           
46 |             
47 |               
{siteTitle}
48 |             
49 |           
50 |           
65 |         
66 |       
62 |         
63 |           
64 |             {menu}
65 |           
66 |         
67 |       
10 |         
11 |         
提示: 请点击左侧菜单栏来操作.
12 |       
26 |         
27 |         
修改密码
28 |         
29 |           
31 |         
32 |       
 94 |         请登录你的邮箱验证帐号!
 95 |       
 96 |     );
 97 |     return (
 98 |       
 99 |         
{main}
100 | 
101 |         
102 |           注册
103 |           登录
104 |         
105 |       
36 |         
39 |       
40 |     );
41 |   }
42 | });
43 | 
--------------------------------------------------------------------------------
/src/page/common/Signup.jsx:
--------------------------------------------------------------------------------
 1 | import React from 'react';
 2 | import { Link, History } from 'react-router';
 3 | import { Button, Form, Input, Select, message } from 'antd';
 4 | 
 5 | import { signupRoles } from 'data/config';
 6 | import { Api, httpErrorCallback } from 'data/api';
 7 | 
 8 | const FormItem = Form.Item;
 9 | const Option = Select.Option;
10 | 
11 | 
12 | function noop() {
13 |   return false;
14 | }
15 | 
16 | function handleSelectChange(value) {
17 |     console.log('selected ' + value);
18 | }
19 | 
20 | 
21 | const Signup = React.createClass({
22 |   displayName: 'Signup',
23 | 
24 |   handleSubmit(e) {
25 |     e.preventDefault();
26 |     console.log('Submit:');
27 |   },
28 | 
29 |   handleReset(e) {
30 |     e.preventDefault();
31 |   },
32 | 
33 |   render() {
34 |     const labelCol = 7;
35 |     const wrapperCol = 15;
36 |     const wrapperStyle = {
37 |       width: '360px',
38 |       padding: '40px 20px',
39 |       margin: '60px auto 20px',
40 |       border: '1px solid #CCC',
41 |       borderRadius: '5px',
42 |       background: '#fff',
43 |     };
44 | 
45 |     return (
46 |       
47 |         
50 |       
51 |     );
52 |   }
53 | });
54 | 
55 | export default Signup;
56 | 
--------------------------------------------------------------------------------
/src/page/demo/FactoryForms.jsx:
--------------------------------------------------------------------------------
 1 | 
 2 | import React, { Component, PropTypes } from 'react';
 3 | 
 4 | import {
 5 |   FormClass, FormItemClass,
 6 |   InputClass,
 7 |   InputNumberClass,
 8 |   CheckboxClass,    // 多选框
 9 |   RadioClass,       // 单选框
10 |   RadioButtonClass,
11 |   RadioGroupClass,
12 |   CascaderClass,    // 级联选择
13 |   TransferClass,    // 穿梭框
14 |   SelectClass,      // 选择器
15 |   OptionClass,
16 |   TreeSelectClass,  // 树选择
17 |   SliderClass,      // 滑动输入条
18 |   SwitchClass,      // 开关
19 |   DatePickerClass,  // 日期选择
20 |   TimePickerClass,  // 时间选择
21 |   UploadClass,      // 上传
22 | } from 'component/factory.js';
23 | 
--------------------------------------------------------------------------------
/src/page/demo/RawForms.jsx:
--------------------------------------------------------------------------------
  1 | 
  2 | import React, { Component, PropTypes } from 'react';
  3 | 
  4 | import {
  5 |   /* 通用 */
  6 |   Col, Row, message, Button, Tabs, Icon, Card, Modal,
  7 |   /* 表单 */
  8 |   Form,        // 表单
  9 |   Input,       // 普通输入框: 
 10 |   InputNumber, // 数字输入框
 11 |   Checkbox,    // 多选框
 12 |   Radio,       // 单选框
 13 |   Cascader,    // 级联选择
 14 |   Transfer,    // 穿梭框
 15 |   Select,      // 选择器
 16 |   TreeSelect,  // 树选择
 17 |   Slider,      // 滑动输入条
 18 |   Switch,      // 开关
 19 |   DatePicker,  // 日期选择
 20 |   TimePicker,  // 时间选择
 21 |   Upload,      // 上传
 22 |   /* 动画 */
 23 |   QueueAnim,
 24 | } from 'antd';
 25 | 
 26 | import { FormModal } from 'component/content/Form.jsx';
 27 | 
 28 | const createForm = Form.create;
 29 | 
 30 | const FormItem = Form.Item;
 31 | const Option = Select.Option;
 32 | const RadioGroup = Radio.Group;
 33 | 
 34 | 
 35 | /*
 36 |    [ Form types ]
 37 |    ==============
 38 |      * Read only
 39 |      * Inline
 40 |      * With image field (file)
 41 |      * Multiple fields in one 
 42 |      *  organized by Row/Col (Advanced search form)
 43 | 
 44 | 
 45 |    Read only
 46 |    ---------
 47 |      * The Content
 48 |      * The Content
 49 | 
 50 |    Inline
 51 |    ------
 52 |      * Just add `inline` property to