list.jsx 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468
  1. import React from 'react'
  2. import { Layout, Button, Icon, Table, Menu, Dropdown, Card, Col, Row, Select } from 'antd'
  3. import { connect } from 'dva'
  4. import TransferBox from '../common/selectUserBox/selectUserBox';
  5. import AccessObjectBox from '../common/accessObjectBox/accessObjectBox'
  6. import { dateFormat } from '../../utils/baseUtils'
  7. import DeleteBox from '../common/deleteBox/deleteBox'
  8. import ShareBox from './shareBox'
  9. import CopyBox from './copyBox'
  10. import ListFilter from '../common/listFilter/index'
  11. import EllipsisTooltip from '../common/ellipsisTooltip/index'
  12. import CusIcon from '../common/cusIcon/index'
  13. import './list.less'
  14. const { Content } = Layout
  15. const { Option } = Select
  16. class DashboardList extends React.Component {
  17. constructor(props) {
  18. super(props);
  19. this.state = {
  20. selectedRecord: null,
  21. visibleChooseDataSourceBox: false,
  22. visibleDistributeBox: false,
  23. visibleShareBox: false,
  24. shareUrl: '',
  25. visibleTransferBox: false,
  26. visibleGroupMenu: false, // 显示分组菜单
  27. visibleDeleteBox: false,
  28. visibleCopyBox: false,
  29. defaultSelectedGroups: [],
  30. defaultSelectedUsers: [],
  31. }
  32. }
  33. componentDidMount() {
  34. const { dashboard, dispatch } = this.props;
  35. const { currentMenu } = dashboard;
  36. this.setScrollTableHeight();
  37. console.log(currentMenu);
  38. if(currentMenu && currentMenu.code !== '-1') {
  39. dispatch({ type: 'dashboard/remoteMenuDashboardList', menuCode: currentMenu.code });
  40. }else {
  41. // dispatch({ type: 'dashboard/fetchList' });
  42. }
  43. document.getElementsByClassName('ant-table-body')[0].scrollTo(0, dashboard.listScrollTop);
  44. }
  45. componentWillUnmount() {
  46. const { dispatch } = this.props;
  47. window.removeEventListener('resize', this.setBodyWidth);
  48. dispatch({ type: 'dashboard/setField', name: 'listScrollTop', value: document.getElementsByClassName('ant-table-body')[0].scrollTop });
  49. }
  50. /**
  51. * 根据视图设置表格高度以呈现滚动条
  52. */
  53. setScrollTableHeight() {
  54. const view = document.getElementsByClassName('dashboard-view')[0];
  55. const mainContentBody = view.getElementsByClassName('dashboard-body')[0];
  56. const tableContentBody = mainContentBody.getElementsByClassName('ant-card-body')[0];
  57. const tableHeader = tableContentBody.getElementsByClassName('ant-table-header')[0];
  58. const tableBody = tableContentBody.getElementsByClassName('ant-table-body')[0];
  59. // 如果上下padding不一致就有问题了
  60. const padding = tableContentBody.children[0].getBoundingClientRect().top - tableContentBody.getBoundingClientRect().top;
  61. // table容器高度 - 上下padding - 表头高度 - 边框线宽
  62. tableBody.style.maxHeight = `${tableContentBody.offsetHeight - padding * 2 - tableHeader.offsetHeight - 2}px`;
  63. }
  64. getShareList = () => {
  65. new Promise((resolve, reject) => {
  66. const { dispatch } = this.props;
  67. const { selectedRecord } = this.state;
  68. dispatch({ type: 'dashboard/shareList', code: selectedRecord.code })
  69. .then(
  70. (resolve) => {
  71. const resData = resolve.data.data;
  72. const { groupNames: defaultSelectedGroups, userNames: defaultSelectedUsers } = resData;
  73. this.setState({
  74. visibleDistributeBox: true,
  75. defaultSelectedGroups: defaultSelectedGroups.map(g => ({
  76. code: g.id + '',
  77. name: g.name
  78. })),
  79. defaultSelectedUsers: defaultSelectedUsers.map(u => ({
  80. code: u.id + '',
  81. name: u.name
  82. })),
  83. });
  84. }
  85. ).catch(reject => {
  86. console.log(reject);
  87. });
  88. });
  89. }
  90. handleVisibleChange = (flag) => {
  91. this.setState({ visibleGroupMenu: flag });
  92. }
  93. hideGroupMenu = () => {
  94. this.setState({
  95. visibleGrouMenu: false
  96. });
  97. }
  98. distribute = (group, geren) => {
  99. const { dispatch } = this.props;
  100. const { selectedRecord } = this.state;
  101. let targets = group.map(g => ({
  102. code: g.code,
  103. name: g.name,
  104. isGroup: true
  105. })).concat(geren.map(g => ({
  106. code: g.code,
  107. name: g.name,
  108. isGroup: false
  109. })))
  110. dispatch({ type: 'dashboard/share', code: selectedRecord.code, targets });
  111. }
  112. onSearch(list, dashboard) {
  113. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  114. let filterLabel = dashboard.filterLabel ? (dashboard.filterLabel + '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') : ''; // 添加转义符号
  115. let filterItem = dashboard.filterItem;
  116. let filterReg = new RegExp('(' + filterLabel + '){1}', 'ig');
  117. return list.map(l => {
  118. let o = Object.assign({}, l);
  119. if(filterItem.type === 'date') {
  120. if(filterLabel===""){
  121. return o;
  122. }else if(filterLabel.indexOf('#')>-1){
  123. let start = filterLabel.split('#')[0]
  124. let end = filterLabel.split('#')[1]
  125. let nowTime = new Date(o[filterItem.name]).getTime();
  126. if(nowTime>=start && nowTime<=end){
  127. return o;
  128. }
  129. return null
  130. }else{
  131. return null
  132. }
  133. }else {
  134. return ((o[filterItem.name] + '').search(filterReg) > -1) ? o : null
  135. }
  136. }).filter(a => a!==null);
  137. }
  138. onSort(list) {
  139. return list.sort((a, b) => {
  140. return new Date(b.createTime) - new Date(a.createTime);
  141. });
  142. }
  143. generateFilterItems = () => {
  144. const { filterItems } = this.props.dashboard;
  145. return filterItems.map(t => <Option key={t.name} value={t.name}>{t.label}</Option>);
  146. }
  147. generateMenuItems = (menuTree) => {
  148. const { dispatch } = this.props;
  149. const { selectedRecord } = this.state;
  150. return menuTree.filter(t => t.type === 'menu').map(t => {
  151. if(t.children && t.children.length > 0) {
  152. return <Menu.SubMenu
  153. key={t.code}
  154. title={selectedRecord.menuCode === t.code ? <span className='current' style={{ fontWeight: 'bold' }}>{t.name}</span> : t.name}
  155. onTitleClick={() => {
  156. dispatch({ type: 'dashboard/remoteSetMenu', dashboard: selectedRecord, menu: t });
  157. let obj = {selectedRecord: null};
  158. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  159. this.setState(obj);
  160. }}
  161. >
  162. {this.generateMenuItems(t.children)}
  163. </Menu.SubMenu>
  164. }else {
  165. return <Menu.Item key={t.code} onClick={() => {
  166. dispatch({ type: 'dashboard/remoteSetMenu', dashboard: selectedRecord, menu: t });
  167. let obj = {selectedRecord: null};
  168. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  169. this.setState(obj);
  170. }}>{selectedRecord.menuCode === t.code ? <span className='current' style={{ fontWeight: 'bold' }}>{t.name}</span> : t.name}</Menu.Item>
  171. }
  172. })
  173. }
  174. // generateCurrentMenu = () => {
  175. // const { currentMenuParents } = this.props.dashboard;
  176. // let arr = [];
  177. // for(let i = currentMenuParents.length - 1; i >= 0; i--) {
  178. // arr.push();
  179. // }
  180. // return arr;
  181. // }
  182. render() {
  183. const { dispatch, dashboard, main } = this.props;
  184. const { visibleShareBox, shareUrl, visibleDistributeBox, visibleTransferBox, visibleDeleteBox,
  185. visibleCopyBox, selectedRecord, defaultSelectedGroups, defaultSelectedUsers } = this.state
  186. const { currentUser } = main;
  187. const { menuTree, filterItem, currentMenu, currentMenuParents } = dashboard;
  188. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  189. let filterLabel = dashboard.filterLabel ? (dashboard.filterLabel + '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') : ''; // 添加转义符号
  190. const moreOperatingMenu = (
  191. <Menu className='menu-operation'>
  192. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={() => {
  193. dispatch({ type: 'dashboard/getShareKey', record: selectedRecord, delay: 7 }).then((key) => {
  194. let obj = { visibleShareBox: true, shareUrl: window.location.origin + '/#/dashboard/share_key/' + key };
  195. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  196. this.setState(obj);
  197. });
  198. }}>
  199. <Icon type='share-alt'/>分享
  200. </Menu.Item>}
  201. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Divider />}
  202. {/* { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item onClick={this.getShareList}>
  203. <Icon type='share-alt'/>分发
  204. </Menu.Item>} */}
  205. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
  206. onClick={()=>{
  207. let obj = {visibleTransferBox: true};
  208. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  209. this.setState(obj);
  210. }}
  211. >
  212. <Icon type="swap" />移交
  213. </Menu.Item>}
  214. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Divider />}
  215. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.SubMenu className='setgroupmenu' title={<div><Icon style={{ marginRight: '6px' }} type='profile' />移动到</div>}>
  216. {this.generateMenuItems(menuTree)}
  217. </Menu.SubMenu>}
  218. { selectedRecord && (selectedRecord.dataConnects.length <= 1) && <Menu.Item
  219. onClick={()=>{
  220. let obj = {visibleCopyBox: true};
  221. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  222. this.setState(obj);
  223. }}
  224. >
  225. <Icon type="copy" />复制
  226. </Menu.Item> }
  227. { selectedRecord && currentUser.code === selectedRecord.creatorCode && <Menu.Item
  228. onClick={(e) => {
  229. let obj = {visibleDeleteBox: true};
  230. obj['visibleOperatingMenu' + selectedRecord.code] = false;
  231. this.setState(obj);
  232. }}
  233. >
  234. <Icon type="delete" />删除
  235. </Menu.Item>}
  236. </Menu>
  237. )
  238. const dashboardColumns = [{
  239. title: '名称',
  240. dataIndex: 'name',
  241. key: 'name',
  242. width: 100,
  243. render: (text, record) => {
  244. return (
  245. <span style={{ color: '#1890ff', cursor: 'pointer' }} onClick={() => {
  246. dispatch({ type: 'dashboardDesigner/reset' });
  247. dispatch({ type: 'main/redirect', path: '/dashboard/' + record.code });
  248. dispatch({ type: 'recent/addRecentRecord', tarId: record.code, recordType: 1});
  249. }}>
  250. { filterLabel && filterItem.name === 'name' ?
  251. ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  252. return (
  253. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  254. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  255. fragment
  256. )
  257. }
  258. )) : text
  259. }
  260. </span>
  261. )
  262. }
  263. }, {
  264. title: '备注',
  265. dataIndex: 'description',
  266. key: 'description',
  267. width: 200,
  268. onCell: () => {
  269. return {
  270. style: {
  271. whiteSpace: 'nowrap',
  272. maxWidth: 200,
  273. }
  274. }
  275. },
  276. render: (text) => <EllipsisTooltip title={text}>{
  277. filterLabel && filterItem.name === 'description' ? ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  278. return (
  279. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  280. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  281. fragment
  282. )
  283. })) : text
  284. }</EllipsisTooltip>,
  285. }, {
  286. title: '创建人',
  287. dataIndex: 'creatorName',
  288. key: 'creatorName',
  289. width: 100,
  290. render: (text, record) => {
  291. return (
  292. <span>
  293. { filterLabel && filterItem.name === 'creatorName' ?
  294. ((text || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  295. return (
  296. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  297. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  298. fragment
  299. )
  300. }
  301. )) : text
  302. }
  303. </span>
  304. )
  305. }
  306. }, {
  307. title: '创建时间',
  308. dataIndex: 'createTime',
  309. key: 'createTime',
  310. render: (text, record) => dateFormat(text, 'yyyy-MM-dd hh:mm:ss'),
  311. width: 100
  312. }, {
  313. title: '操作',
  314. key: 'action',
  315. render: (text, record, index) => (
  316. <Dropdown key={record.code} code={record.code} overlay={moreOperatingMenu} trigger={['click']} visible={this.state['visibleOperatingMenu' + record.code]} onVisibleChange={visible => {
  317. let obj = {};
  318. obj['visibleOperatingMenu' + record.code] = visible;
  319. this.setState(obj)
  320. }}>
  321. <Icon type="setting" />
  322. </Dropdown>
  323. ),
  324. width: 50
  325. }];
  326. return (
  327. <Layout className='dashboard-view'>
  328. <Content>
  329. <Card className="dashboard-body" title={
  330. <Row className='dashboard-tools' type='flex' justify='space-between'>
  331. <Col className='menus' style={{ display: 'flex' }}>
  332. {/* { this.generateCurrentMenu() } */}
  333. { [].concat(currentMenuParents).reverse().map(m => m.name).join(' > ') }
  334. </Col>
  335. <Col className='search'>
  336. <Col>
  337. <Button className='btn-refresh' onClick={() => {
  338. dispatch({ type: 'dashboard/setFilterLabel', label: '' });
  339. if(!currentMenu || currentMenu.code === '-1') {
  340. // dispatch({ type: 'dashboard/fetchList', mandatory: true });
  341. }else {
  342. dispatch({ type: 'dashboard/remoteMenuDashboardList', menuCode: currentMenu.code });
  343. }
  344. }}>
  345. <CusIcon type='bi-refresh'/>
  346. </Button>
  347. </Col>
  348. <Col>
  349. <ListFilter modelName='dashboard' model={dashboard}/>
  350. </Col>
  351. <Col>
  352. <Button disabled={!currentMenu || currentMenu.code === '-1'} onClick={() => {
  353. dispatch({ type: 'dashboardDesigner/reset' });
  354. dispatch({ type: 'dashboard/remoteQucikAdd', menuCode: currentMenu.code });
  355. }}>
  356. <Icon type="layout" />创建报表
  357. </Button>
  358. </Col>
  359. </Col>
  360. </Row>
  361. }>
  362. <Table
  363. className='dashboard-table'
  364. columns={dashboardColumns}
  365. dataSource={
  366. this.onSort(
  367. this.onSearch(dashboard.list, dashboard)
  368. )
  369. }
  370. size='small'
  371. scroll={{x: false, y: true}}
  372. pagination={false}
  373. onRow={(record) => {
  374. return {
  375. onClick: () => {
  376. this.setState({ selectedRecord: record})
  377. }
  378. }
  379. }}
  380. />
  381. </Card>
  382. </Content>
  383. {visibleDistributeBox && <AccessObjectBox
  384. visibleBox={visibleDistributeBox}
  385. hideBox={() => {
  386. this.setState({
  387. visibleDistributeBox: false
  388. })
  389. }}
  390. okHandler={this.distribute}
  391. defaultSelectedGroups={defaultSelectedGroups}
  392. defaultSelectedUsers={defaultSelectedUsers}
  393. />}
  394. {visibleTransferBox && <TransferBox
  395. visibleBox={visibleTransferBox}
  396. title='选择移交对象'
  397. okHandler={(user) => {
  398. dispatch({ type: 'dashboard/transfer', dashboardCode: this.state.selectedRecord.code, userCode: user.code });
  399. }}
  400. hideBox={() => {
  401. this.setState({
  402. visibleTransferBox: false
  403. })
  404. }}
  405. />}
  406. {visibleDeleteBox && <DeleteBox
  407. visibleBox={visibleDeleteBox}
  408. text={`确定要删除报表【${selectedRecord.name}】吗?`}
  409. hideBox={() => {
  410. this.setState({
  411. visibleDeleteBox: false
  412. })
  413. }}
  414. okHandler={() => {
  415. dispatch({ type: 'dashboard/remoteDelete', code: this.state.selectedRecord.code })
  416. }}
  417. />}
  418. {visibleShareBox && <ShareBox
  419. visibleBox={visibleShareBox}
  420. shareUrl={shareUrl}
  421. hideBox={() => {
  422. this.setState({
  423. visibleShareBox: false
  424. })
  425. }}
  426. onRefreshKey={(delay) => {
  427. return dispatch({ type: 'dashboard/getShareKey', record: this.state.selectedRecord, delay: delay })
  428. .then((key) => {
  429. this.setState({
  430. shareUrl: window.location.origin + '/#/dashboard/share_key/' + key
  431. })
  432. })
  433. }}
  434. />}
  435. {visibleCopyBox && <CopyBox
  436. visibleBox={visibleCopyBox}
  437. hideBox={()=>{this.setState({visibleCopyBox: false})}}
  438. currentDashboardCode={selectedRecord.code}
  439. currentDataConnect={selectedRecord.dataConnects[0]}
  440. />}
  441. </Layout>
  442. )
  443. }
  444. }
  445. export default connect(({ present: { main, dashboard } }) => ({ main, dashboard }))(DashboardList)