Commit deccc40d authored by superman's avatar superman

create project

parents
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 4
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab
{
"parser": "babel-eslint",
"extends": "eslint-config-airbnb",
"rules": {
"spaced-comment": [0],
"no-unused-vars": [0],
"no-empty": [0],
"react/wrap-multilines": [0],
"react/no-multi-comp": [0],
"no-constant-condition": [0],
"react/jsx-no-bind": [0],
"react/prop-types": [0],
"arrow-body-style": [0],
"react/prefer-stateless-function": [0],
"semi": [0]
},
"ecmaFeatures": {
"experimentalObjectRestSpread": true
}
}
dist
node_modules
.DS_Store
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<resourceExtensions />
<wildcardResourcePatterns>
<entry name="!?*.java" />
<entry name="!?*.form" />
<entry name="!?*.class" />
<entry name="!?*.groovy" />
<entry name="!?*.scala" />
<entry name="!?*.flex" />
<entry name="!?*.kt" />
<entry name="!?*.clj" />
<entry name="!?*.aj" />
</wildcardResourcePatterns>
<annotationProcessing>
<profile default="true" name="Default" enabled="false">
<processorPath useClasspath="true" />
</profile>
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<component name="CopyrightManager">
<settings default="" />
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<includedPredefinedLibrary name="ECMAScript 6" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ClientPropertiesManager">
<properties class="javax.swing.AbstractButton">
<property name="hideActionText" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JComponent">
<property name="html.disable" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JEditorPane">
<property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" />
<property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" />
<property name="charset" class="java.lang.String" />
</properties>
<properties class="javax.swing.JList">
<property name="List.isFileList" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JPasswordField">
<property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JSlider">
<property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" />
<property name="JSlider.isFilled" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JTable">
<property name="Table.isFileList" class="java.lang.Boolean" />
<property name="JTable.autoStartsEdit" class="java.lang.Boolean" />
<property name="terminateEditOnFocusLost" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JToolBar">
<property name="JToolBar.isRollover" class="java.lang.Boolean" />
</properties>
<properties class="javax.swing.JTree">
<property name="JTree.lineStyle" class="java.lang.String" />
</properties>
<properties class="javax.swing.text.JTextComponent">
<property name="caretAspectRatio" class="java.lang.Double" />
<property name="caretWidth" class="java.lang.Integer" />
</properties>
</component>
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
<component name="MavenImportPreferences">
<option name="generalSettings">
<MavenGeneralSettings>
<option name="mavenHome" value="Bundled (Maven 3)" />
</MavenGeneralSettings>
</option>
</component>
<component name="ProjectLevelVcsManager" settingsEditedManually="false">
<OptionsSetting value="true" id="Add" />
<OptionsSetting value="true" id="Remove" />
<OptionsSetting value="true" id="Checkout" />
<OptionsSetting value="true" id="Update" />
<OptionsSetting value="true" id="Status" />
<OptionsSetting value="true" id="Edit" />
<ConfirmationsSetting value="0" id="Add" />
<ConfirmationsSetting value="0" id="Remove" />
</component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_3" default="false" assert-keyword="false" jdk-15="false">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/shuniu-admin.iml" filepath="$PROJECT_DIR$/shuniu-admin.iml" />
</modules>
</component>
</project>
\ No newline at end of file
This diff is collapsed.
# react-redux-boilerplate
A boilerplate with react, redux, redux-saga, react-router, webpack, babel, css-modules ...
## 环境准备
先安装依赖
```bash
$ npm install
```
想要更好的开发体验,还需安装两个 Chrome 插件:[Redux DevTools](https://chrome.google.com/webstore/detail/lmhkpmbekcpmknklioeibfkpmmfibljd)[LiveReload](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei)
## 启动调试
```bash
$ npm start
$ open http://localhost:8989/
```
## 构建代码
```bash
$ npm run build
// 构建但不压缩
$ npm run build -- --no-compress
```
## 目录结构
```
.
├── /dist/ # 构建输出的文件会在这里
├── /node_modules/ # 第三方类库和工具
├── /src/ # 应用源码
│ ├── /components/ # React components
│ ├── /entries/ # 应用入口
│ ├── /layouts/ # 布局信息
│ ├── /reducers/ # reducers
│ ├── /routes/ # 路由信息
│ ├── /sagas/ # redux-sagas
│ └── /services/ # 处理和服务器的交互
├── proxy.config.js # 配置 dora-plugin-proxy,用于 mock 和在线调试
├── webpack.config.js # 扩展 webpack 配置
└── package.json # 配置入口文件、依赖和 scripts
```
## 系统组织
![](https://camo.githubusercontent.com/068c4ff126977b861cff3338428bdde6927f7dad/68747470733a2f2f6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f43684d775a42755a6c614c725377652e706e67)
详见:[React + Redux 最佳实践](https://github.com/sorrycc/blog/issues/1)
## 内置类库
- [react](https://github.com/facebook/react)
- [redux](https://github.com/reactjs/redux)
- [redux-saga](https://github.com/yelouafi/redux-saga)
- [redux-actions](https://github.com/acdlite/redux-actions)
- [react-router](https://github.com/reactjs/react-router)
- [classnames](https://github.com/JedWatson/classnames)
- [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch)
- [react-router](https://github.com/reactjs/react-router)
- [react-router-redux](https://github.com/reactjs/react-router-redux)
## 工具特性
热替换和 LiveReload
> 基于 [Webpack Vanilla HMR](https://webpack.github.io/docs/hot-module-replacement-with-webpack.html),支持 `components`, `reducers`, `routers`, `sagas`, `layouts` 目录的模块热替换,其余目录的修改则会自动刷新页面。
> CSS 的自动刷新需通过 [LiveReload](https://chrome.google.com/webstore/detail/livereload/jnihajbhpnppcggbcgedagnkighmdlei) Chrome 插件配合使用。
> - [Why Vanilla HMR](https://github.com/reactjs/redux/pull/1455)
支持 css-modules
> 所有 less 文件会被解析为 css-modules
运行错误和语法错误的提醒
> 通过 [redbox-react](https://github.com/KeywordBrain/redbox-react) 和 webpack hmr overlay 提示运行错误和语法错误
自动引入 `reducer``saga`
> 通过 webpack 的 `require.context` 黑魔法批量引入 `reducer` 和 `saga`,新增、删除和重命名时会更方便
自动安装 npm 依赖
> ![](https://camo.githubusercontent.com/898e02d6539900efe65fadbfd15e2a1d7ce4dccf/68747470733a2f2f6f732e616c697061796f626a656374732e636f6d2f726d73706f7274616c2f4b6541474f776a70746a6152684d6d2e676966)
数据 mock 和线上调试
> 通过 dora-plugin-proxy 实现,详见:https://github.com/dora-js/dora-plugin-proxy#规则定义
...
## License
MIT
{
"private": true,
"entry": {},
"dependencies": {
"antd": "^1.1.0",
"atool-build": "^0.7.1",
"babel-plugin-antd": "^0.4.0",
"babel-plugin-transform-runtime": "^6.8.0",
"babel-runtime": "^6.6.1",
"classnames": "^2.2.3",
"es3ify-loader": "^0.2.0",
"form-data": "^1.0.0-rc4",
"history": "^2.0.1",
"isomorphic-fetch": "^2.2.1",
"js-cookie": "^2.1.1",
"rc-animate": "^2.3.0",
"rc-queue-anim": "^0.11.12",
"react": "^15.0.2",
"react-dom": "^15.0.2",
"react-redux": "4.4.x",
"react-router": "^2.0.1",
"react-router-redux": "^4.0.1",
"redux": "^3.5.2",
"redux-actions": "0.9.x",
"redux-async-connect": "^1.0.0-rc4",
"redux-saga": "^0.10.4"
},
"devDependencies": {
"atool-test-mocha": "^0.1.4",
"babel-eslint": "^6.0.2",
"dora": "0.3.x",
"dora-plugin-browser-history": "^0.1.1",
"dora-plugin-livereload": "^0.4.0",
"dora-plugin-proxy": "^0.6.1",
"dora-plugin-webpack": "0.6.x",
"dora-plugin-webpack-hmr": "^0.1.0",
"eslint": "^2.10.1",
"eslint-config-airbnb": "^9.0.1",
"eslint-plugin-import": "^1.8.0",
"eslint-plugin-jsx-a11y": "^1.2.0",
"eslint-plugin-react": "^5.1.1",
"expect": "^1.20.1",
"glob": "^7.0.3",
"pre-commit": "1.x",
"redbox-react": "^1.2.2"
},
"pre-commit": [
"lint"
],
"scripts": {
"build": "NODE_ENV=production atool-build",
"lint": "eslint --ext .js,.jsx src/",
"start": "dora --plugins \"proxy,webpack,webpack-hmr,livereload?enableJs=false&injectHost=127.0.0.1,browser-history?index=/src/entries/index.html\"",
"test": "atool-test-mocha ./src/**/__tests__/*-test.js"
}
}
// Learn more on how to config.
// - https://github.com/dora-js/dora-plugin-proxy#规则定义
module.exports = {
'/api/todos': function (req, res) {
setTimeout(function () {
res.json({
status: 1,
result: [
{
id: 1,
text: 'Learn antd',
isComplete: true,
},
{
id: 2,
text: 'Learn ant-tool',
},
{
id: 3,
text: 'Learn dora',
},
],
});
}, 500);
},
'/api/*': 'http://react.yanky.cn/', //'http://192.168.1.126:8080/',
};
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/node_modules" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>
\ No newline at end of file
import React, { Component, PropTypes } from 'react';
import { Router, Route, IndexRoute, Link } from 'react-router';
import { Collapse } from 'antd';
const Panel = Collapse.Panel;
export default class App extends Component {
static propTypes = {
children: PropTypes.object.isRequired,
};
static contextTypes = {
store: PropTypes.object.isRequired
};
render(){
const styles = require('./App.less');
return (
<div className={styles.normal}>
<div className={styles.head}>
<h1>枢纽科技</h1>
</div>
<div className={styles.content}>
<div className={styles.side}>
<Collapse accordion>
<Panel header="产品管理">
<Link to="/products">产品列表</Link><br/>
<Link to="/products/add">增加产品</Link>
</Panel>
<Panel header="订单管理">
<Link to="/trades">订单列表</Link><br/>
<Link to="/trade/add">增加订单</Link>
</Panel>
</Collapse>
</div>
<div className={styles.main}>
{this.props.children}
</div>
</div>
<div className={styles.foot}>
Built with react, react-router, redux, redux-saga, ant-tool, css-modules, antd...
</div>
</div>
);
}
}
.normal {
display: flex;
flex-direction: column;
height: 100%;
}
.head {
background: #364171;
height: 80px;
padding: 8px;
color: #fff;
border-bottom: 1px solid #ccc;
}
.content {
flex: 1;
display: flex;
}
.side {
padding: 8px;
width: 20%;
min-width: 200px;
//max-width: 200px;
background: #fafafa;
border-right: 1px solid #ccc;
margin-right: 0px;
}
.main {
padding: 8px;
flex: 1 0 auto;
overflow: auto;
}
.foot {
background: #fafafa;
border-top: 1px solid #ccc;
padding: 8px;
}
:global(.tac){
text-align: center !important;
}
import React, {Component, PropTypes} from 'react';
// import Todos from './Todos/Todos';
const Home = ({location}) => {
return (
<div>
<h1>欢迎使用枢纽科技后台</h1>
</div>
);
};
Home.propTypes = {};
export default Home;
//<Todos location={location}/>
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Form, Input, Button, Checkbox, message, Spin} from 'antd';
@connect(state=>({
loading: state.user.loading
}))
@Form.create()
export default class Login extends Component {
static propTypes = {
form: PropTypes.object,
dispatch: PropTypes.func,
};
handleSubmit = (e)=> {
e.preventDefault();
const data = this.props.form.getFieldsValue();
console.log('收到表单值:', data);
this.props.dispatch({
...data,
push: this.props.history.push,
type: 'LOGIN_REQUEST'
});
};
render = ()=> {
const {form:{getFieldProps}, loading} = this.props;
const styles = require('./Login.less');
const bg = require('./images/bg1.jpg');
return (
<Spin spinning={loading} tip="正在登录...">
<div className={styles.normal}>
<div className={styles.content}>
<div className={styles['login-bg']}>
<img src={bg}/>
</div>
<div className={styles['login-container']}>
<div>
<div>
<h1>枢纽科技后台管理系统</h1>
<Form inline onSubmit={this.handleSubmit.bind(this)}>
<div>
<Form.Item>
<Input placeholder="请输入账户名" {...getFieldProps('username')} />
</Form.Item>
<Form.Item>
<Input type="password"
placeholder="请输入密码" {...getFieldProps('password')} />
</Form.Item>
</div>
<div>
<Form.Item>
<Checkbox {...getFieldProps('agreement')}>记住我</Checkbox>
</Form.Item>
<Button type="primary" htmlType="submit">登录</Button>
</div>
</Form>
</div>
</div>
</div>
</div>
</div>
</Spin>
);
}
}
.normal {
display: flex;
flex-direction: column;
height: 100%;
}
.content {
flex: 1;
display: flex;
}
.login-bg {
width: 50%;
overflow: hidden;
}
.login-container {
width: 50%;
display: flex;
justify-content: center;
flex-direction: column;
& > div {
display: flex;
justify-content: center;
h1 {
text-align: center;
margin-bottom: 50px;
font-size: 30px;
font-weight: 300;
color: #4e5a64;
}
form {
:global(.ant-form-item) {
margin: 0 10px;
}
input[type="text"],
input[type="password"] {
padding: 10px;
border: 0;
border-bottom: 1px solid #a0a9b4;
color: #868e97;
font-size: 14px;
margin-bottom: 30px;
border-radius: 0;
box-shadow: none;
&:focus {
border-bottom: 1px solid #444;
color: #444;
box-shadow: none;
}
}
button {
float: right;
margin-right: 10px;
background-color: #32c5d2;
border-color: #32c5d2;
border-radius: 0;
}
}
}
}
:global(.fade-enter) {
opacity: 0;
animation-duration: 2s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
&:global(.fade-enter-active) {
animation-name: fadeIn;
animation-play-state: running;
}
}
:global(.fade-appear) {
opacity: 0;
animation-duration: 2s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
&:global(.fade-appear-active) {
animation-name: fadeIn;
animation-play-state: running;
}
}
:global(.fade-leave) {
animation-duration: 2s;
animation-fill-mode: both;
animation-timing-function: cubic-bezier(0.55, 0, 0.55, 0.2);
animation-play-state: paused;
&:global(.fade-leave-active) {
animation-name: fadeOut;
animation-play-state: running;
}
}
@keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
import React from 'react';
import { Button } from 'antd';
import styles from './NotFound.less';
const NotFound = () => {
return (
<div className={styles.normal}>
<div className={styles.container}>
<h1 className={styles.title}>404</h1>
<p className={styles.desc}>未找到该页面</p>
<a href="/"><Button type="primary" style={{ marginTop: 5 }}>返回首页</Button></a>
</div>
</div>
);
};
export default NotFound;
.normal {
width: 100%;
height: 100%;
min-height: 100vh;
padding-top: 120px;
}
.container {
padding: 0;
margin: 0 auto;
width: 620px;
height: 300px;
text-align: center;
}
.title {
font-size: 80px;
color: #666;
margin-top: 20px;
margin-bottom: 10px;
}
.desc {
font-size: 14px;
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Form, Input, Button, Checkbox, message, Tabs} from 'antd';
@connect()
@Form.create()
export default class AddItem extends Component {
static propTypes = {
form: PropTypes.object,
dispatch: PropTypes.func
};
handleSubmit(e){
};
render = ()=> {
const {getFieldProps} = this.props.form;
return (
<Form horizontal onSubmit={this.handleSubmit.bind(this)}>
<Tabs tabPosition="left">
<Tabs.TabPane tab="基本信息" key="tab-pane-1">
<Form.Item label="商品类目">
</Form.Item>
<Form.Item label="密码">
<Input type="password" placeholder="请输入密码" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="商品标题">
<Input placeholder="商品标题" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="商品短标题">
<Input placeholder="商品短标题" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="商品主图">
<Input placeholder="商品短标题" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="商品简介">
<Input placeholder="商品简介" {...getFieldProps('password')} />
</Form.Item>
</Tabs.TabPane>
<Tabs.TabPane tab="基本信息2" key="tab-pane-2">
<Form.Item label="产品的预期收益">
<Input placeholder="产品的预期收益" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="佣金算法">
<Input placeholder="佣金算法" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="最高佣金">
<Input placeholder="最高佣金" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="募集目标/本期额度">
<Input placeholder="募集目标/本期额度" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="预约募集/已预约(实际)">
<Input placeholder="预约募集/已预约(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="预约募集/已预约(假的)">
<Input placeholder="预约募集/已预约(假的)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
</Tabs.TabPane>
<Tabs.TabPane tab="基本信息3" key="tab-pane-3">
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item label="实际募集/已募集(实际)">
<Input placeholder="实际募集/已募集(实际)" {...getFieldProps('password')} />
</Form.Item>
<Form.Item>
<Checkbox {...getFieldProps('agreement')}>记住我</Checkbox>
</Form.Item>
</Tabs.TabPane>
<Tabs.TabPane tab="基本要素" key="tab-pane-4">
</Tabs.TabPane>
</Tabs>
<Button type="primary" htmlType="submit">登录</Button>
</Form>
);
}
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Table, Icon} from 'antd';
import {params, formatDateTime, productStatusToString} from '../../utils';
const columns = [
{
title: '类目',
dataIndex: 'cateId',
key: 'cateId',
render: (cateId, record)=>(<span data-cate-id={cateId}>{record.cateName}</span>)
}, {
title: '标题',
dataIndex: 'shortTitle',
key: 'shortTitle',
render: (shortTitle, record)=>(<span title={shortTitle}>{(shortTitle + '').substring(0, 20)}</span>)
}, {
title: '募集比率',
dataIndex: 'rate',
key: 'rate'
}, {
title: '状态',
dataIndex: 'status',
key: 'status',
render: (status, record)=>(<span data-status={status}>{productStatusToString(status)}</span>)
}, {
title: '创建时间',
dataIndex: 'dateCreated',
key: 'dateCreated',
render: (dateCreated, record)=>(
<span>
{dateCreated && formatDateTime(dateCreated)}
</span>
)
}, {
title: '操作',
key: 'operation',
render: (text, record)=>(
<span>
<a href={'/product/item?id='+ record.id}>详情</a>
<span className="ant-divider"></span>
<a href="#">公告</a>
</span>
)
}
];
@connect(state=>({
items: state.product.items,
loading: state.product.loading,
total: state.product.total
}))
export default class List extends Component {
constructor(props, context) {
super(props, context);
}
componentWillMount() {
this.fetchList(this.props.location.query);
};
fetchList(query) {
this.props.dispatch({
type: 'FETCH_PRODUCT_LIST',
query
});
};
render() {
const {total, items, loading, history:{replace}, location:{pathname, query}} = this.props;
const pagination = {
total: total,
pageSize: parseInt(query.s, 10) || 10,
current: parseInt(query.p, 10) || 1,
showSizeChanger: true,
onShowSizeChange: (current, pageSize)=> {
console.log('Current: ', current, '; PageSize: ', pageSize);
query.p = current;
query.s = pageSize;
replace(pathname + '?' + params(query));
this.fetchList(query);
},
onChange: (current)=> {
console.log('Current: ', current);
query.p = current;
replace(pathname + '?' + params(query));
this.fetchList(query);
}
};
return <div>
<h1>产品列表</h1>
<Table className="ant-table" columns={columns}
dataSource={Array.isArray(items)?items:[]}
loading={loading}
pagination={pagination}
scroll={{ y: window.innerHeight-280 }}
/>
</div>;
}
}
import React, { Component, PropTypes } from 'react';
import classnames from 'classnames';
import styles from './Todo.less';
const Todo = ({ data, onToggleComplete }) => {
const { text, isComplete } = data;
const todoCls = classnames({
[styles.normal]: true,
[styles.isComplete]: isComplete,
});
return (
<div className={todoCls}>
<div className={styles.checkbox}>
<input
type="checkbox"
value=""
checked={isComplete}
onChange={onToggleComplete.bind(this)}
/>
</div>
<div className={styles.text}>
{text}
</div>
</div>
);
};
Todo.propTypes = {
data: PropTypes.object.isRequired,
onToggleComplete: PropTypes.func.isRequired,
};
export default Todo;
.normal {
display: flex;
}
.checkbox {
margin-right: 6px;
}
.isComplete {
color: #ccc;
text-decoration: line-through;
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Spin} from 'antd';
import Todo from './Todo';
import styles from './Todos.less';
const Todos = ({todos, dispatch}) => {
const handleToggleComplete = (id) => {
dispatch({
type: 'todos/toggleComplete',
payload: id,
});
};
const renderList = () => {
const {list, loading} = todos;
if (loading) {
return <Spin />;
}
return (
<div className={styles.list}>
{list.map(item => <Todo
key={item.id}
data={item}
onToggleComplete={handleToggleComplete.bind(this, item.id)}
/>
)}
</div>
);
};
return (
<div className={styles.normal}>
{renderList()}
</div>
);
};
Todos.propTypes = {};
function filter(todos, pathname) {
const newList = todos.list.filter(todo => {
if (pathname === '/actived') {
return !todo.isComplete;
}
if (pathname === '/completed') {
return todo.isComplete;
}
return true;
});
return {...todos, list: newList};
}
function mapStateToProps({todos}, {location}) {
return {
todos: filter(todos, location.pathname),
};
}
export default connect(mapStateToProps)(Todos);
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Form, Input, Button, Checkbox, message, Row, Col} from 'antd';
import {
formatDateTime,
tradeStatusToString,
tradeCreateTypeToString
} from '../../utils';
@connect(state=>({
item: state.trade.item
}))
export default class Item extends Component {
componentWillMount() {
const {dispatch, params:{id}} = this.props;
dispatch({
type: 'FETCH_TRADE_ITEM',
id
});
};
handleGoBack(e){
e.preventDefault();
this.props.history.goBack();
}
render() {
const {item} = this.props;
const styles = require('./Item.less');
const tw = 8;
const vw = 16;
return (
<div className={styles.trade}>
<div className={styles.tradeTable}>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>产品</Col>
<Col span={vw}>{item.title}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>募集开始时间</Col>
<Col span={vw}>{formatDateTime(item.fundRaisedStartTime)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>募集结束时间</Col>
<Col span={vw}>{formatDateTime(item.fundRaisedEndTime)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>状态</Col>
<Col span={vw}>{tradeStatusToString(item.status)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>创建类型</Col>
<Col span={vw}>{tradeCreateTypeToString(item.type)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>预约时间</Col>
<Col span={vw}>{formatDateTime(item.reservationTime)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>预约金额</Col>
<Col span={vw}>{item.reservationAmount}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>预期回报</Col>
<Col span={vw}>
{
item.prospectiveReturn && item.prospectiveReturn.sy &&
<span>{'收益 ' + item.prospectiveReturn.sy}</span>
}
{
item.prospectiveReturn && item.prospectiveReturn.yj &&
<span>{'佣金 ' + item.prospectiveReturn.yj}</span>
}
</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>报单时间</Col>
<Col span={vw}>{formatDateTime(item.submitReceiptTime)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>打款审核时间</Col>
<Col span={vw}>{formatDateTime(item.remittanceAuditTime)}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>确认打款金额</Col>
<Col span={vw}>{item.remittanceAmount}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>实际回报</Col>
<Col span={vw}>
{
item.realReturn && item.realReturn.sy &&
<span>{'收益 ' + item.realReturn.sy}</span>
}
{
item.realReturn && item.realReturn.yj &&
<span>{'佣金 ' + item.realReturn.yj}</span>
}
</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>买家名字</Col>
<Col span={vw}>{item.buyerName}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>买家身份证</Col>
<Col span={vw}>{item.buyerIdentityCardNumber}</Col>
</Row>
<Row type="flex" justify="space-around" align="middle">
<Col span={tw}>创建时间</Col>
<Col span={vw}>{formatDateTime(item.dateCreated)}</Col>
</Row>
</div>
<p>
<Button onClick={this.handleGoBack.bind(this)}>返回</Button>
</p>
</div>
);
}
};
.trade {
p {
text-align: center;
padding: 20px 0;
max-width: 800px;
}
}
.tradeTable {
border-top: 1px solid #e9e9e9;
border-bottom: 1px solid #e9e9e9;
max-width: 800px;
//margin: auto;
font-size: 14px;
:global(.ant-row),
:global(.ant-row-flex) {
border-bottom: 1px solid #e9e9e9;
transition: all .3s ease;
&:last-child {
border-bottom: 0;
}
&:hover {
background: #eaf8fe;
}
& > div { //col
min-height: 50px;
padding: 16px 10px;
word-break: break-all;
&:first-child {
text-align: right;
}
&:last-child {
border-left: 1px solid #e9e9e9;
}
span {
margin-right: 1em;
&:last-child {
margin-right: 0;
}
}
&:global(.nopadding) {
padding: 0;
}
:global(.ant-row),
:global(.ant-row-flex) {
&:hover {
background: #def5ff;
}
}
}
}
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Table, Icon} from 'antd';
import { Link } from 'react-router';
import {params, formatDateTime, tradeStatusToString} from '../../utils';
const columns = [
{
title: '订单ID',
dataIndex: 'id',
key: 'id',
width:80,
// fixed:'left'
}, {
title: '产品',
dataIndex: 'shortTitle',
key: 'shortTitle',
render: (shortTitle, record)=>(<span title={shortTitle}>{(shortTitle + '').substring(0, 20)}</span>)
}, {
title: '预约时间',
dataIndex: 'reservationTime',
key: 'reservationTime',
width:130,
className:'tac',
render: (reservationTime, record)=>(
<span>
{reservationTime && formatDateTime(reservationTime)}
</span>
)
}, {
title: '投资人',
dataIndex: 'buyerName',
key: 'buyerName',
width:100,
className:'tac',
}, {
title: '预约额度',
dataIndex: 'reservationAmount',
key: 'reservationAmount',
width:100,
className:'tac',
}, {
title: '实际打款',
dataIndex: 'remittanceAmount',
key: 'remittanceAmount',
width:100,
className:'tac',
}, {
title: '进度',
dataIndex: 'status',
key: 'status',
width:150,
className:'tac',
render: (status, record)=>(<span data-status={status}>{tradeStatusToString(status)}</span>)
}, {
title: '操作',
key: 'operation',
width:140,
// fixed:'right',
className:'tac',
render: (text, record)=>(
<span>
<Link to={'/trades/'+ record.id}>邮寄合同</Link>
<span className="ant-divider"></span>
<Link to={'/trades/'+ record.id}>发放佣金</Link>
</span>
)
}
];
@connect(state=>({
items: state.trade.items,
loading: state.trade.loading,
total: state.trade.total,
}))
export default class List extends Component {
constructor(props, context) {
super(props, context);
}
componentWillMount() {
this.fetchList(this.props.location.query);
};
fetchList(query){
this.props.dispatch({
type: 'FETCH_TRADE_LIST',
query
});
}
handleRowClick({id}){
this.props.history.push('/trades/'+id);
}
render() {
const { total, items, loading, history:{replace}, location:{pathname, query}} = this.props;
const pagination = {
total: total,
pageSize: parseInt(query.s, 10) || 10,
current: parseInt(query.p, 10) || 1,
showSizeChanger: true,
onShowSizeChange: (current, pageSize)=> {
console.log('Current: ', current, '; PageSize: ', pageSize);
query.p = current;
query.s = pageSize;
replace(pathname + '?' + params(query));
this.fetchList(query);
},
onChange: (current) => {
console.log('Current: ', current);
query.p = current;
replace(pathname + '?' + params(query));
this.fetchList(query);
}
};
return <Table className="ant-table" columns={columns}
dataSource={Array.isArray(items)?items:[]}
loading={loading}
pagination={pagination}
scroll={{ y: window.innerHeight-245 }}
onRowClick={this.handleRowClick.bind(this)}
/>;
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>SHUNIU TECH</title>
<link rel="stylesheet" href="/index.css" />
</head>
<body>
<div id="root"></div>
<script src="/common.js"></script>
<script src="/index.js"></script>
</body>
</html>
import './index.html';
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose, combineReducers } from 'redux';
import createSagaMiddleware from 'redux-saga';
import {Router, browserHistory } from 'react-router';
import { syncHistoryWithStore, routerReducer as routing } from 'react-router-redux';
import reducers from '../reducers/index';
import { ReduxAsyncConnect } from 'redux-async-connect';
import SagaManager from '../sagas/SagaManager';
import './index.less';
//////////////////////
// Store
const sagaMiddleware = createSagaMiddleware();
const initialState = {};
const enhancer = compose(
applyMiddleware(sagaMiddleware),
window.devToolsExtension ? window.devToolsExtension() : f => f
);
const store = createStore(combineReducers({
...reducers, routing,
}), initialState, enhancer);
SagaManager.startSagas(sagaMiddleware);
if (module.hot) {
module.hot.accept('../reducers', () => {
const reducers = require('../reducers');
const combinedReducers = combineReducers({ ...reducers, routing });
store.replaceReducer(combinedReducers);
});
module.hot.accept('../sagas/SagaManager', () => {
SagaManager.cancelSagas(store);
require('../sagas/SagaManager').default.startSagas(sagaMiddleware);
});
}
//////////////////////
// Render
const history = syncHistoryWithStore(browserHistory, store);
let render = () => {
const getRoutes = require('../routes/index');
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
{getRoutes(store)}
</Router>
</Provider>
, document.getElementById('root'));
};
if (module.hot) {
const renderNormally = render;
const renderException = (error) => {
const RedBox = require('redbox-react');
ReactDOM.render(<RedBox error={error} />, document.getElementById('root'));
};
render = () => {
try {
renderNormally();
} catch (error) {
console.error('error', error);
renderException(error);
}
};
module.hot.accept('../routes/index', () => {
render();
});
}
render();
:global {
html, body, #root {
height: 100%;
}
}
import expect from 'expect';
import todos from '../todos';
describe('todos', () => {
it('todos/get', () => {
expect(todos({}, { type: 'todos/get' })).toEqual({ loading: true });
});
it('todos/get/success', () => {
const newState = todos({ list: 1, loading: true }, { type: 'todos/get/success', payload:2 });
expect(newState).toEqual({
loading: false,
list: 2,
});
});
});
// Use require.context to require reducers automatically
// Ref: https://webpack.github.io/docs/context.html
const context = require.context('./', false, /\.js$/);
const keys = context.keys().filter(item => item !== './index.js');
const reducers = keys.reduce((memo, key) => {
memo[key.match(/([^\/]+)\.js$/)[1]] = context(key);
return memo;
}, {});
export default reducers;
import { handleActions } from 'redux-actions';
import { combineReducer } from 'redux';
const product = handleActions({
['FETCH_PRODUCT_LIST'](state) {
return { ...state, loading: true, };
},
['FETCH_PRODUCT_LIST_SUCCESS'](state, action) {
return { ...state, loading: false, items: action.items, total:action.total };
},
['FETCH_PRODUCT_LIST_FAILED'](state, action) {
return { ...state, err: action.err, loading: false, };
}
}, {
items: [],
loading: false,
});
export default product;
import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
const todos = handleActions({
['todos/get'](state) {
return {...state, loading: true,};
},
['todos/get/success'](state, action) {
return {...state, list: action.payload, loading: false,};
},
['todos/get/failed'](state, action) {
return {...state, err: action.err, loading: false,};
},
['todos/delete'](state, action) {
const id = action.payload;
const newList = state.list.filter(todo => todo.id !== id);
return {...state, list: newList,};
},
['todos/create'](state, action) {
const text = action.payload;
const newTodo = {text,};
return {...state, list: [newTodo, ...state.list],};
},
['todos/toggleComplete'](state, action) {
const id = action.payload;
const newList = state.list.map(todo => {
if (id === todo.id) {
return {...todo, isComplete: !todo.isComplete};
} else {
return todo;
}
});
return {...state, list: newList,};
},
['todos/toggleCompleteAll'](state, action) {
const isComplete = action.payload;
const newList = state.list.map(todo => ({...todo, isComplete}));
return {...state, list: newList,};
},
}, {
list: [],
loading: false,
});
export default todos;
import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
const trade = handleActions({
['FETCH_TRADE_LIST'](state) {
return {...state, loading: true,};
},
['FETCH_TRADE_LIST_SUCCESS'](state, action) {
return {...state, loading: false, items: action.items, total: action.total};
},
['FETCH_TRADE_LIST_FAILED'](state, action) {
return {...state, err: action.err, loading: false};
},
['FETCH_TRADE_ITEM'](state){
return {...state, loading: true}
},
['FETCH_TRADE_ITEM_SUCCESS'](state, action){
return {...state, loading: false, item: action.item}
},
['FETCH_TRADE_ITEM_FAILED'](state){
return {...state, err: action.err, loading: false}
}
}, {
item: {},
items: [],
loading: false,
});
export default trade;
import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
let _user = sessionStorage.getItem('user');
try{
_user = JSON.parse(_user);
}catch(ex){
_user = {};
}
const user = handleActions({
['LOGIN_REQUEST'](state) {
return {...state, loading: true};
},
['LOGIN_SUCCESS'](state, action) {
sessionStorage.setItem('user', JSON.stringify(action.user));
return {...state, loading: false, ...action.user};
},
['LOGIN_ERROR'](state, action) {
return {...state, err: action.err, loading: false};
},
['LOGOUT'](state) {
return {...state, auth: {}, loading: false};
}
}, {
..._user,
loading: false
});
export default user;
import React, {PropTypes, Component} from 'react';
import {Route, IndexRoute, Link} from 'react-router';
import Home from '../containers/Home/Home';
import NotFound from '../containers/NotFound/NotFound';
import Login from '../containers/Login/Login';
import App from '../containers/App/App';
import ProductList from '../containers/Product/List';
import ProductAddItem from '../containers/Product/AddItem';
import TradeList from '../containers/Trade/List';
import TradeItem from '../containers/Trade/Item';
export default (store)=> {
const requireAuth = (nextState, replace, cb) => {
const {user} = store.getState();
if (!user || !user.token) {
replace('/login');
}
cb();
};
return (
<Route path="/">
<Route onEnter={requireAuth} component={App}>
<IndexRoute component={Home}/>
<Route path="products" >
<IndexRoute component={ProductList} />
<Route path="add" component={ProductAddItem} />
</Route>
<Route path="trades" >
<IndexRoute component={TradeList} />
<Route path=":id" component={TradeItem} />
</Route>
<Route path="/actived" component={Home}/>
<Route path="/completed" component={Home}/>
</Route>
<Route path="/login" component={Login} />
<Route path="*" component={NotFound}/>
</Route>
);
};
import rootSaga from './index';
import { take, fork, cancel } from 'redux-saga/effects';
const sagas = [rootSaga];
export const CANCEL_SAGAS_HMR = 'CANCEL_SAGAS_HMR';
function createAbortableSaga (saga) {
if (process.env.NODE_ENV === 'development') {
return function* main () {
const sagaTask = yield fork(saga);
yield take(CANCEL_SAGAS_HMR);
yield cancel(sagaTask);
};
} else {
return saga;
}
}
const SagaManager = {
startSagas(sagaMiddleware) {
sagas.map(createAbortableSaga).forEach(saga => sagaMiddleware.run(saga));
},
cancelSagas(store) {
store.dispatch({
type: CANCEL_SAGAS_HMR
});
}
};
export default SagaManager;
import {fork} from 'redux-saga/effects';
// Use require.context to require sagas automatically
// Ref: https://webpack.github.io/docs/context.html
const context = require.context('./', false, /\.js$/);
const keys = context.keys().filter(item => item !== './index.js' && item !== './SagaManager.js');
export default function* root() {
for (let i = 0; i < keys.length; i++) {
let fn = context(keys[i]);
if (typeof fn === 'function') {
yield fork(fn);
} else if (typeof fn['default'] === 'function') {
yield fork(fn['default']);
}
// yield fork(context(keys[i]));
}
}
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel, select} from 'redux-saga/effects';
import {fetchList} from '../services/product';
import {message} from 'antd';
function* getList(query) {
try {
const {total, items} = yield call(fetchList, query);
yield put({
type: 'FETCH_PRODUCT_LIST_SUCCESS',
total,
items
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_PRODUCT_LIST_FAILED',
err,
});
}
}
function* watchProductList() {
while (true) {
const {query} = yield take('FETCH_PRODUCT_LIST');
yield fork(getList, query);
}
}
export default function*() {
yield fork(watchProductList);
}
import { takeLatest } from 'redux-saga';
import { take, call, put, fork, cancel } from 'redux-saga/effects';
import { getAll } from '../services/todos';
import { message } from 'antd';
function* getTodos() {
try {
const data = yield call(getAll);
if (data) {
yield put({
type: 'todos/get/success',
payload: data,
});
}
} catch (err) {
message.error(err);
//yield put({
// type: 'todos/get/failed',
// err,
//});
}
}
function* watchTodosGet() {
yield takeLatest('todos/get', getTodos)
}
export default function* () {
yield fork(watchTodosGet);
// // Load todos.
// yield put({
// type: 'todos/get',
// });
}
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel, select} from 'redux-saga/effects';
import {fetchList, fetchItem} from '../services/trade';
import {message} from 'antd';
function* getList(query) {
try {
const {total, items} = yield call(fetchList, query);
yield put({
type: 'FETCH_TRADE_LIST_SUCCESS',
total,
items
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_TRADE_LIST_FAILED',
err,
});
}
}
function* watchTradeList() {
while (true) {
const {query} = yield take('FETCH_TRADE_LIST');
yield fork(getList, query);
}
}
function* getItem(id) {
try {
const item = yield call(fetchItem, id);
yield put({
type: 'FETCH_TRADE_ITEM_SUCCESS',
item
});
} catch (err) {
console.log(err);
message.error(err);
yield put({
type: 'FETCH_TRADE_ITEM_FAILED',
err,
});
}
}
function* watchTradeItem() {
while (true) {
const {id} = yield take('FETCH_TRADE_ITEM');
yield fork(getItem, id);
}
}
export default function*() {
yield fork(watchTradeList);
yield fork(watchTradeItem);
}
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel} from 'redux-saga/effects';
import {fetch, clear, save} from '../services/user';
import {message} from 'antd';
function* authorize(username, password, push) {
try {
const user = yield call(fetch, username, password);
console.log(user);
yield put({type: 'LOGIN_SUCCESS', user});
console.log('login ok');
push('/');
} catch (error) {
yield put({type: 'LOGIN_ERROR', error})
}
}
function* loginFlow() {
while (true) {
console.log('login flow');
const {username, password, push} = yield take('LOGIN_REQUEST');
yield fork(authorize, username, password, push);
yield take(['LOGOUT', 'LOGIN_ERROR']);
yield call(clear);
}
}
export default function* () {
yield fork(loginFlow);
}
import xFetch from './xFetch';
import {params} from '../utils';
export async function fetchList(query) {
return xFetch('/api/products' +'?' + params(query));
}
import xFetch from './xFetch';
export async function getAll() {
return xFetch('/api/todos');
}
import xFetch from './xFetch';
import {params} from '../utils';
export async function fetchList(query) {
return xFetch('/api/trades' +'?' + params(query));
}
export async function fetchItem(id) {
return xFetch('/api/trades/'+ id);
}
import xFetch from './xFetch';
export async function fetch(username, password) {
let form = new FormData();
form.append('username', username);
form.append('password', password);
return xFetch('/api/authenticate', {
method: 'POST',
body: form
});
}
export async function clear() {
sessionStorage.clear();
return Promise.resolve();
}
export async function save(user) {
sessionStorage.setItem('user', user);
return Promise.resolve(user);
}
import fetch from 'isomorphic-fetch';
// import cookie from 'js-cookie';
const errorMessages = (res) => `${res.status} ${res.statusText}`;
function check401(res) {
if (res.status === 401) {
//location.href = '/401';
}
return res;
}
function check404(res) {
if (res.status === 404) {
return Promise.reject(errorMessages(res));
}
return res;
}
function jsonParse(res) {
return res.json().then(json => ({...res, json}));
}
function errorMessageParse(res) {
const {status, msg, message, result} = res.json;
if (status != 1) {
return Promise.reject(msg || message);
}
return result;
}
function xFetch(url, options) {
const opts = {...options};
let user;
try{
user = JSON.parse(sessionStorage.getItem('user'));
}catch(ex){
user = {};
}
opts.headers = {
...opts.headers,
authorization: user.token || '',
};
return fetch(url, opts)
.then(check401)
.then(check404)
.then(jsonParse)
.then(errorMessageParse);
}
export default xFetch;
export const params = query => Object.keys(query).map((key)=> {
return key + '=' + query[key];
}).join('&');
export const leftPad = num => {
return num >= 10 ? num : ('0' + num);
};
export const formatDateTime = (time = 0, format = 'YYYY-MM-DD hh:mm:ss') => {
const date = new Date(time);
const data = {
YYYY: date.getFullYear(),
MM: leftPad(date.getMonth() + 1),
DD: leftPad(date.getDate()),
hh: leftPad(date.getHours()),
mm: leftPad(date.getMinutes()),
ss: leftPad(date.getSeconds())
};
return format.replace(/[A-Za-z]+/g, metch => {
return data[metch];
});
};
export const productStatusToString = status => {
switch (status) {
case 0 :
return '无效, 已删除';
case 1 :
return '未发布';
case 5 :
return '预热中';
case 11:
return '募集中';
case 17:
return '暂停';
case 21:
return '已封账';
case 31:
return '产品成立';
case -9:
return '草稿';
default:
return '未定义';
}
};
export const tradeStatusToString = status => {
switch (status){
case 1 :
return '未报单,等待客户打款';
case 10:
return '已报单,等待验资';
case 11:
return '验资成功,等待项目成立';
case 16:
return '验资失败,资金未到账';
case 12:
return '待成立';
case 21:
return '项目已成立';
case 22:
return '项目成立';
case 31:
return '交易关闭';
case 32:
return '交易关闭';
case 33:
return '交易关闭';
default:
return '未定义';
}
};
export const tradeCreateTypeToString = type => {
switch (type){
case 1:
return '后台创建';
case 5:
return 'APP创建';
default:
return '未知创建者';
}
}
// Learn more on how to config.
// - https://github.com/ant-tool/atool-build#配置扩展
const webpack = require('atool-build/lib/webpack');
const fs = require('fs');
const path = require('path');
const glob = require('glob');
module.exports = function (webpackConfig) {
webpackConfig.babel.plugins.push('transform-runtime');
webpackConfig.babel.plugins.push(['antd', {
style: 'css', // if true, use less
}]);
// Enable this if you have to support IE8.
// webpackConfig.module.loaders.unshift({
// test: /\.jsx?$/,
// loader: 'es3ify-loader',
// });
// Parse all less files as css module.
webpackConfig.module.loaders.forEach(function (loader, index) {
if (typeof loader.test === 'function' && loader.test.toString().indexOf('\\.less$') > -1) {
loader.test = /\.dont\.exist\.file/;
}
if (loader.test.toString() === '/\\.module\\.less$/') {
loader.test = /\.less$/;
}
});
// Load src/entries/*.js as entry automatically.
const files = glob.sync('./src/entries/*.js');
const newEntries = files.reduce(function (memo, file) {
const name = path.basename(file, '.js');
memo[name] = file;
return memo;
}, {});
webpackConfig.entry = Object.assign({}, webpackConfig.entry, newEntries);
if (process.env.NODE_ENV !== 'production') {
webpackConfig.devtool = 'inline-source-map';
}
return webpackConfig;
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment