StandardTableList.js 9.8 KB

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