StandardTableList.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. import React, { PureComponent, Fragment } from 'react';
  2. import { Alert, Table, Select, Input, Button, Pagination } from 'antd';
  3. import PropTypes from 'prop-types';
  4. import styles from './StandardTableList.less';
  5. function getSearchField(options) {
  6. if (options && options.keys && options.keys.length) {
  7. return options.keys[0].field;
  8. }
  9. }
  10. export default class StandardTableList extends PureComponent {
  11. static propTypes = {
  12. loading: PropTypes.bool,
  13. dataSource: PropTypes.array,
  14. columns: PropTypes.array,
  15. header: PropTypes.oneOfType([
  16. PropTypes.object,
  17. PropTypes.bool,
  18. ]),
  19. footer: PropTypes.oneOfType([
  20. PropTypes.object,
  21. PropTypes.bool,
  22. ]),
  23. showStatusSelect: PropTypes.bool,
  24. rowSelectable: PropTypes.bool,
  25. keepUIState: PropTypes.object,
  26. };
  27. static defaultProps = {
  28. keepUIState: {},
  29. loading: false,
  30. dataSource: [],
  31. columns: [],
  32. header: false,
  33. footer: false,
  34. showStatusSelect: true,
  35. rowSelectable: true,
  36. };
  37. constructor(props) {
  38. super(props);
  39. const {
  40. keepUIState,
  41. header: {
  42. basicSearch,
  43. },
  44. footer: {
  45. pagination,
  46. },
  47. } = this.props;
  48. this.state = {
  49. batchActionKey: keepUIState.batchActionKey,
  50. searchSelectKey: keepUIState.searchSelectKey || getSearchField(basicSearch),
  51. searchInputValue: keepUIState.searchInputValue || '',
  52. selectedKeys: keepUIState.selectedKeys || [],
  53. selectedStatusKey: keepUIState.selectedStatusKey || 'ALL',
  54. pageNo: keepUIState.pageNo || pagination.pageNo || 1,
  55. pageSize: keepUIState.pageSize || pagination.pageSize || 15,
  56. totalSize: keepUIState.totalSize || pagination.totalSize || 0,
  57. };
  58. }
  59. componentWillReceiveProps(nextProps) {
  60. if (nextProps.footer) {
  61. const { pagination } = nextProps.footer;
  62. const { pageNo, pageSize, totalSize } = pagination;
  63. this.setState({ pageNo, pageSize, totalSize });
  64. }
  65. }
  66. getListHeader = () => {
  67. const {
  68. showStatusSelect,
  69. header: { basicSearch, onAdvanceFilterClick, onCreateClick, onDownload, campusAmount },
  70. footer: { pagination },
  71. } = this.props;
  72. const { keys } = basicSearch;
  73. return (
  74. <div className={styles.header}>
  75. <div className={styles.headerSearch}>
  76. <div className={styles.basicSearch}>
  77. <div className={styles.left}>
  78. {showStatusSelect && (
  79. <Select
  80. onChange={this.handleStatusChange}
  81. value={this.state.selectedStatusKey}
  82. style={{ width: '20%', marginRight: 10 }}
  83. >
  84. <Select.Option key="all" value="ALL">全部状态</Select.Option>
  85. <Select.Option key="normal" value="NORMAL">正常</Select.Option>
  86. <Select.Option key="delete" value="DEL">已删除</Select.Option>
  87. </Select>
  88. )}
  89. <Input.Search
  90. value={this.state.searchInputValue}
  91. style={{ width: '75%' }}
  92. addonBefore={
  93. <Select
  94. placeholder="请选择"
  95. value={this.state.searchSelectKey}
  96. onChange={this.handleSearchSelectChange}
  97. >
  98. {keys.map(item => (
  99. <Select.Option
  100. key={item.field}
  101. value={item.field}
  102. >
  103. {item.name}
  104. </Select.Option>))}
  105. </Select>
  106. }
  107. placeholder="请输入"
  108. enterButton
  109. onChange={this.handleInputChange}
  110. onSearch={this.handleSearchBtnClick}
  111. />
  112. </div>
  113. <div className={styles.right}>
  114. {onAdvanceFilterClick && (
  115. <a
  116. className={styles.searchLevel}
  117. onClick={onAdvanceFilterClick}
  118. >高级筛选
  119. </a>
  120. )}
  121. {campusAmount !== undefined && (
  122. <span
  123. style={{ marginLeft: 5, marginRight: 10, }}
  124. >当前共有<span style={{fontWeight:600, color:'#5E8732'}}>{campusAmount}</span>个校区&nbsp;&nbsp;
  125. <span style={{fontWeight:600, color:'#5E8732'}}>{pagination.totalSize}</span>个终端用户
  126. </span>
  127. )}
  128. <Button icon="sync" onClick={this.handleRefreshBtnClick}>刷新</Button>
  129. {/* noCreate 参数控制是否显示新建按钮 */}
  130. {onCreateClick !== undefined && (
  131. <Button
  132. icon="plus"
  133. type="primary"
  134. style={{ marginLeft: 5 }}
  135. onClick={onCreateClick}
  136. >新建
  137. </Button>
  138. )}
  139. {onDownload !== undefined && (
  140. <Button
  141. icon="download"
  142. type="primary"
  143. style={{ marginLeft: 5 }}
  144. onClick={onDownload}
  145. >下载
  146. </Button>
  147. )}
  148. </div>
  149. </div>
  150. </div>
  151. <Alert
  152. message={(
  153. <Fragment>
  154. 已选择 <a style={{ fontWeight: 600 }}>{this.state.selectedKeys.length}</a> 项&nbsp;&nbsp;
  155. 总计 <a style={{ fontWeight: 600 }}>{pagination.totalSize}</a> 项&nbsp;&nbsp;
  156. </Fragment>
  157. )}
  158. type="info"
  159. showIcon
  160. />
  161. </div>
  162. );
  163. };
  164. getListFooter = () => {
  165. const { footer: { batchActions, pagination } } = this.props;
  166. const paginationProps = {
  167. total: this.state.totalSize,
  168. current: this.state.pageNo,
  169. pageSize: this.state.pageSize,
  170. showSizeChanger: true,
  171. showQuickJumper: true,
  172. onChange: this.handleListPageChange,
  173. onShowSizeChange: this.handleListPageSizeChange,
  174. };
  175. return (
  176. <div className={styles.footer}>
  177. {batchActions && (
  178. <div className={styles.batch}>
  179. <Select
  180. style={{ width: 100 }}
  181. placeholder="请选择..."
  182. value={this.state.batchActionKey}
  183. onChange={this.handleBatchActionSelectChange}
  184. >
  185. {batchActions.map(item => (
  186. <Select.Option key={item.key} value={item.key}>
  187. {item.name}
  188. </Select.Option>))}
  189. </Select>
  190. <Button
  191. type="primary"
  192. style={{ marginLeft: 15 }}
  193. onClick={this.handleBatchBtnClick}
  194. disabled={!this.state.selectedKeys.length}
  195. >确定
  196. </Button>
  197. </div>
  198. )}
  199. {pagination && (
  200. <div className={styles.pagination}>
  201. <Pagination
  202. {...paginationProps}
  203. showTotal={total => `共 ${total} 条`}
  204. />
  205. </div>)}
  206. </div>
  207. );
  208. };
  209. // 过滤
  210. handleFilterOperation = (kv) => {
  211. const {
  212. header: {
  213. onFilterClick,
  214. },
  215. } = this.props;
  216. const {
  217. searchSelectKey,
  218. searchInputValue,
  219. selectedStatusKey,
  220. pageNo,
  221. pageSize,
  222. } = this.state;
  223. const queryParams = {
  224. pageNo,
  225. pageSize,
  226. [searchSelectKey]: searchInputValue,
  227. status: selectedStatusKey,
  228. ...kv,
  229. };
  230. if (queryParams.status === 'ALL') {
  231. delete queryParams.status;
  232. }
  233. onFilterClick(queryParams, this.state);
  234. };
  235. // 单选/取消单选
  236. handleItemSelectChange = (itemId, checked) => {
  237. const { selectedKeys } = this.state;
  238. const newSelectedKeys = [...selectedKeys];
  239. if (checked) {
  240. this.setState({ selectedKeys: [...newSelectedKeys, itemId] });
  241. } else {
  242. this.setState({ selectedKeys: newSelectedKeys.filter(a => a !== itemId) });
  243. }
  244. };
  245. // 刷新页面,重置筛选参数
  246. cleanFilterParams = () => {
  247. const { header: { basicSearch } } = this.props;
  248. this.setState({
  249. batchActionKey: undefined,
  250. searchSelectKey: getSearchField(basicSearch),
  251. selectedKeys: [],
  252. searchInputValue: '',
  253. allChecked: false,
  254. selectedStatusKey: 'ALL',
  255. });
  256. };
  257. // 过滤状态
  258. handleStatusChange = (value) => {
  259. this.setState({
  260. selectedStatusKey: value,
  261. }, () =>
  262. this.handleFilterOperation({ status: value })
  263. );
  264. };
  265. // 选择搜索字段
  266. handleSearchSelectChange = (value) => {
  267. this.setState({ searchSelectKey: value });
  268. };
  269. // 响应input输入
  270. handleInputChange = (e) => {
  271. this.setState({ searchInputValue: e.target.value });
  272. };
  273. // 筛选搜索操作
  274. handleSearchBtnClick = (value) => {
  275. const { searchSelectKey } = this.state;
  276. this.setState({
  277. searchInputValue: value,
  278. }, () =>
  279. this.handleFilterOperation({ [searchSelectKey]: value, pageNo: 1 })
  280. );
  281. };
  282. // 刷新操作
  283. handleRefreshBtnClick = () => {
  284. this.handleFilterOperation({});
  285. };
  286. // list pageNo变化
  287. handleListPageChange = (page, pageSize) => {
  288. this.setState({
  289. pageSize,
  290. pageNo: page,
  291. }, () => {
  292. this.handleFilterOperation({
  293. pageSize,
  294. pageNo: page,
  295. });
  296. });
  297. };
  298. // list pageSize变化
  299. handleListPageSizeChange = (current, size) => {
  300. this.setState({
  301. pageSize: size,
  302. pageNo: current,
  303. }, () => {
  304. this.handleFilterOperation({
  305. pageSize: size,
  306. pageNo: current,
  307. });
  308. });
  309. };
  310. // 选择批量处理类型
  311. handleBatchActionSelectChange = (value) => {
  312. this.setState({ batchActionKey: value });
  313. };
  314. // 批量处理操作
  315. handleBatchBtnClick = () => {
  316. const { footer: { onBatchClick } } = this.props;
  317. const { batchActionKey, selectedKeys } = this.state;
  318. onBatchClick(batchActionKey, selectedKeys);
  319. };
  320. handleRowSelectChange = (selectedKeys) => {
  321. this.setState({ selectedKeys });
  322. // 把selectedKeys传递给父组件
  323. if (this.props.rowSelectChange) {
  324. this.props.rowSelectChange(selectedKeys);
  325. }
  326. };
  327. render() {
  328. const {
  329. loading,
  330. columns,
  331. dataSource,
  332. header,
  333. footer,
  334. rowSelectable,
  335. ...restProps
  336. } = this.props;
  337. const listFooter = footer ? this.getListFooter : false;
  338. const listHeader = header ? this.getListHeader : false;
  339. const { selectedKeys } = this.state;
  340. const rowSelection = rowSelectable ? {
  341. selectedKeys,
  342. onChange: this.handleRowSelectChange,
  343. getCheckboxProps: record => ({
  344. disabled: record.disabled,
  345. }),
  346. } : null;
  347. return (
  348. <Table
  349. bordered={false}
  350. title={listHeader}
  351. footer={listFooter}
  352. loading={loading}
  353. rowKey={record => record.key}
  354. rowSelection={rowSelection}
  355. columns={columns}
  356. dataSource={dataSource}
  357. pagination={false}
  358. className={styles.table}
  359. {...restProps}
  360. />
  361. );
  362. }
  363. }