list.jsx 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. import React from 'react'
  2. import { Layout, Button, Icon, Input, Menu, Dropdown, Card, Col, Row } from 'antd'
  3. import { connect } from 'dva'
  4. import { dateFormat } from '../../utils/baseUtils'
  5. import Ellipsis from 'ant-design-pro/lib/Ellipsis'
  6. import DistributeBox from './distributeBox'
  7. import TransferBox from './transferBox'
  8. import 'ant-design-pro/dist/ant-design-pro.css'
  9. import Thumbnail from './thumbnail'
  10. import './list.less'
  11. const { Content } = Layout
  12. const { Search } = Input
  13. const CardGrid = Card.Grid
  14. class DashboardList extends React.Component {
  15. constructor(props) {
  16. super(props);
  17. this.state = {
  18. selectedRecord: null,
  19. visibleChooseDataSourceBox: false,
  20. visibleDistributeBox: false,
  21. visibleTransferBox: false,
  22. visibleGroupMenu: false, // 显示分组菜单
  23. }
  24. }
  25. componentDidMount() {
  26. const { dispatch } = this.props;
  27. this.setBodyWidth();
  28. dispatch({ type: 'dashboard/fetchList' });
  29. }
  30. /**
  31. * 设置卡片容器宽度 = 每行最大卡片数量 * 卡片宽度
  32. */
  33. setBodyWidth() {
  34. const chartBody = document.getElementsByClassName('dashboard-body')[0]; // 卡片容器
  35. const parent = chartBody.parentNode; // 父级容器
  36. const pWidth = parent.offsetWidth; // 父级容器宽度
  37. const pPadding = 10 + 10; // 父级容器左右padding
  38. const cWidth = 512; // 每个卡片宽度
  39. const cMargin = 5 + 5; // 每个卡片左右margin
  40. const pTrueWidth = pWidth - pPadding; // 父容器实际可用宽度
  41. const cTrueWidth = cWidth + cMargin; // 卡片实际占用宽度
  42. const count = Math.floor(pTrueWidth/cTrueWidth); // 每行最大卡片数量
  43. chartBody.style.width = count * cTrueWidth + 'px';
  44. }
  45. generateCard() {
  46. const { dashboard, dispatch } = this.props;
  47. const { selectedRecord } = this.state;
  48. const list = dashboard.list;
  49. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  50. let filterLabel = dashboard.filterLabel.replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
  51. const operationMenu = (
  52. <Menu className='menu-operation'>
  53. <Menu.Item onClick={() => {
  54. this.setState({visibleDistributeBox: true})
  55. }}>
  56. <Icon type='share-alt'/>分发
  57. </Menu.Item>
  58. <Menu.Divider />
  59. <Menu.Item
  60. onClick={()=>{
  61. this.setState({ visibleTransferBox: true})
  62. }}
  63. >
  64. <Icon type="swap" />移交
  65. </Menu.Item>
  66. <Menu.Item onClick={() => {
  67. dispatch({ type: 'dashboard/remoteDelete', code: selectedRecord.code });
  68. }}>
  69. <Icon type='delete'/>删除
  70. </Menu.Item>
  71. </Menu>
  72. )
  73. let cards = list.filter(l => {
  74. let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
  75. return (l.title || '').search(reg) !== -1 || (l.description || '').search(reg) !== -1;
  76. }).sort((a, b) => {
  77. return new Date(b.createTime) - new Date(a.createTime)
  78. }).map( (l, i) => (
  79. <CardGrid className='dashboard-card' key={i} onClick={() => {
  80. this.setState({ selectedRecord: l })
  81. }}>
  82. <Card
  83. title={
  84. <Row type='flex' justify='space-between'>
  85. <Col span={21} style={{ overflow: 'hidden', textOverflow: 'ellipsis' }} >
  86. { filterLabel ?
  87. ((l.title || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  88. return (
  89. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  90. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  91. fragment
  92. )
  93. }
  94. )) : l.title
  95. }
  96. </Col>
  97. <Col style={{ textAlign: 'right' }} span={3} >
  98. <Icon type='star-o'/>
  99. </Col>
  100. </Row>
  101. }
  102. cover={
  103. <Col className='cover-body'>
  104. <Row className='thumb' onClick={() => {
  105. dispatch({ type: 'dashboardDesigner/reset' });
  106. dispatch({ type: 'main/redirect', path: '/dashboard/' + l.code });
  107. }}>
  108. <Thumbnail type={l.type} code={l.code} option={l.chartOption}/>
  109. </Row>
  110. <Row className='desc'>
  111. <Ellipsis tooltip={l.description.length > 16} lines={2}>{
  112. <span>
  113. { filterLabel ?
  114. ((l.description || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  115. return (
  116. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  117. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  118. fragment
  119. )
  120. }
  121. )) : l.description
  122. }
  123. </span>
  124. }</Ellipsis>
  125. </Row>
  126. <Row className='footer' type='flex' justify='end' align='bottom'>
  127. <Col style={{ textAlign: 'left' }} span={22}>
  128. <Row>{l.creator} {dateFormat(l.createTime, 'yyyy-MM-dd')}</Row>
  129. </Col>
  130. <Col span={2} style={{ textAlign: 'right' }}>
  131. <Dropdown overlay={operationMenu} trigger={['click']}>
  132. <Icon type="ellipsis" />
  133. </Dropdown>
  134. </Col>
  135. </Row>
  136. </Col>
  137. }
  138. >
  139. </Card>
  140. </CardGrid>
  141. ));
  142. if(cards.length === 0) {
  143. cards = <div style={{ padding: '7px', textAlign: 'center', fontSize: '14px', color: 'rgba(0, 0, 0, 0.45)' }}>暂无数据</div>
  144. }
  145. return cards;
  146. }
  147. handleVisibleChange = (flag) => {
  148. this.setState({ visibleGroupMenu: flag });
  149. }
  150. hideGroupMenu = () => {
  151. this.setState({
  152. visibleGrouMenu: false
  153. });
  154. }
  155. render() {
  156. const { dispatch, dashboard } = this.props;
  157. const { visibleDistributeBox, visibleTransferBox } = this.state
  158. return (
  159. <Layout className='dashboard-list'>
  160. <Content>
  161. <Card title={
  162. <Row className='tools' type='flex' justify='space-between'>
  163. <Col style={{ display: 'flex' }}>
  164. </Col>
  165. <Col className='search'>
  166. <Col style={{ padding: '0 5px' }}>
  167. <Search
  168. placeholder="请输入关键字"
  169. value={dashboard.filterLabel}
  170. onChange={e => {
  171. dispatch({ type: 'dashboard/setFilterLabel', label: e.target.value });
  172. }}
  173. />
  174. </Col>
  175. <Col >
  176. <Button onClick={() => {
  177. dispatch({ type: 'main/redirect', path: { pathname: '/dashboard/create' } });
  178. }}>
  179. <Icon type="layout" />创建看板
  180. </Button>
  181. </Col>
  182. </Col>
  183. </Row>
  184. }>
  185. <div className='dashboard-body'>
  186. { this.generateCard() }
  187. </div>
  188. </Card>
  189. </Content>
  190. <DistributeBox
  191. visibleDistributeBox={visibleDistributeBox}
  192. selectedRecord={this.state.selectedRecord}
  193. hideBox={() => {
  194. this.setState({
  195. visibleDistributeBox: false
  196. });
  197. }} />
  198. <TransferBox
  199. visibleTransferBox={visibleTransferBox}
  200. onCancel={() => {
  201. this.setState({
  202. visibleTransferBox: false
  203. })
  204. }} />
  205. </Layout>
  206. )
  207. }
  208. }
  209. function mapStateToProps({ present: { dashboard } }) {
  210. return { dashboard }
  211. }
  212. export default connect(mapStateToProps)(DashboardList)