list.jsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. import React from 'react'
  2. import { Layout, Row, Col, Button, Icon, Card, Select, Menu, Dropdown } from 'antd'
  3. import { connect } from 'dva'
  4. import DeleteBox from '../common/deleteBox/deleteBox'
  5. import EmptyContent from '../common/emptyContent/index'
  6. import DataConnectBox from '../dataSourceDetail/dataConnectBox'
  7. import ListFilter from '../common/listFilter/index'
  8. import CusIcon from '../common/cusIcon/index'
  9. import './list.less'
  10. const CardGrid = Card.Grid
  11. const { Content } = Layout
  12. const { Option } = Select
  13. class DataConnect extends React.Component {
  14. constructor(props) {
  15. super(props);
  16. this.state = {
  17. visibleDeleteBox: false,
  18. }
  19. this.bodyRef = React.createRef();
  20. };
  21. componentDidMount() {
  22. const { dispatch } = this.props;
  23. this.setBodyWidth();
  24. dispatch({ type: 'dataConnect/fetchList' });
  25. window.addEventListener('resize', this.setBodyWidth);
  26. }
  27. componentWillUnmount() {
  28. window.removeEventListener('resize', this.setBodyWidth)
  29. }
  30. /**
  31. * 设置卡片容器宽度 = 每行最大卡片数量 * 卡片宽度
  32. */
  33. setBodyWidth = () => {
  34. const cardBody = this.bodyRef.current; // 卡片容器
  35. const parent = cardBody.parentNode; // 父级容器
  36. const pWidth = parent.offsetWidth; // 父级容器宽度
  37. const pPadding = 10 + 10; // 父级容器左右padding
  38. const cWidth = 160; // 每个卡片宽度
  39. const cMargin = 8 + 8; // 每个卡片左右margin
  40. const pTrueWidth = pWidth - pPadding; // 父容器实际可用宽度
  41. const cTrueWidth = cWidth + cMargin; // 卡片实际占用宽度
  42. const count = Math.floor(pTrueWidth/cTrueWidth); // 每行最大卡片数量
  43. const cardBodyWidth = (count - 1) * cTrueWidth; // 可能有滚动条,减少一个
  44. cardBodyWidth > 0 ? cardBody.style.width = cardBodyWidth + 'px' : void(0);
  45. }
  46. onSearch(list, text) {
  47. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  48. let filterLabel = (text || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
  49. return list.map(l => {
  50. let o = Object.assign({}, l);
  51. let reg = new RegExp('('+ filterLabel +'){1}', 'ig');
  52. if(o.name && o.name.search(reg) !== -1) {
  53. return o;
  54. }else if(o.description && o.description.search(reg) !== -1) {
  55. return o;
  56. }else {
  57. return null
  58. }
  59. }).filter(a => a!==null);
  60. }
  61. onSort(list) {
  62. return list.sort((a, b) => {
  63. return new Date(b.createDate) - new Date(a.createDate);
  64. });
  65. }
  66. handleVisibleChange = (flag) => {
  67. this.setState({ visibleGroupMenu: flag });
  68. }
  69. generateOperationMenu = (l) => {
  70. const { dispatch } = this.props;
  71. return (
  72. <Menu className='menu-operation'>
  73. <Menu.Item onClick={()=>{
  74. dispatch({ type: 'dataConnect/setSelected', selected: l });
  75. dispatch({ type: 'dataConnect/setNewModel', model: l });
  76. dispatch({ type: 'dataConnect/setNewModelFields', fields: [
  77. { name: 'visibleBox', value: true },
  78. { name: 'boxOperation', value: 'modify' }
  79. ] });
  80. }}>
  81. <Icon type="info-circle-o" />属性设置
  82. </Menu.Item>
  83. <Menu.Item onClick={(e) => {
  84. dispatch({ type: 'dataConnect/setSelected', selected: l });
  85. dispatch({ type: 'dataConnect/setNewModel', model: { ...l, code: null } });
  86. dispatch({ type: 'dataConnect/setNewModelFields', fields: [
  87. { name: 'visibleBox', value: true },
  88. { name: 'boxOperation', value: 'create' }
  89. ] });
  90. }}>
  91. <Icon type="copy" />复制新增
  92. </Menu.Item>
  93. <Menu.Item onClick={(e) => {
  94. dispatch({ type: 'dataConnect/setSelected', selected: l });
  95. this.setState({ visibleDeleteBox: true})
  96. }}>
  97. <Icon type="delete" />删除
  98. </Menu.Item>
  99. </Menu>
  100. )
  101. }
  102. generateCard() {
  103. const { dataConnect, dispatch } = this.props;
  104. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  105. let filterLabel = dataConnect.filterLabel ? (dataConnect.filterLabel + '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') : ''; // 添加转义符号
  106. let filterItem = dataConnect.filterItem
  107. let filterReg = new RegExp('(' + filterLabel + '){1}', 'ig');
  108. let cards = dataConnect.list.map(l => {
  109. let o = Object.assign({}, l);
  110. if(filterItem.type === 'date') {
  111. if(filterLabel===""){
  112. return o;
  113. }else if(filterLabel.indexOf('#')>-1){
  114. let start = filterLabel.split('#')[0]
  115. let end = filterLabel.split('#')[1]
  116. let nowTime = o[filterItem.name].getTime();
  117. if(nowTime>=start && nowTime<=end){
  118. return o;
  119. }
  120. return null
  121. }else{
  122. return null
  123. }
  124. }else {
  125. return ((o[filterItem.name] + '').search(filterReg) > -1) ? o : null
  126. }
  127. }).filter(a => a!==null).map( (l, i) => (
  128. <CardGrid className='dataconnect-card' key={i}>
  129. <Card
  130. bordered={false}
  131. hoverable={true}
  132. title={
  133. <Row type='flex' justify='start'
  134. onClick={() => {
  135. // 选中项设置
  136. dispatch({ type: 'dataConnect/setSelected', selected: l });
  137. dispatch({ type: 'dataConnect/setNewModel', model: l });
  138. dispatch({ type: 'dataConnect/setNewModelFields', fields: [
  139. { name: 'visibleBox', value: true },
  140. { name: 'boxOperation', value: 'modify' }
  141. ] });
  142. }}
  143. >
  144. <Col className='label' title={l.name}>
  145. { (filterItem.name === 'name' && filterLabel) ?
  146. ((l.name || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  147. return (
  148. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  149. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  150. fragment
  151. )
  152. }
  153. )) : l.name
  154. }
  155. </Col>
  156. </Row>
  157. }
  158. actions={[
  159. <div>
  160. {l.dbType === 'file' ? <Icon type='file' /> : <div><Icon type='database' />{l.dbType[0].toUpperCase() + l.dbType.slice(1)}</div>}
  161. </div>,
  162. <Dropdown overlay={this.generateOperationMenu(l)} trigger={['click']}>
  163. <Icon style={{ fontSize: '24px' }} type="ellipsis" theme="outlined" />
  164. </Dropdown>
  165. ]}
  166. >
  167. <div className='content'
  168. onClick={() => {
  169. // 选中项设置
  170. dispatch({ type: 'dataConnect/setSelected', selected: l });
  171. dispatch({ type: 'dataConnect/setNewModel', model: l });
  172. dispatch({ type: 'dataConnect/setNewModelFields', fields: [
  173. { name: 'visibleBox', value: true },
  174. { name: 'boxOperation', value: 'modify' }
  175. ] });
  176. }}
  177. >
  178. { /** TODO 这里只考虑了数据库的展示情况,需要兼容展示文件类型的数据链接 */ }
  179. <Row className='address'>
  180. { filterLabel && filterItem.name === 'address' ?
  181. ((l.address || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  182. return (
  183. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  184. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  185. fragment
  186. )
  187. }
  188. )) : l.address
  189. }
  190. </Row>
  191. <Row className='username'>
  192. { filterLabel && filterItem.name === 'userName' ?
  193. ((l.userName || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  194. return (
  195. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  196. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  197. fragment
  198. )
  199. }
  200. )) : l.userName
  201. }
  202. </Row>
  203. </div>
  204. </Card>
  205. </CardGrid>
  206. ))
  207. if(cards.length === 0) {
  208. return <EmptyContent />
  209. }
  210. return cards;
  211. }
  212. generateFilterItems = () => {
  213. const { filterItems } = this.props.dataConnect;
  214. return filterItems.map(t => <Option key={t.name} value={t.name}>{t.label}</Option>);
  215. }
  216. render() {
  217. const { dataConnect, dispatch } = this.props;
  218. const { selected } = dataConnect;
  219. const { visibleDeleteBox } = this.state;
  220. return (
  221. <Layout className='layout-dataconnect'>
  222. <Content>
  223. <Card title={
  224. <Row className='tools' type='flex' justify='space-between'>
  225. <Col style={{ display: 'flex' }}>
  226. </Col>
  227. <Col className='search'>
  228. <Col>
  229. <Button className='btn-refresh' onClick={() => {
  230. dispatch({ type: 'dataConnect/setFilterLabel', label: '' });
  231. dispatch({ type: 'dataConnect/fetchList', mandatory: true });
  232. }}>
  233. <CusIcon type='bi-refresh'/>
  234. </Button>
  235. </Col>
  236. <Col>
  237. <ListFilter modelName='dataConnect' model={dataConnect} />
  238. </Col>
  239. <Col>
  240. <Button className='btn-add' onClick={() => {
  241. // 设置新增默认值
  242. dispatch({ type: 'dataConnect/setNewModel', model: { dbType: 'oracle', dbName: 'orcl' } });
  243. dispatch({ type: 'dataConnect/setNewModelFields', fields: [
  244. { name: 'visibleBox', value: true },
  245. { name: 'boxOperation', value: 'create' }
  246. ] });
  247. }}>
  248. 添加数据链接
  249. </Button>
  250. </Col>
  251. </Col>
  252. </Row>
  253. }>
  254. <div ref={this.bodyRef} className='body'>
  255. { this.generateCard() }
  256. </div>
  257. </Card>
  258. </Content>
  259. {visibleDeleteBox && <DeleteBox
  260. visibleBox={visibleDeleteBox}
  261. text={<div><span>确定要删除数据链接【{selected.name}】吗?</span></div>}
  262. hideBox={() => {
  263. this.setState({
  264. visibleDeleteBox: false
  265. })
  266. }}
  267. okHandler={() =>{
  268. dispatch({ type: 'dataConnect/remoteDelete', code: selected.code })
  269. }}
  270. />}
  271. {dataConnect.newOne.visibleBox && <DataConnectBox />}
  272. </Layout>
  273. )
  274. }
  275. }
  276. function mapStateToProps({present: {dataConnect}}) {
  277. return { dataConnect }
  278. }
  279. export default connect(mapStateToProps)(DataConnect)