提交 a820e505 authored 作者: vipcxj's avatar vipcxj

init commit

上级 630dad0e
<component name="ProjectDictionaryState">
<dictionary name="yaohx_169">
<words>
<w>anticon</w>
<w>dropdown</w>
<w>infos</w>
<w>lodash</w>
<w>publics</w>
<w>sizeme</w>
<w>yyyy</w>
</words>
</dictionary>
</component>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="Eslint" enabled="true" level="ERROR" enabled_by_default="true" />
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="polyfill" level="application" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{polyfill}" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptSettings">
<option name="languageLevel" value="JSX" />
</component>
<component name="JsBowerSettings">
<node-interpreter value="project" />
<exe-path />
<config-path />
</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$/.idea/jbpm-demo01.iml" filepath="$PROJECT_DIR$/.idea/jbpm-demo01.iml" />
</modules>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectTasksOptions" suppressed-tasks="Less" />
</project>
\ No newline at end of file
// Karma configuration
// Generated on Thu Jun 29 2017 17:26:36 GMT+0800 (CST)
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha'],
// list of files / patterns to load in the browser
files: [
'./test/**/*.js',
],
// list of files to exclude
exclude: [
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'./test/**/*.js': ['webpack', 'sourcemap'],
},
webpack: {
devtool: 'inline-source-map',
module: {
rules: [{
test: /\.js$/,
// exclude this dirs from coverage
exclude: [/node_modules/],
loader: 'babel-loader',
}],
},
watch: true,
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['Chrome', 'Safari'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity,
});
};
......@@ -143,44 +143,44 @@ const toFunction = (keysExpr, valueExpr) => {
}
}
return ret;
} else if (moment.isDate(value) || moment.isMoment(value)) {
} else if (moment.isDate(value) || moment.isMoment(value) || is(value, 'String')) {
let ret = true;
if (!(/^\s*$/).test(parsedExpr.down)) {
const a = moment(value);
const b = moment(Number.parseFloat(parsedExpr.down));
if (parsedExpr.downClose) {
ret = ret && (a.isBefore(b) || a.isSame(b));
ret = ret && (a.isAfter(b) || a.isSame(b));
} else {
ret = ret && a.isBefore(b);
ret = ret && a.isAfter(b);
}
}
if (!(/^\s*$/).test(parsedExpr.up)) {
const a = moment(value);
const b = moment(Number.parseFloat(parsedExpr.up));
if (parsedExpr.upClose) {
ret = ret && (a.isAfter(b) || a.isSame(b));
ret = ret && (a.isBefore(b) || a.isSame(b));
} else {
ret = ret && a.isAfter(b);
ret = ret && a.isBefore(b);
}
}
return ret;
}
} else if (parsedExpr.operator === 'like') {
if (is(value, 'String')) {
return new RegExp(`^${parsedExpr.like
const reg = new RegExp(`^${parsedExpr.like
.replace(/%/g, '[\\s\\S]*')
.replace(/_/g, '[\\s\\S]')
.replace(/\[!([\s\S]+)]/, '[^$1]')}$`)
.test(value);
.replace(/\[!([\s\S]+)]/, '[^$1]')}$`);
return reg.test(value);
}
} else if (parsedExpr.operator === 'list') {
for (const test of parsedExpr.list) {
// noinspection EqualityComparisonWithCoercionJS
if (test != value) { // eslint-disable-line eqeqeq
return false;
if (test == value) { // eslint-disable-line eqeqeq
return true;
}
}
return true;
return false;
}
throw new Error(`Invalid input.${keysExpr}|${valueExpr}.`);
};
......
......@@ -3,6 +3,7 @@
"scripts": {
"start": "roadhog server",
"build": "roadhog build",
"test": "cross-env BABEL_ENV=test karma start karma.config.js",
"lint": "eslint --ext .js src test",
"precommit": "npm run lint"
},
......@@ -26,6 +27,7 @@
"react": "^15.4.0",
"react-dom": "^15.4.0",
"react-sizeme": "^2.3.4",
"uuid": "^3.1.0",
"word-wrap": "^1.2.3",
"xml2js": "^0.4.17"
},
......@@ -33,6 +35,9 @@
"babel-eslint": "^7.1.1",
"babel-plugin-dva-hmr": "^0.3.2",
"babel-plugin-transform-runtime": "^6.9.0",
"chai": "^4.0.2",
"cross-env": "^5.0.1",
"enzyme": "^2.9.1",
"eslint": "^3.12.2",
"eslint-config-airbnb": "^13.0.0",
"eslint-plugin-import": "^2.2.0",
......@@ -40,7 +45,9 @@
"eslint-plugin-react": "^6.8.0",
"expect": "^1.20.2",
"husky": "^0.12.0",
"karma": "^1.7.0",
"mockjs": "^1.0.1-beta3",
"react-test-renderer": "^15.6.1",
"redbox-react": "^1.3.2",
"roadhog": "^0.6.0"
}
......
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { Table } from 'antd'
class MyTable extends Component {
render() {
return (
<Table />
);
}
}
Table.propTypes = {};
Table.defaultProps = {};
export default MyTable;
import React, { Component } from 'react';
import dva from 'dva';
import { getApp } from '../../data/app';
const connect = (modelCreator, mapStateToProps, mapDispatchToProps, mergeProps, options) => (Comp) => {
const { name, model } = modelCreator();
const app = getApp();
class StatefulComponent extends Component {
componentWillMount() {
app.model(model);
}
componentWillUnmount() {
app.unmodel(model);
}
render() {
const { children, ...rest } = this.props;
const mapState = (state) => {
const props = mapStateToProps ? mapStateToProps(state) : {};
props[name] = state[model.namespace];
return props;
};
const Output = dva.connect(mapState, mapDispatchToProps, mergeProps, options)(Comp);
return (
<Output {...rest}>
{ children }
</Output>
);
}
}
return StatefulComponent;
};
export default connect;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { filterValidParams } from '../../utils/helper';
class TwoBind extends Component {
constructor(props, context) {
super(props, context);
this.state = {
value: '',
};
}
componentWillReceiveProps(nextProps) {
this.setState({
value: nextProps[nextProps.valueKey],
});
}
render() {
const { valueKey, cbKey, cbMap, wrapped, children, ...rest } = this.props.children.props;
const Wrapped = wrapped;
const callback = (cbValue) => {
this.setState({
value: cbMap(cbValue),
});
};
const props = {
[valueKey]: this.state.value,
[cbKey]: callback,
};
return (
<Wrapped {...rest} {...props}>
{ children }
</Wrapped>
);
}
}
TwoBind.propTypes = {
valueKey: PropTypes.string,
cbKey: PropTypes.string,
cbMap: PropTypes.func,
wrapped: PropTypes.func.isRequired,
};
const fucIdentity = value => value;
TwoBind.defaultProps = {
valueKey: 'value',
cbKey: 'onChange',
cbMap: fucIdentity,
};
const makeTwoBind = ({ valueKey, cbKey, cbMap } = {}) => (Comp) => {
const params = filterValidParams({ valueKey, cbKey, cbMap });
return <TwoBind {...params} wrapped={Comp} />;
};
export default makeTwoBind;
import React from 'react';
import moment from 'moment';
import modelCreator from './model';
import connect from '../../hoc/stateful';
import TableEx from '../../../components/table/index';
import config from '../../../utils/config';
import styles from './index.less';
const columns = [{
title: '流程',
dataIndex: 'pName',
key: 'pName',
filterType: 'text',
}, {
title: '任务',
dataIndex: 'nName',
key: 'nName',
filterType: 'text',
}, {
title: '状态',
dataIndex: 'state',
key: 'state',
filterType: 'enum',
filterEnums: [{
text: '状态1',
value: '状态1',
}, {
text: '状态2',
value: '状态2',
}, {
text: '状态3',
value: '状态3',
}, {
text: '状态4',
value: '状态4',
}, {
text: '状态5',
value: '状态5',
}],
}, {
title: '日期',
dataIndex: 'date',
key: 'date',
filterType: 'date',
render(date) {
return date.format(config.defaultDateTimeFormat);
},
}, {
title: '期限',
dataIndex: 'deadline',
key: 'deadline',
render(deadline) {
const now = moment();
const late = deadline.diff(now, 'days', true);
if (late < 0) {
const style = {
color: '#f04134',
};
return <span style={style}>{ `超时 ${deadline.from(now, true)}` }</span>;
} else if (late < 1) {
const style = {
color: '#ffbf00',
};
return <span style={style}>{ `仅剩 ${deadline.to(now, true)}` }</span>;
} else {
const style = {
color: '#00a854',
};
return <span style={style}>{ `还剩 ${deadline.to(now, true)}` }</span>;
}
},
}];
class DsTable extends React.Component {
constructor(props, context) {
super(props, context);
this.loadData = this::this.loadData;
this.getCurrent = this::this.getCurrent;
this.state = {
filters: [],
current: 1,
pageSize: 10,
};
}
componentDidMount() {
this.loadData();
}
getCurrent() {
const { num } = this.props.task;
const pageNum = ((num / this.state.pageSize) | 0) + 1;
const current = this.state.current > pageNum ? pageNum : this.state.current;
return current;
}
loadData() {
const filters0 = this.state.filters
.filter(({ filter }) => !!filter)
.map(({ key, filter }) => ([
`f-${key}`,
filter,
]));
const psz = this.state.pageSize; // eslint-disable-line no-shadow
const pst = (this.state.current - 1) * psz;
this.props.dispatch({ type: 'task/fetchTasks', payload: { pst, psz, filters: filters0 } });
}
render() {
const { list, num } = this.props.task;
const tableProps = {
dataSource: list,
columns,
filters: this.state.filters.map(filter => filter.filter),
loading: this.props.loading.effects['task/fetchTasks'],
pagination: {
current: this.state.current,
total: num,
pageSize: this.state.pageSize,
},
onChange: (pagination) => {
this.setState({
current: pagination.current,
pageSize: pagination.pageSize,
}, () => {
this.loadData();
});
},
onFilter: (filters) => {
this.setState({
filters,
current: 1,
}, () => {
this.loadData();
});
},
};
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<TableEx {...tableProps} />
</div>
</div>
);
}
}
export default connect(modelCreator, ({ loading }) => ({ loading }))(DsTable);
.wrapper {
position: absolute;
width: 100%;
height: 100%;
padding: 12px;
}
.container {
height: 100%;
padding: 24px;
background-color: #fff;
overflow: auto;
}
import moment from 'moment';
import uuid from 'uuid/v4';
import _ from 'lodash';
const prefix = uuid();
const modelCreator = () => {
const name = 'task';
const namespace = _.uniqueId(prefix);
return {
name,
model: {
namespace,
state: {
num: 0,
list: [],
},
reducers: {
queryCountSuccess(state, { payload: num }) {
return {
...state,
num,
};
},
queryTasksSuccess(state, { payload: list }) {
return {
...state,
list,
};
},
},
effects: {
*fetchTasks({ payload: { pst, psz, filters } }, { put }) {
const num = 100;
yield put({ type: 'queryCountSuccess', payload: num });
yield put({
type: 'queryTasksSuccess',
payload: _.range(0, num).map(idx => ({
key: idx.toString(),
pName: `流程${idx}`,
nName: `任务${idx}`,
state: `状态${(idx / 3 | 0) + 1}`,
date: moment(),
deadline: moment(),
})),
});
},
},
subscriptions: {},
},
};
};
export default modelCreator;
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { Table } from 'antd';
import Search from './search';
class TableEx extends Component {
constructor(props, context) {
super(props, context);
const columns = props.columns ? props.columns.map((column, index) => ({
key: column.dataIndex,
filtered: false,
filterDropdownVisible: false,
filter: props.filters ? props.filters[index] : null,
})) : [];
this.state = {
columns,
};
}
makeColumns() {
if (!this.props.columns) {
return [];
}
return this.props.columns.map((column, index) => {
const ret = {
...column,
};
const state = this.state.columns[index];
if (ret.filterType) {
const onSearch = (filter) => {
const newColumns = [...this.state.columns];
newColumns[index].filter = filter;
newColumns[index].filtered = !!filter;
newColumns[index].filterDropdownVisible = false;
this.setState({ columns: newColumns }, () => {
this.props.onFilter(this.state.columns.map(c => ({
key: c.key,
filter: c.filter,
})));
});
};
ret.filterDropdown = (
<Search type={ret.filterType} onSearchSubmit={onSearch} filterEnums={ret.filterEnums} />
);
ret.filterDropdownVisible = state.filterDropdownVisible;
ret.onFilterDropdownVisibleChange = (visible) => {
const newColumns = [...this.state.columns];
newColumns[index].filterDropdownVisible = visible;
this.setState({ columns: newColumns });
};
ret.filterIcon = Search.getIcon(ret.filterType, state.filtered);
}
return ret;
}, this);
}
render() {
return (
<Table {...this.props} columns={this.makeColumns()} />
);
}
}
const funcVoid = () => {};
TableEx.propTypes = {
onFilter: PropTypes.func,
};
TableEx.defaultProps = {
onFilter: funcVoid,
};
export default TableEx;
import React, {Component} from 'react';
import PropTypes from 'prop-types';
import { DatePicker, Button } from 'antd';
import moment from 'moment';
import config from '../../../utils/config';
import styles from './date.less';
const RangePicker = DatePicker.RangePicker;
class DateSearch extends Component {
constructor(props, context) {
super(props, context);
this.onClick = this::this.onClick;
this.onChange = this::this.onChange;
this.onOk = this::this.onOk;
this.bindContainer = this::this.bindContainer;
this.state = {
value: [moment(), moment()],
};
}
onClick() {
const [start, end] = this.state.value;
let sValue = '';
let eValue = '';
if (start && start.isValid()) {
sValue = start.valueOf().toString();
}
if (end && end.isValid) {
eValue = end.valueOf().toString();
}
const filter = (sValue || eValue) ? `~[${sValue},${eValue})` : null;
this.props.onSearchSubmit(filter);
}
onChange(value) {
this.setState({
value,
});
}
onOk(value) {
this.setState({
value,
});
}
bindContainer(node) {
this.container = node;
}
render() {
return (
<div ref={this.bindContainer} className={styles.container}>
<RangePicker
showTime
value={this.state.value}
format={config.defaultDateTimeFormat}
placeholder={['开始时间', '结束时间']}
onChange={this.onChange}
onOk={this.onOk}
getCalendarContainer={() => this.container}
/>
<Button className={styles.popupOk} onClick={this.onClick}>ok</Button>
</div>
);
}
}
DateSearch.propTypes = {
onSearchSubmit: PropTypes.func,
};
const funcVoid = () => {};
DateSearch.defaultProps = {
onSearchSubmit: funcVoid,
};
export default DateSearch;
@import "index";
.container:extend(.popup) {}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Checkbox, Button } from 'antd';
import styles from './enum.less';
class EnumSearch extends Component {
constructor(props, context) {
super(props, context);
this.submit = this::this.submit;
this.reset = this::this.reset;
const checked = props.filterEnums.map(() => false);
this.state = {
checked,
};
}
submit() {
const { onSearchSubmit } = this.props;
const values = this.props.filterEnums
.filter((filterEnum, index) => this.state.checked[index])
.map(filterEnum => filterEnum.value);
if (values && values.length === 1) {
onSearchSubmit(`=${values[0]}`);
} else if (values && values.length > 1) {
onSearchSubmit(`^${values.join(',')}`);
} else {
onSearchSubmit(null);
}
}
reset() {
this.setState({
checked: this.state.checked.map(() => false),
});
}
render() {
const filterEnums = this.props.filterEnums;
return (
<div className={styles.container}>
{
filterEnums.map((filterEnum, index) => {
const onChange = (e) => {
const checked = [...this.state.checked];
checked[index] = e.target.checked;
this.setState({ checked });
};
return (
<span key={index}>
<Checkbox checked={this.state.checked[index]} onChange={onChange}>
{ filterEnum.text }
</Checkbox>
<br />
</span>
);
}, this)
}
<div className={styles.buttonBar}>
<Button onClick={this.submit}>ok</Button>
<Button onClick={this.reset} className={styles.reset}>reset</Button>
</div>
</div>
);
}
}
EnumSearch.propTypes = {
filterEnums: PropTypes.arrayOf(PropTypes.shape({
text: PropTypes.string,
value: PropTypes.string,
})).isRequired,
onSearchSubmit: PropTypes.func,
};
const funcVoid = () => {};
EnumSearch.defaultProps = {
onSearchSubmit: funcVoid,
};
export default EnumSearch;
@import "index";
.container:extend(.popup) {}
.buttonBar {
margin-top: 8px;
.reset {
margin-left: 4px;
}
}
import React from 'react';
import PropTypes from 'prop-types';
import { Icon } from 'antd';
import TextSearch from './text';
import DateSearch from './date';
import EnumSearch from './enum';
const Search = (props) => {
const { type, onSearchSubmit, filterEnums, children, ...rest } = props;
if (type === 'text') {
return (
<TextSearch onSearchSubmit={onSearchSubmit} {...rest}>
{ children }
</TextSearch>
);
} else if (type === 'date') {
return <DateSearch onSearchSubmit={onSearchSubmit} {...rest} />;
} else if (type === 'enum') {
return <EnumSearch filterEnums={filterEnums} onSearchSubmit={onSearchSubmit} {...rest} />;
} else {
return null;
}
};
Search.getIcon = (type, filtered) => {
const style = {
color: filtered ? '#108ee9' : '#aaa',
};
if (type === 'text') {
return (
<Icon type="search" style={style} />
);
} else if (type === 'date') {
return (
<Icon type="calendar" style={style} />
);
} else if (type === 'enum') {
return (
<Icon type="filter" style={style} />
);
} else {
return null;
}
};
Search.propTypes = {
onSearchSubmit: PropTypes.func,
};
export default Search;
.popup {
border-radius: 6px;
box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
padding: 8px;
background-color: #fff;
}
.popupOk {
margin-left: 4px;
}
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { Input, Icon, Button } from 'antd';
import styles from './text.less';
class TextSearch extends Component {
constructor(props, context) {
super(props, context);
this.onChange = this::this.onChange;
this.bindRef = this::this.bindRef;
this.submit = this::this.submit;
this.emitEmpty = this::this.emitEmpty;
this.state = {
value: '',
};
}
onChange(e) {
const { onSearchPreChange, onSearchChanged } = this.props;
const value = onSearchPreChange(e.target.value);
this.setState({ value }, () => onSearchChanged(value));
}
bindRef(node) {
this.input = node;
}
submit() {
const { onSearchSubmit } = this.props;
const value = this.state.value;
const filter = value ? `@%${value}%` : null;
onSearchSubmit(filter);
}
emitEmpty() {
this.input.focus();
this.setState({
value: '',
});
}
render() {
const { onSearchPreChange, onSearchChanged, onSearchSubmit, ...rest } = this.props; // eslint-disable-line no-unused-vars
const props = {
suffix: this.state.value ? <Icon type="close" style={{ marginRight: '4px' }} onClick={this.emitEmpty} /> : null,
value: this.state.value,
onChange: this.onChange,
onPressEnter: this.submit,
ref: this.bindRef,
};
return (
<div className={styles.container}>
<div className={styles.inputWrapper}>
<Input {...rest} {...props} />
</div>
<Button className={styles.popupOk} onClick={this.submit}>ok</Button>
</div>
);
}
}
TextSearch.propTypes = {
onSearchPreChange: PropTypes.func,
onSearchChanged: PropTypes.func,
onSearchSubmit: PropTypes.func,
};
const funcIdentity = value => value;
const funcVoid = () => {};
TextSearch.defaultProps = {
onSearchPreChange: funcIdentity,
onSearchChanged: funcVoid,
onSearchSubmit: funcVoid,
};
export default TextSearch;
@import "index";
.container:extend(.popup) {}
.inputWrapper {
position: relative;
display: inline-block;
}
const data = {
app: null,
};
export function initApp(app) {
data.app = app;
}
export function getApp() {
return data.app;
}
......@@ -2,6 +2,7 @@ import dva from 'dva';
import createLoading from 'dva-loading';
import { browserHistory } from 'dva/router';
import moment from 'moment';
import { initApp } from './data/app';
import appModel from './models/app';
import routerConfig from './router';
import { showError } from './utils/error';
......@@ -19,6 +20,8 @@ const app = dva({
},
});
initApp(app);
app.model(appModel);
// 2. Plugins
......
......@@ -6,8 +6,6 @@ export default {
namespace: 'task',
state: {
num: 0,
pageStart: 0,
pageSize: 0,
list: [],
},
reducers: {
......@@ -17,36 +15,30 @@ export default {
num,
};
},
queryTasksSuccess(state, { payload: { pageStart, pageSize, list } }) {
queryTasksSuccess(state, { payload: list }) {
return {
...state,
pageStart,
pageSize,
list,
};
},
},
effects: {
*fetchTasks({ payload: { pst, psz } }, { put, call }) {
const num = Number.parseInt(yield call(countTasks), 10);
*fetchTasks({ payload: { pst, psz, filters } }, { put, call }) {
const num = Number.parseInt(yield call(countTasks, filters), 10);
yield put({ type: 'queryCountSuccess', payload: num });
const tasks = yield call(fetchTasks, { pst, psz });
const tasks = yield call(fetchTasks, { pst, psz, filters });
yield put({
type: 'queryTasksSuccess',
payload: {
pageStart: pst,
pageSize: psz,
list: tasks.map((task) => {
return {
key: `${task.pId}-${task.nId}`,
pName: task.pName,
nName: task.nName,
state: task.state,
date: moment(task.date, config.defaultDateTimeFormat),
deadline: moment(task.deadline, config.defaultDateTimeFormat),
};
}),
},
payload: tasks.map((task) => {
return {
key: `${task.pId}-${task.nId}`,
pName: task.pName,
nName: task.nName,
state: task.state,
date: moment(task.date, config.defaultDateTimeFormat),
deadline: moment(task.deadline, config.defaultDateTimeFormat),
};
}),
});
},
},
......
import React from 'react';
import PropTypes from 'prop-types';
import moment from 'moment';
import { Table, Input, Icon } from 'antd';
import { connect } from 'dva';
import TableEx from '../../../../components/table/index';
import config from '../../../../utils/config';
import styles from './list.less';
......@@ -10,18 +10,38 @@ const columns = [{
title: '流程',
dataIndex: 'pName',
key: 'pName',
filterType: 'text',
}, {
title: '任务',
dataIndex: 'nName',
key: 'nName',
filterType: 'text',
}, {
title: '状态',
dataIndex: 'state',
key: 'state',
filterType: 'enum',
filterEnums: [{
text: '状态1',
value: '状态1',
}, {
text: '状态2',
value: '状态2',
}, {
text: '状态3',
value: '状态3',
}, {
text: '状态4',
value: '状态4',
}, {
text: '状态5',
value: '状态5',
}],
}, {
title: '日期',
dataIndex: 'date',
key: 'date',
filterType: 'date',
render(date) {
return date.format(config.defaultDateTimeFormat);
},
......@@ -51,45 +71,72 @@ const columns = [{
},
}];
const psz = 10;
class List extends React.Component {
constructor(props, context) {
super(props, context);
this.onSearch = this::this.onSearch;
this.loadData = this::this.loadData;
this.getCurrent = this::this.getCurrent;
this.state = {
filters: [],
current: 1,
pageSize: 10,
};
}
componentDidMount() {
this.props.dispatch({ type: 'task/fetchTasks', payload: { pst: 0, psz } });
this.loadData();
}
getCurrent() {
const { num } = this.props.task;
const pageNum = ((num / this.state.pageSize) | 0) + 1;
const current = this.state.current > pageNum ? pageNum : this.state.current;
return current;
}
onSearch(value) {
console.log(value);
loadData() {
const filters0 = this.state.filters
.filter(({ filter }) => !!filter)
.map(({ key, filter }) => ([
`f-${key}`,
filter,
]));
const psz = this.state.pageSize; // eslint-disable-line no-shadow
const pst = (this.state.current - 1) * psz;
this.props.dispatch({ type: 'task/fetchTasks', payload: { pst, psz, filters: filters0 } });
}
render() {
const { list, pageStart, pageSize, num } = this.props.task;
const dispatch = this.props.dispatch;
const { list, num } = this.props.task;
const tableProps = {
dataSource: list,
columns,
filters: this.state.filters.map(filter => filter.filter),
loading: this.props.loading.effects['task/fetchTasks'],
pagination: {
current: (pageStart / pageSize) | 0,
current: this.state.current,
total: num,
pageSize,
pageSize: this.state.pageSize,
},
onChange: (pagination) => {
this.setState({
current: pagination.current,
pageSize: pagination.pageSize,
}, () => {
this.loadData();
});
},
onChange(pagination) {
const psz = pagination.pageSize; // eslint-disable-line no-shadow
const pst = pagination.current * psz;
dispatch({ type: 'task/fetchTasks', payload: { pst, psz } });
onFilter: (filters) => {
this.setState({
filters,
current: 1,
}, () => {
this.loadData();
});
},
};
return (
<div className={styles.wrapper}>
<div className={styles.container}>
<div className={styles.search}>
<Input.Search onSearch={this.onSearch} />
</div>
<Table {...tableProps} />
<TableEx {...tableProps} />
</div>
</div>
);
......
......@@ -9,9 +9,4 @@
padding: 24px;
background-color: #fff;
overflow: auto;
.search {
width: 300px;
margin-bottom: 24px;
}
}
import request from '../utils/request';
export async function countTasks() {
return request('/api/bpm/task/all/count');
export async function countTasks(filters = []) {
return request('/api/bpm/task/all/count', filters);
}
export async function fetchTasks({ pst, psz }) {
return request('/api/bpm/task/all/info', { pst, psz });
export async function fetchTasks({ pst, psz, filters }) {
const queryParams = [...filters];
queryParams.push(['pst', pst]);
queryParams.push(['psz', psz]);
return request('/api/bpm/task/all/info', queryParams);
}
import moment from 'moment';
import _ from 'lodash';
import { createJSEncrypt } from './jsencrypt';
import config from './config';
......@@ -112,3 +113,7 @@ export function makePromise1(thunk) {
});
}
export function filterValidParams(params) {
return _.pickBy(params, _.negate(_.isUndefined));
}
......@@ -105,8 +105,13 @@ export default function request(url, params = {}, options = {}, auth = true) {
}
token = encrypt(token);
}
let queryParams = _(token ? { ...params, token } : params)
.toPairs();
let queryParams;
if (Array.isArray(params)) {
queryParams = [...params, ['token', token]];
} else {
queryParams = _(token ? { ...params, token } : params)
.toPairs();
}
queryParams = queryParams.map(([k, v]) => (_.isNil(v) ? k : `${k}=${encodeURIComponent(v)}`));
queryParams = queryParams.join('&');
let realUrl = url;
......
/**
* Created by yaohx_169 on 2017/6/29.
*/
import React from 'react';
import { mount } from 'enzyme';
import chai from 'chai';
import DsTable from '../src/components/table/dstable';
chai.should();
describe('ioc', () => {
describe('stateful', () => {
const table = mount(<DsTable />);
console.log(table.find('td').at(0));
});
});
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论