accessConfig.jsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. import React from 'react'
  2. import { connect } from 'dva'
  3. import { Layout, Card, Row, Col, Table, Input, Checkbox, Button, Icon, Tag } from 'antd'
  4. // import FilterBox from '../common/filterBox/filterBox'
  5. // import AccessObjectBox from '../common/accessObjectBox/accessObjectBox'
  6. // import DeleteBox from '../common/deleteBox/deleteBox'
  7. import moment from 'moment'
  8. import './accessConfig.less';
  9. import Loadable from 'react-loadable';
  10. import SkeletonModal from 'components/common/skeletonModal';
  11. const FilterBox = Loadable({
  12. loader: () => import('components/common/filterBox/filterBox'),
  13. loading: () => <SkeletonModal />
  14. });
  15. const AccessObjectBox = Loadable({
  16. loader: () => import('components/common/accessObjectBox/accessObjectBox'),
  17. loading: () => <SkeletonModal />
  18. });
  19. const DeleteBox = Loadable({
  20. loader: () => import('components/common/deleteBox/deleteBox'),
  21. loading: () => <SkeletonModal />
  22. });
  23. const { Content } = Layout
  24. const Search = Input.Search;
  25. class DataSourceAccessConfig extends React.Component{
  26. constructor(props){
  27. super(props);
  28. this.state={
  29. inputRow: -1,
  30. filterLabel: '',
  31. visibleAccessObjectBox: false,
  32. visiblePolicyRuleBox: false, // 显示过滤规则编辑窗口
  33. currentPolicy: {}, // 选中的策略
  34. visibleFilterBox: false,
  35. visibleDeleteBox: false,
  36. }
  37. }
  38. componentDidMount() {
  39. const { dispatch } = this.props;
  40. dispatch({ type: 'user/fetchList' });
  41. }
  42. showAccessObjectBox = () => {
  43. this.setState({ visibleAccessObjectBox:true })
  44. }
  45. hideAccessObjectBox = () => {
  46. this.setState({ visibleAccessObjectBox:false })
  47. }
  48. showPolicyRuleBox= () => {
  49. this.setState({ visiblePolicyRuleBox: true })
  50. }
  51. hidePolicyRuleBox= () => {
  52. this.setState({ visiblePolicyRuleBox: false })
  53. }
  54. setAccessObject = (group, geren) => {
  55. const { dispatch } = this.props;
  56. const { currentPolicy } = this.state;
  57. let targets = group.map(g => ({
  58. code: g.code,
  59. name: g.name,
  60. isGroup: true
  61. })).concat(geren.map(g => ({
  62. code: g.code,
  63. name: g.name,
  64. isGroup: false
  65. })))
  66. dispatch({ type: 'dataSourcePolicy/remoteSetTarget', policy: currentPolicy, targets });
  67. }
  68. createFilters = (filters) => {
  69. const { dispatch } = this.props;
  70. let { currentPolicy } = this.state;
  71. currentPolicy.filters = filters;
  72. dispatch({ type: 'dataSourcePolicy/remoteModify', policy: currentPolicy });
  73. }
  74. /**
  75. * 生成过滤规则文本
  76. */
  77. createFilterLabel = (filter) => {
  78. let { label, operator, operatorLabel, type, value1, value2 } = filter;
  79. let filterLabel;
  80. if(type === 'string' || type === 'index') {
  81. if(operator === 'null' || operator === 'notNull') {
  82. filterLabel = `${label} ${operatorLabel}`;
  83. }else {
  84. filterLabel = `${label} ${operatorLabel} ${value1}`;
  85. }
  86. }else if(type === 'scale') {
  87. if(operator === 'null' || operator === 'notNull') {
  88. filterLabel = `${label} ${operatorLabel}`;
  89. }else if(operator === 'between') {
  90. filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`;
  91. }else {
  92. filterLabel = `${label} ${operatorLabel} ${value1}`;
  93. }
  94. }else if(type === 'time') {
  95. value1 = moment(value1).format('YYYY/MM/DD');
  96. value2 = moment(value2).format('YYYY/MM/DD');
  97. if(operator === 'null' || operator === 'notNull') {
  98. filterLabel = `${label} ${operatorLabel}`;
  99. }else if(operator === 'between') {
  100. filterLabel = `${label} ${operatorLabel} ${value1} ~ ${value2}`;
  101. }else {
  102. filterLabel = `${label} ${operatorLabel} ${value1}`;
  103. }
  104. }else if(type === 'categorical') {
  105. if(operator === 'null' || operator === 'notNull') {
  106. filterLabel = `${label} ${operatorLabel}`;
  107. }else {
  108. filterLabel = `${label} ${operatorLabel} ${value1}`;
  109. }
  110. }else {
  111. filterLabel = '错误条件';
  112. }
  113. return filterLabel;
  114. }
  115. render() {
  116. const { dataSourcePolicy, dataSourceDetail, dispatch } = this.props;
  117. const { visibleAccessObjectBox, visiblePolicyRuleBox, visibleDeleteBox, currentPolicy } = this.state;
  118. const polices = dataSourcePolicy.list;
  119. const group = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => t.isGroup) : []) : [];
  120. const geren = currentPolicy ? (currentPolicy.targets ? currentPolicy.targets.filter(t => !t.isGroup) : []) : [];
  121. const reg = new RegExp('([+ \\- & | ! ( ) { } \\[ \\] ^ \" ~ * ? : ( ) \/])', 'g'); // 需要转义的字符
  122. let filterLabel = (this.state.filterLabel || '').replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1'); // 添加转义符号
  123. const columnData = dataSourceDetail.columns ? dataSourceDetail.columns.filter(c => c.using).map(c => ({
  124. key: c.key,
  125. label: c.alias,
  126. name: c.name,
  127. selection: [],
  128. type: c.columnType,
  129. groupable: c.groupable,
  130. // filterable: c.filterable,
  131. filterable: true,
  132. bucketizable: c.bucketizable
  133. })) : [];
  134. const columns = [{
  135. title:'启用',
  136. dataIndex:'enabled',
  137. render: (v, r) => <Checkbox
  138. onChange={(e) => {
  139. let policy = {
  140. ...r,
  141. enabled: e.target.checked
  142. }
  143. dispatch({ type: 'dataSourcePolicy/remoteModify', policy });
  144. }}
  145. checked={v}
  146. />,
  147. width: 50
  148. },{
  149. title: '策略名',
  150. dataIndex: 'name',
  151. render: (value, record, index) => <div key={index}>
  152. {
  153. this.state.inputRow === index ? <Input value={value} suffix={<Icon style={{ cursor: 'pointer', color: '#52C41A' }} type="check-circle" onClick={() => {
  154. this.setState({
  155. inputRow: -1
  156. });
  157. dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
  158. }}/>} onChange={(e) => {
  159. dispatch({ type: 'dataSourcePolicy/modify', policy: { ...record, name: e.target.value } });
  160. }} onBlur={() => {
  161. this.setState({
  162. inputRow: -1
  163. });
  164. dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
  165. }} onPressEnter={() => {
  166. this.setState({
  167. inputRow: -1
  168. });
  169. dispatch({ type: 'dataSourcePolicy/remoteModify', policy: record });
  170. }}/> : <span onClick={() => {
  171. this.setState({
  172. inputRow: index
  173. });
  174. }}>
  175. { filterLabel ?
  176. ((value || '').split(new RegExp(`(${filterLabel})`, 'i')).map((fragment, i) => {
  177. return (
  178. fragment.toLowerCase().replace(new RegExp('(\\\\)', 'g'), '\\$1').replace(reg, '\\$1') === filterLabel.toLowerCase() ?
  179. <span key={i} style={{fontWeight: 'bold', color: 'red'}} className="highlight">{fragment}</span> :
  180. fragment
  181. )
  182. }
  183. )) : value
  184. }
  185. </span>
  186. }
  187. {
  188. this.state.inputRow !== index && <Icon style={{ cursor: 'pointer' }} type='edit' onClick={() => {
  189. this.setState({
  190. inputRow: index
  191. });
  192. }} />
  193. }
  194. </div>,
  195. width: 150
  196. }, {
  197. title: '行开放策略',
  198. dataIndex: 'filters',
  199. render: (filter, record, index) => <div key={index}>
  200. {filter.length > 0 ? filter.map((f, i) =>
  201. <Tag
  202. className='filter-tag'
  203. key={i}
  204. closable={false}
  205. >
  206. {this.createFilterLabel(f)}
  207. </Tag>
  208. ) : <Tag key='all' className='filter-tag' closable={false}>所有数据</Tag>}
  209. <Tag
  210. key='add'
  211. onClick={this.showPolicyRuleBox}
  212. className={`filter-tag filter-tag-add`}
  213. >
  214. <Icon type="filter" theme="outlined" />
  215. </Tag>
  216. </div>,
  217. width: 500
  218. }, {
  219. title: '开放对象',
  220. dataIndex: 'targets',
  221. render: (targets, record, index) => <div key={index}>
  222. {
  223. targets.map(item => <Tag className='user-tag' key={item.code}>{item.isGroup ? <Icon type="team" theme="outlined" /> : <Icon type="user" theme="outlined" />}{item.name}</Tag>)
  224. }
  225. <Tag key='add' onClick={this.showAccessObjectBox}><Icon type="plus" /></Tag>
  226. </div>,
  227. width: 300
  228. }, {
  229. title: '操作',
  230. render: (v,r) => <div><span style={{ cursor: 'pointer' }} onClick={() => {
  231. this.setState({
  232. visibleDeleteBox: true
  233. });
  234. }}><Icon type='delete'></Icon>删除</span></div>,
  235. width: 120
  236. }];
  237. return (
  238. <Layout className='policy-config'>
  239. <Content>
  240. <Card bordered={false} className='policy-body' title={
  241. <Row className='policy-tools' type='flex' justify='space-between'>
  242. <Col className='policy-public' style={{ display: 'flex' }}>
  243. {/* 完全开放数据源<Switch size="small"/> */}
  244. </Col>
  245. <Col className='search'>
  246. <Search
  247. placeholder="请输入关键字"
  248. value={this.state.filterLabel}
  249. onChange={e => {
  250. this.setState({
  251. filterLabel: e.target.value
  252. });
  253. }}
  254. />
  255. <Button className='add-btn' onClick={() => {
  256. dispatch({ type: 'dataSourcePolicy/remoteAdd', policy: {
  257. enabled: false,
  258. name: '新策略',
  259. targets: [],
  260. filters: []
  261. } });
  262. }}>
  263. 添加策略
  264. </Button>
  265. </Col>
  266. </Row>
  267. }>
  268. <Table
  269. className='policy-table'
  270. columns={columns}
  271. dataSource={polices ? polices.filter(l => {
  272. let reg = new RegExp('(' + filterLabel + '){1}', 'ig');
  273. return (l.name || '').search(reg) !== -1;
  274. }).map((p, i) => ({ ...p, key: i })) : []}
  275. size='small'
  276. pagination={false}
  277. onRow={(record) => {
  278. return {
  279. onClick: () => {this.setState({ currentPolicy: record})}
  280. }
  281. }}
  282. />
  283. </Card>
  284. {visiblePolicyRuleBox && <FilterBox
  285. type='dataSource'
  286. code={dataSourceDetail.code}
  287. columns={columnData}
  288. filterData={currentPolicy.filters}
  289. visibleFilterBox={visiblePolicyRuleBox}
  290. hideFilterBox={this.hidePolicyRuleBox}
  291. createFilters={this.createFilters}
  292. />}
  293. {visibleAccessObjectBox && <AccessObjectBox
  294. visibleBox={visibleAccessObjectBox}
  295. hideBox={this.hideAccessObjectBox}
  296. okHandler={this.setAccessObject}
  297. defaultSelectedGroups={group}
  298. defaultSelectedUsers={geren}
  299. />}
  300. {visibleDeleteBox && <DeleteBox
  301. visibleBox={visibleDeleteBox}
  302. hideBox={() => {
  303. this.setState({ visibleDeleteBox: false })
  304. }}
  305. text={<div><span>确定要删除策略【{currentPolicy.name}】吗?</span><br/><span style={{ color: 'red' }}>(此操作可能导致策略开放对象使用该数据源创建的图表失效!)</span></div>}
  306. okHandler={() =>{
  307. dispatch({ type: 'dataSourcePolicy/remoteDelete', code: currentPolicy.code });
  308. // dispatch({ type: 'dataSource/remoteDelete', code: selectedRecord.code })
  309. }}
  310. />}
  311. </Content>
  312. </Layout>
  313. );
  314. }
  315. }
  316. function mapStateToProps({ user, dataSourcePolicy, dataSourceDetail }) {
  317. return { user, dataSourcePolicy, dataSourceDetail }
  318. }
  319. export default connect(mapStateToProps)(DataSourceAccessConfig);