List.tsx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. /*
  2. * <<
  3. * Davinci
  4. * ==
  5. * Copyright (C) 2016 - 2017 EDP
  6. * ==
  7. * Licensed under the Apache License, Version 2.0 (the "License");
  8. * you may not use this file except in compliance with the License.
  9. * You may obtain a copy of the License at
  10. *
  11. * http://www.apache.org/licenses/LICENSE-2.0
  12. *
  13. * Unless required by applicable law or agreed to in writing, software
  14. * distributed under the License is distributed on an "AS IS" BASIS,
  15. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. * See the License for the specific language governing permissions and
  17. * limitations under the License.
  18. * >>
  19. */
  20. import React, { useEffect, useState, useCallback, useMemo } from 'react'
  21. import { useSelector, useDispatch } from 'react-redux'
  22. import { createStructuredSelector } from 'reselect'
  23. import { RouteComponentWithParams } from 'utils/types'
  24. import { makeSelectWidgets, makeSelectLoading } from './selectors'
  25. import { makeSelectCurrentProject } from 'containers/Projects/selectors'
  26. import { checkNameUniqueAction } from 'containers/App/actions'
  27. import { WidgetActions } from './actions'
  28. import Helmet from 'react-helmet'
  29. import { Link } from 'react-router-dom'
  30. import {
  31. Row,
  32. Col,
  33. Breadcrumb,
  34. Icon,
  35. Button,
  36. Table,
  37. Tooltip,
  38. Popconfirm
  39. } from 'antd'
  40. import { ButtonProps } from 'antd/lib/button'
  41. import { ColumnProps, SorterResult } from 'antd/lib/table'
  42. import Container, { ContainerTitle, ContainerBody } from 'components/Container'
  43. import Box from 'components/Box'
  44. import SearchFilterDropdown from 'app/components/SearchFilterDropdown'
  45. import CopyModal from './components/CopyModal'
  46. import utilStyles from 'assets/less/util.less'
  47. import { useTablePagination } from 'utils/hooks'
  48. import ModulePermission from 'containers/Account/components/checkModulePermission'
  49. import { initializePermission } from 'containers/Account/components/checkUtilPermission'
  50. import { IWidgetBase, IWidgetFormed } from './types'
  51. import widgetlibs from './config'
  52. import { IWidgetConfigBase } from './components/Widget'
  53. const mapStateToProps = createStructuredSelector({
  54. widgets: makeSelectWidgets(),
  55. loading: makeSelectLoading(),
  56. currentProject: makeSelectCurrentProject()
  57. })
  58. const columnTitle = {
  59. name: '组件名称',
  60. viewName: '数据资产名称'
  61. }
  62. const WidgetList: React.FC<RouteComponentWithParams> = (props) => {
  63. const dispatch = useDispatch()
  64. const tablePagination = useTablePagination(0)
  65. const { match, history } = props
  66. useEffect(() => {
  67. const projectId = +match.params.projectId
  68. if (projectId) {
  69. dispatch(WidgetActions.loadWidgets(projectId))
  70. }
  71. }, [])
  72. const { widgets, loading, currentProject } = useSelector(mapStateToProps)
  73. const onCheckName = useCallback(
  74. (widgetName: string, resolve: () => void, reject: (err: string) => void) =>
  75. dispatch(
  76. checkNameUniqueAction(
  77. 'widget',
  78. { name: widgetName, projectId: currentProject.id },
  79. resolve,
  80. reject
  81. )
  82. ),
  83. [currentProject]
  84. )
  85. const openCopyModal = useCallback(
  86. (widget: IWidgetBase) => () => {
  87. setCopyFromWidget(widget)
  88. setCopyModalVisible(true)
  89. },
  90. []
  91. )
  92. const copyWidget = useCallback((widget: IWidgetBase) => {
  93. dispatch(
  94. WidgetActions.copyWidget(widget, () => {
  95. setCopyModalVisible(false)
  96. })
  97. )
  98. }, [])
  99. const cancelCopy = useCallback(() => {
  100. setCopyModalVisible(false)
  101. }, [])
  102. const toWorkbench = useCallback(
  103. (widgetId?: number) => () => {
  104. sessionStorage.removeItem('editWidgetFromDashboard')
  105. const workbenchUrl = `/project/${match.params.projectId}/widget`
  106. history.push(widgetId ? `${workbenchUrl}/${widgetId}` : workbenchUrl)
  107. },
  108. []
  109. )
  110. const onDeleteWidget = useCallback(
  111. (widgetId: number) => () => {
  112. dispatch(WidgetActions.deleteWidget(widgetId))
  113. },
  114. []
  115. )
  116. const [filterText, setFilterText] = useState('')
  117. const [filterColumnKey, setFilterColumnKey] = useState('')
  118. const [tableSorter, setTableSorter] = useState<SorterResult<IWidgetBase>>(
  119. null
  120. )
  121. const [copyModalVisible, setCopyModalVisible] = useState(false)
  122. const [copyFromWidget, setCopyFromWidget] = useState<IWidgetBase>(null)
  123. const filterWidgets = useMemo(() => {
  124. if (!Array.isArray(widgets) || !widgets.length) {
  125. return []
  126. }
  127. const regex = new RegExp(filterText, 'gi')
  128. const filterWidgets = widgets.filter(
  129. (v) => v.name.match(regex) || v.viewName.match(regex) || v.description.match(regex)
  130. )
  131. return filterWidgets
  132. }, [filterText, widgets])
  133. const { widgetPermission, AdminButton, EditButton } = useMemo(
  134. () => ({
  135. widgetPermission: initializePermission(
  136. currentProject,
  137. 'widgetPermission'
  138. ),
  139. AdminButton: ModulePermission<ButtonProps>(
  140. currentProject,
  141. 'widget',
  142. true
  143. )(Button),
  144. EditButton: ModulePermission<ButtonProps>(
  145. currentProject,
  146. 'widget',
  147. false
  148. )(Button)
  149. }),
  150. [currentProject]
  151. )
  152. const searchWidget = useCallback((value: string, dataIndex: string) => {
  153. setFilterText(value)
  154. setFilterColumnKey(dataIndex)
  155. }, [])
  156. const getFilterProps = (dataIndex: string) => ({
  157. filterDropdown: ({ setSelectedKeys, selectedKeys }) => (
  158. <SearchFilterDropdown
  159. placeholder={columnTitle[dataIndex]}
  160. value={selectedKeys[0]}
  161. onChange={e => setSelectedKeys(e.target.value ? [e.target.value] : [])}
  162. onSearch={() => searchWidget(selectedKeys[0], dataIndex)}
  163. />
  164. ),
  165. sortOrder: tableSorter && tableSorter.columnKey === dataIndex ? tableSorter.order : void 0,
  166. render: (text: string) => {
  167. const regex = new RegExp(`(${filterText})`, 'gi')
  168. return (filterText && filterColumnKey === dataIndex ? (
  169. <span
  170. dangerouslySetInnerHTML={{
  171. __html: text.replace(
  172. regex,
  173. `<span class='${utilStyles.highlight}'>$1</span>`
  174. )
  175. }}
  176. />
  177. ) : (
  178. text
  179. ))
  180. }
  181. })
  182. const mappingIcon = (widgetConfig: IWidgetConfigBase) => {
  183. const selectedChart = widgetConfig.selectedChart
  184. const mode = widgetlibs[widgetConfig.mode]
  185. return mode[selectedChart - 1].icon
  186. }
  187. const columns: Array<ColumnProps<IWidgetFormed>> = [
  188. {
  189. title: columnTitle.name,
  190. dataIndex: 'name',
  191. sorter: (a, b) => (a.name > b.name ? 1 : -1),
  192. ...getFilterProps('name'),
  193. render: (_, record) => (
  194. <div>
  195. <i className={`iconfont ${mappingIcon(record.config)}`}></i>
  196. <span style={{ marginLeft: 8 }}>{record.name}</span>
  197. </div>
  198. )
  199. },
  200. {
  201. title: columnTitle.viewName,
  202. dataIndex: 'viewName',
  203. sorter: (a, b) => (a.name > b.name ? 1 : -1),
  204. ...getFilterProps('viewName')
  205. },
  206. {
  207. title: '描述',
  208. dataIndex: 'description'
  209. }
  210. ]
  211. if (widgetPermission) {
  212. columns.push({
  213. title: '操作',
  214. key: 'action',
  215. align: 'center',
  216. width: 145,
  217. render: (_, record) => (
  218. <span className='ant-table-action-column'>
  219. <Tooltip title='复制'>
  220. <EditButton
  221. icon='copy'
  222. shape='circle'
  223. type='ghost'
  224. onClick={openCopyModal(record)}
  225. />
  226. </Tooltip>
  227. <Tooltip title='修改' trigger='hover'>
  228. <EditButton
  229. icon='edit'
  230. shape='circle'
  231. type='ghost'
  232. onClick={toWorkbench(record.id)}
  233. />
  234. </Tooltip>
  235. <Popconfirm
  236. title='确定删除?'
  237. placement='bottom'
  238. onConfirm={onDeleteWidget(record.id)}
  239. >
  240. <Tooltip title='删除'>
  241. <AdminButton icon='delete' shape='circle' type='ghost' />
  242. </Tooltip>
  243. </Popconfirm>
  244. </span>
  245. )
  246. })
  247. }
  248. const tableChange = useCallback(
  249. (_1, _2, sorter: SorterResult<IWidgetBase>) => {
  250. setTableSorter(sorter)
  251. },
  252. []
  253. )
  254. return (
  255. <>
  256. <Container>
  257. <Helmet title='可视化组件' />
  258. {
  259. !history?.location?.pathname?.includes('dataShareService') && <ContainerTitle>
  260. <Row>
  261. <Col span={24} className={utilStyles.shortcut}>
  262. <Breadcrumb className={utilStyles.breadcrumb}>
  263. <Breadcrumb.Item>
  264. <Link to=''>Widget</Link>
  265. </Breadcrumb.Item>
  266. </Breadcrumb>
  267. <Link to={`/account/organization/${currentProject.orgId}`}>
  268. <i className='iconfont icon-organization' />
  269. </Link>
  270. </Col>
  271. </Row>
  272. </ContainerTitle>}
  273. <ContainerBody>
  274. <Box>
  275. <Box.Header>
  276. <Box.Title>
  277. <Icon type='bars' />
  278. 可视化组件列表
  279. </Box.Title>
  280. <Box.Tools>
  281. <Tooltip placement='bottom' title='新增'>
  282. <AdminButton
  283. type='primary'
  284. icon='plus'
  285. onClick={toWorkbench()}
  286. />
  287. </Tooltip>
  288. </Box.Tools>
  289. </Box.Header>
  290. <Box.Body>
  291. <Row>
  292. <Col span={24}>
  293. <Table
  294. rowKey='id'
  295. bordered
  296. dataSource={filterWidgets}
  297. columns={columns}
  298. pagination={tablePagination}
  299. loading={loading}
  300. onChange={tableChange}
  301. />
  302. </Col>
  303. </Row>
  304. </Box.Body>
  305. </Box>
  306. </ContainerBody>
  307. </Container>
  308. <CopyModal
  309. visible={copyModalVisible}
  310. loading={false}
  311. fromWidget={copyFromWidget}
  312. onCheckUniqueName={onCheckName}
  313. onCopy={copyWidget}
  314. onCancel={cancelCopy}
  315. />
  316. </>
  317. )
  318. }
  319. export default WidgetList