Commit 11ac5abf authored by superman's avatar superman

update

parent e404de17
This diff is collapsed.
......@@ -2,78 +2,6 @@
// - 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/catessddsd': function (req, res) {
setTimeout(()=>{
res.json({
status:1,
result:[
{
value:1,
label:'信托',
children:[
{
value:101,
label:'房产'
},{
value:102,
label:'政府'
},{
value:103,
label:'企业流贷'
}
]
},{
value:2,
label:'资管',
children:[
{
value:201,
label:'房产'
},{
value:202,
label:'政府'
},{
value:203,
label:'企业流贷'
}
]
},{
value:3,
label:'私募',
children:[
{
value:301,
label:'契约型'
}
]
}
]
})
},100);
},
'/api/*': 'http://react.yanky.cn/',
// '/api/*': 'http://192.168.1.126:8080/'
// '/api/*': 'http://react.yanky.cn/',
'/api/*': 'http://192.168.1.126:8080/'
};
......@@ -107,6 +107,16 @@ export default class App extends Component {
<MenuItemContent to="/upload" cn="图片上传" en="Upload Images"/>
</Menu.Item>
</SubMenu>
<SubMenu key="sub3" title={<span><Icon type="folder" /><span>管理员</span></span>}>
<MenuItemGroup title='用户管理' key='users'>
<Menu.Item>
<MenuItemContent to="/admin/users" cn="用户列表" en="Users"/>
</Menu.Item>
<Menu.Item>
<MenuItemContent to="/admin/users/create" cn="创建用户" en="Create users"/>
</Menu.Item>
</MenuItemGroup>
</SubMenu>
</Menu>
</section>
<footer>
......
......@@ -29,6 +29,7 @@
}
& > section {
flex: 1;
overflow: auto;
}
:global {
......
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {
Row,
Col,
Form,
Input,
Button,
Checkbox,
Select,
message,
Tabs,
Cascader,
Radio,
Upload,
Icon,
Modal,
DatePicker,
Table,
Spin
} from 'antd';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
import {formItemLayout, footerFormSubmitLayout} from '../../utils';
@connect(state=>({
loading: state.user.loading,
}))
@Form.create()
export default class AddItem extends Component {
constructor(props, content) {
super(props, content);
}
handleSubmit(e) {
e.preventDefault();
const data = this.props.form.getFieldsValue();
console.log(data);
this.props.dispatch({
type: 'CREATE_USER_ITEM',
data
});
}
render = ()=> {
const {loading, form:{getFieldProps}, location:{query}} = this.props;
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost" onClick={e=>{e.preventDefault(); this.props.history.goBack();}}>
<Icon type="rollback"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['用户管理', '添加用户']}
title="添加用户"
operation={operation}
/>
);
return (
<Layout header={header}>
<Spin spinning={loading}>
<Form className="main-form" horizontal onSubmit={this.handleSubmit.bind(this)}>
<Form.Item label="用户名" help="请使用手机号" {...formItemLayout}>
<Input placeholder="用户名" {...getFieldProps('username')} />
</Form.Item>
<Form.Item label="密码" {...formItemLayout}>
<Input placeholder="密码" type="password"
{...getFieldProps('password')} />
</Form.Item>
<Form.Item {...footerFormSubmitLayout} style={{marginTop:30}}>
<Button type="primary" htmlType="submit" loading={loading}><Icon type="save"/>创建</Button>
<Button onClick={e=>{e.preventDefault(); this.props.history.goBack();}}
style={{marginLeft:'1em'}}>
<Icon type="rollback"/>返回
</Button>
</Form.Item>
</Form>
</Spin>
</Layout>
);
}
}
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {Table, Icon, Row, Col, Button, Form, Input, Cascader, Select} from 'antd';
import {Link} from 'react-router';
import {serialize, formatDateTime, tradeStatusToString} from '../../utils';
import Layout from '../../components/Layout/Layout';
import MainHeader from '../../components/MainHeader/MainHeader';
const columns = [
{
title: '用户ID',
dataIndex: 'id',
key: 'id',
width: 70,
// fixed:'left'
}, {
title: '用户名',
dataIndex: 'shortTitle',
key: 'shortTitle',
render: (shortTitle, record)=>(<span title={shortTitle}>{(shortTitle + '').substring(0, 15)}</span>)
}, {
title: '预约时间',
dataIndex: 'reservationTime',
key: 'reservationTime',
width: 150,
className: 'tac',
render: (reservationTime, record)=>(
<span>
{reservationTime && formatDateTime(reservationTime)}
</span>
)
}, {
title: '投资人',
dataIndex: 'buyerName',
key: 'buyerName',
width: 80,
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: 160,
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/contract/'+ record.id} onClick={e=>e.stopPropagation()}>合同</Link>
{
(record.status == 11 || record.status == 21) &&
<span>
<span className="ant-divider"></span>
<Link to={'/trades/commission/'+ record.id} onClick={e=>e.stopPropagation()}>佣金</Link>
</span>
}
</span>
)
}
];
@connect(state=>({
items: state.user.items,
loading: state.user.loading,
total: state.user.total,
}))
@Form.create()
export default class List extends Component {
constructor(props, context) {
super(props, context);
this.state = {
filterVisible: false,
}
}
componentWillMount() {
this.fetchList(this.props.location.query);
};
fetchList(query) {
this.props.dispatch({
type: 'FETCH_USER_LIST',
query
});
}
handleRowClick({id}) {
this.props.history.push('/admin/users/' + id);
}
handleFilterVisible() {
this.setState({
filterVisible: !this.state.filterVisible
});
}
handleFilterSubmit(e) {
e.preventDefault();
// const formData = this.props.form.getFieldsValue();
// if (formData.categoryId && formData.categoryId[1]) {
// formData.categoryId = formData.categoryId[1];
// }
// const searchQuery = {...this.props.location.query, ...formData};
// console.log(searchQuery);
// this.props.history.replace(this.props.location.pathname + '?' + serialize(searchQuery));
// this.fetchList(searchQuery);
}
handleResetFilterForm(e) {
e.preventDefault();
// this.props.form.setFieldsValue({
// id: undefined,
// status: undefined,
// categoryId: undefined,
// title: undefined
// });
}
render() {
const {
total,
items,
loading,
history:{replace},
form:{getFieldProps},
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 + '?' + serialize(query));
this.fetchList(query);
},
onChange: (current) => {
console.log('Current: ', current);
query.p = current;
replace(pathname + '?' + serialize(query));
this.fetchList(query);
}
};
const operation = (
<div style={{textAlign:'right'}}>
<Button.Group>
<Button type="ghost" onClick={this.handleFilterVisible.bind(this)}>
<Icon type="filter"/>
</Button>
</Button.Group>
</div>
);
const header = (
<MainHeader breadcrumb={['用户管理', '用户列表']}
operation={operation}
title="用户列表">
{
this.state.filterVisible &&
<Form className="filterForm" inline onSubmit={this.handleFilterSubmit.bind(this)}>
<Form.Item label="ID">
<Input placeholder="请输入搜索ID" {...searchStyle} {...getFieldProps('id')}/>
</Form.Item>
<Form.Item label="类目">
{
cates &&
<Cascader options={cates} placeholder="请选产品类目" {...searchStyle}
{...getFieldProps('categoryId')}
/>
}
</Form.Item>
<Form.Item label="标题">
<Input placeholder="请输入搜索标题" {...searchStyle} {...getFieldProps('title')}/>
</Form.Item>
<Form.Item label="状态">
<Select placeholder="请选择状态" {...searchStyle} {...getFieldProps('status')}>
<Select.Option key="status-option-default"
value={null}>请选择</Select.Option>
</Select>
</Form.Item>
<Form.Item>
<Button.Group size="default">
<Button type="primary" size="default" htmlType="submit" loading={loading}><Icon
type="search"/>筛选</Button>
<Button type="ghost" size="default" htmlType="reset"
onClick={this.handleResetFilterForm.bind(this)}><Icon
type="cross-circle-o"/>清空</Button>
</Button.Group>
</Form.Item>
</Form>
}
</MainHeader>
);
return (
<Layout header={header}>
<Table className="list-table" columns={columns}
dataSource={Array.isArray(items)?items:[]}
loading={loading}
pagination={pagination}
scroll={{ y: window.innerHeight-(this.state.filterVisible? 203 :150) }}
onRowClick={this.handleRowClick.bind(this)}
/>
</Layout>
);
}
}
......@@ -21,3 +21,6 @@ export CustomMessageList from './CustomMessage/List';
export CustomMessageAddItem from './CustomMessage/AddItem';
export CustomMessageItem from './CustomMessage/Item';
export BaseUpload from './BaseFunction/BaseUpload';
export UsersAddItem from './Users/Additem';
export UsersList from './Users/List';
......@@ -2,9 +2,9 @@ import {handleActions} from 'redux-actions';
import {combineReducer} from 'redux';
let _user = sessionStorage.getItem('user');
try{
try {
_user = JSON.parse(_user);
}catch(ex){
} catch (ex) {
_user = {};
}
......@@ -21,10 +21,30 @@ const user = handleActions({
},
['LOGOUT'](state) {
return {...state, auth: {}, loading: false};
},
['CREATE_USER_ITEM'](state){
return {...state, loading: true};
},
['CREATE_USER_ITEM_SUCCESS'](state, action){
return {...state, loading: false};
},
['CREATE_USER_ITEM_FAILED'](state, action){
return {...state, loading: false, err: action.err};
},
['FETCH_USER_LIST'](state){
return {...state, loading: true};
},
['FETCH_USER_LIST_SUCCESS'](state, action){
return {...state, loading: false, items: action.items, total: action.total};
},
['FETCH_USER_LIST_FAILED'](state, action){
return {...state, loading: false, err: action.err};
}
}, {
..._user,
loading: false
loading: false,
});
export default user;
......@@ -23,7 +23,9 @@ import {
CustomMessageList,
CustomMessageAddItem,
CustomMessageItem,
BaseUpload
BaseUpload,
UsersAddItem,
UsersList
} from '../containers/index';
export default (store)=> {
......@@ -72,7 +74,13 @@ export default (store)=> {
<Route path="create" component={CustomMessageAddItem}/>
<Route path=":id" component={CustomMessageItem}/>
</Route>
<Route path="upload" component={BaseUpload} />
<Route path="upload" component={BaseUpload}/>
<Route path="admin">
<Route path="users">
<IndexRoute component={UsersList} />
<Route path="create" component={UsersAddItem} />
</Route>
</Route>
</Route>
<Route path="/login" component={Login}/>
<Route path="*" component={NotFound}/>
......
import {takeLatest} from 'redux-saga';
import {take, call, put, fork, cancel} from 'redux-saga/effects';
import {fetch, clear, save} from '../services/user';
import {fetch, clear, save, create, fetchList} from '../services/user';
import {message} from 'antd';
function* authorize(username, password, push) {
......@@ -10,8 +10,8 @@ function* authorize(username, password, push) {
yield put({type: 'LOGIN_SUCCESS', user});
console.log('login ok');
push('/');
} catch (error) {
yield put({type: 'LOGIN_ERROR', error})
} catch (err) {
yield put({type: 'LOGIN_ERROR', err})
}
}
......@@ -25,7 +25,49 @@ function* loginFlow() {
}
}
function* addItem(data) {
try{
yield call(create, data);
yield put({type: 'CREATE_USER_ITEM_SUCCESS'});
}catch(err){
console.log(err);
message.error(err);
yield put({type: 'CREATE_USER_ITEM_FAILED', err});
}
}
function* watchAddItem(){
while(true){
const {data} = yield take('CREATE_USER_ITEM');
yield fork(addItem, data);
}
}
function* getList(query){
try{
const {items, total} = yield call(fetchList, query);
yield put({
type:'FETCH_USER_LIST_SUCCESS',
items: items,
total
});
}catch(err){
console.log(err);
message.error(err);
yield put({type: 'FETCH_USER_LIST_FAILED', err});
}
}
function* watchList() {
while(true){
const {query} = yield take('FETCH_USER_LIST');
yield fork(getList, query);
}
}
export default function* () {
yield fork(loginFlow);
yield fork(watchAddItem);
yield fork(watchList);
}
......@@ -8,6 +8,17 @@ export async function fetch(username, password) {
});
}
export async function create(data) {
return xFetch('/api/users', {
method:'POST',
body: serialize(data)
});
}
export async function fetchList(query){
return xFetch('/api/admin/users' + '?' + serialize(query));
}
export async function clear() {
sessionStorage.clear();
return Promise.resolve();
......
......@@ -20,7 +20,11 @@ function check404(res) {
}
function jsonParse(res) {
return res.json().then(json => ({...res, json}));
try {
return res.json().then(json => ({...res, json}));
}catch(err){
return Promise.reject(err);
}
}
function errorMessageParse(res) {
......@@ -34,21 +38,20 @@ function errorMessageParse(res) {
function xFetch(url, options) {
const opts = {...options};
let user;
try{
try {
user = JSON.parse(sessionStorage.getItem('user')) || {};
}catch(ex){
} catch (ex) {
user = {};
}
opts.headers = {
...opts.headers,
authorization: user.token || '',
};
if(opts.method!='GET'){
if (opts.method != 'GET') {
opts.headers['content-type'] = 'application/x-www-form-urlencoded';
}
return fetch(url, opts)
.then(check401)
.then(check404)
......
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