DashboardItem.tsx 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892
  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 from 'react'
  21. import classnames from 'classnames'
  22. import LocalControlPanel from 'containers/ControlPanel/Local'
  23. import DashboardItemMask from './DashboardItemMask'
  24. import DownloadCsv, { IDownloadCsvProps } from 'components/DownloadCsv'
  25. import {
  26. DrillCharts,
  27. WidgetDimension,
  28. DrillType,
  29. IDrillDetail
  30. } from 'components/DataDrill/types'
  31. import DataDrill from 'components/DataDrill/Panel'
  32. import DataDrillHistory from 'components/DataDrill/History'
  33. import { operationWidgetProps } from 'components/DataDrill/abstract/widgetOperating'
  34. import {
  35. strategiesOfDrillUpHasDrillHistory,
  36. strategiesOfDrillUpNullDrillHistory,
  37. strategiesOfDrillDownHasDrillHistory,
  38. strategiesOfDrillDownNullDrillHistory
  39. } from 'components/DataDrill/strategies'
  40. import { getLastItemValueOfArray } from 'components/DataDrill/util'
  41. import {
  42. IFormedView,
  43. IFormedViews,
  44. IShareFormedViews,
  45. IViewModel
  46. } from 'containers/View/types'
  47. import Widget, {
  48. IWidgetConfig,
  49. IPaginationParams,
  50. RenderType
  51. } from 'containers/Widget/components/Widget'
  52. import { ChartTypes } from 'containers/Widget/config/chart/ChartTypes'
  53. import { DrillableChart } from 'containers/Widget/config/chart/DrillableChart'
  54. import { IconProps } from 'antd/lib/icon'
  55. import { Icon, Tooltip, Popconfirm, Popover, Dropdown, Menu } from 'antd'
  56. import ModulePermission from 'containers/Account/components/checkModulePermission'
  57. import ShareDownloadPermission from 'containers/Account/components/checkShareDownloadPermission'
  58. import { IProject } from 'containers/Projects/types'
  59. import {
  60. IQueryConditions,
  61. IQueryVariableMap,
  62. TShareVizsType,
  63. ILoadData
  64. } from '../types'
  65. import { IWidgetFormed, IWidgetBase } from 'app/containers/Widget/types'
  66. import {
  67. ControlPanelLayoutTypes,
  68. ControlPanelTypes
  69. } from 'app/components/Control/constants'
  70. import { OnGetControlOptions } from 'app/components/Control/types'
  71. import styles from '../Dashboard.less'
  72. import utilStyles from 'app/assets/less/util.less'
  73. import EnhancerPanel from 'components/DataDrill/EnhancerPanel'
  74. interface IDashboardItemProps {
  75. itemId: number
  76. alias?: string
  77. widget: IWidgetFormed
  78. widgets: IWidgetFormed[]
  79. formedViews: IFormedViews | IShareFormedViews
  80. view?: Partial<IFormedView>
  81. isTrigger?: boolean
  82. datasource: any
  83. loading: boolean
  84. polling: boolean
  85. interacting: boolean
  86. frequency: number
  87. shareToken: string
  88. shareLoading?: boolean
  89. downloadCsvLoading: boolean
  90. rendered?: boolean
  91. renderType: RenderType
  92. selectedItems: number[]
  93. currentProject?: IProject
  94. queryConditions: IQueryConditions
  95. container?: string
  96. errorMessage: string
  97. onSelectDrillHistory?: (history?: any, item?: number, itemId?: number) => void
  98. onLoadData: ILoadData
  99. onShowEdit?: (
  100. itemId: number
  101. ) => (e: React.MouseEvent<HTMLSpanElement>) => void
  102. onShowDrillEdit?: (
  103. itemId: number
  104. ) => (e: React.MouseEvent<HTMLSpanElement>) => void
  105. onDeleteDashboardItem?: (itemId: number) => () => void
  106. onResizeDashboardItem: (itemId: number) => void
  107. onRenderChartError: (itemId: number, error: Error) => void
  108. onOpenSharePanel?: (
  109. id: number,
  110. type: TShareVizsType,
  111. title: string,
  112. itemId?: number
  113. ) => void
  114. onDownloadCsv: (itemId: number) => void
  115. onTurnOffInteract: (itemId: number) => void
  116. onShowFullScreen: (itemId: number) => void
  117. onCheckTableInteract: (itemId: number) => boolean
  118. onDoTableInteract: (itemId: number, triggerData: object) => void
  119. onEditWidget?: (itemId: number, widgetId: number) => void
  120. onDrillData?: (e: object) => void
  121. onSelectChartsItems?: (
  122. itemId: number,
  123. renderType: string,
  124. selectedItems: number[]
  125. ) => void
  126. onGetControlOptions: OnGetControlOptions
  127. onControlSearch: (
  128. type: ControlPanelTypes,
  129. relatedItems: number[],
  130. formValues?: object,
  131. itemId?: number
  132. ) => void
  133. onMonitoredSyncDataAction?: () => void
  134. onMonitoredSearchDataAction?: () => void
  135. }
  136. interface IDashboardItemStates {
  137. controlPanelVisible: boolean
  138. queryVariables: IQueryVariableMap
  139. model: IViewModel
  140. isDrilling: boolean
  141. dataDrillPanelPosition: boolean | object
  142. whichDataDrillBrushed: boolean | object[]
  143. sourceDataOfBrushed: object[]
  144. sourceDataGroup: string[]
  145. // isShowDrillPanel: boolean
  146. widgetProps: IWidgetConfig
  147. cacheWidgetProps: IWidgetConfig
  148. cacheWidgetId: boolean | number
  149. }
  150. export class DashboardItem extends React.PureComponent<
  151. IDashboardItemProps,
  152. IDashboardItemStates
  153. > {
  154. constructor(props) {
  155. super(props)
  156. this.state = {
  157. controlPanelVisible: false,
  158. queryVariables: {},
  159. model: null,
  160. isDrilling: true,
  161. dataDrillPanelPosition: false,
  162. whichDataDrillBrushed: false,
  163. sourceDataOfBrushed: [],
  164. cacheWidgetProps: null,
  165. widgetProps: null,
  166. cacheWidgetId: false,
  167. sourceDataGroup: []
  168. }
  169. }
  170. public static defaultProps = {
  171. onShowEdit: () => void 0,
  172. onShowDrillEdit: () => void 0,
  173. onDeleteDashboardItem: () => void 0
  174. }
  175. private pollingTimer: number
  176. private container: HTMLDivElement = null
  177. public componentWillMount() {
  178. const { itemId, widget, view, onLoadData, container } = this.props
  179. const { cacheWidgetProps, cacheWidgetId } = this.state
  180. if (container === 'share') {
  181. if (widget.config.autoLoadData) {
  182. onLoadData('clear', itemId)
  183. }
  184. this.initPolling(this.props)
  185. }
  186. this.setState({
  187. model: view.model
  188. })
  189. if (!cacheWidgetProps) {
  190. this.setState({
  191. widgetProps: { ...widget.config },
  192. cacheWidgetProps: { ...widget.config },
  193. cacheWidgetId: widget.id
  194. })
  195. }
  196. }
  197. public componentWillReceiveProps(nextProps: IDashboardItemProps) {
  198. const { widget, queryConditions } = this.props
  199. let { model } = this.state
  200. if (nextProps.widget !== widget) {
  201. model = nextProps.view.model
  202. this.setState({
  203. model
  204. })
  205. }
  206. if (nextProps.queryConditions !== queryConditions) {
  207. const {
  208. variables,
  209. linkageVariables,
  210. globalVariables
  211. } = nextProps.queryConditions
  212. this.setState({
  213. queryVariables: [
  214. ...variables,
  215. ...linkageVariables,
  216. ...globalVariables
  217. ].reduce((obj, { name, value }) => {
  218. obj[`$${name}$`] = value
  219. return obj
  220. }, {})
  221. })
  222. }
  223. }
  224. public componentWillUpdate(
  225. nextProps: IDashboardItemProps,
  226. nextState: IDashboardItemStates
  227. ) {
  228. const {
  229. itemId,
  230. widget,
  231. polling,
  232. frequency,
  233. rendered,
  234. container,
  235. onLoadData
  236. } = nextProps
  237. if (!container) {
  238. if (!this.props.rendered && rendered) {
  239. // clear
  240. if (widget.config.autoLoadData) {
  241. onLoadData('clear', itemId)
  242. }
  243. this.initPolling(this.props)
  244. }
  245. }
  246. if (polling !== this.props.polling || frequency !== this.props.frequency) {
  247. this.initPolling(nextProps)
  248. }
  249. }
  250. public componentDidUpdate(prevProps, prevState) {
  251. if (prevState.controlPanelVisible !== this.state.controlPanelVisible) {
  252. const { itemId, onResizeDashboardItem } = this.props
  253. onResizeDashboardItem(itemId)
  254. }
  255. }
  256. public componentWillUnmount() {
  257. clearInterval(this.pollingTimer)
  258. }
  259. private initPolling = (props: IDashboardItemProps) => {
  260. const { polling, frequency, itemId, onLoadData } = props
  261. clearInterval(this.pollingTimer)
  262. if (polling) {
  263. this.pollingTimer = window.setInterval(() => {
  264. onLoadData('refresh', itemId)
  265. }, Number(frequency) * 1000)
  266. }
  267. }
  268. private syncData = () => {
  269. const { itemId, onLoadData, onMonitoredSyncDataAction } = this.props
  270. onLoadData('flush', itemId)
  271. if (onMonitoredSyncDataAction) {
  272. onMonitoredSyncDataAction()
  273. }
  274. }
  275. private toggleControlPanel = () => {
  276. this.setState({
  277. controlPanelVisible: !this.state.controlPanelVisible
  278. })
  279. }
  280. private onFullScreen = () => {
  281. const { itemId, onShowFullScreen } = this.props
  282. onShowFullScreen(itemId)
  283. }
  284. private downloadCsv = () => {
  285. const { itemId, onDownloadCsv } = this.props
  286. onDownloadCsv(itemId)
  287. }
  288. private openSharePanel = () => {
  289. const { itemId, widget, onOpenSharePanel } = this.props
  290. const { id, name } = widget
  291. onOpenSharePanel(id, 'widget', name, itemId)
  292. }
  293. private checkTableInteract = () => {
  294. const { itemId, onCheckTableInteract } = this.props
  295. return onCheckTableInteract(itemId)
  296. }
  297. private doInteract = (triggerData) => {
  298. const { itemId, onDoTableInteract } = this.props
  299. onDoTableInteract(itemId, triggerData)
  300. }
  301. private paginationChange = (pageNo: number, pageSize: number, orders) => {
  302. const { itemId, queryConditions, onLoadData } = this.props
  303. const { drillHistory } = queryConditions
  304. const pagination = {
  305. ...queryConditions.pagination,
  306. pageNo,
  307. pageSize
  308. }
  309. if (drillHistory && drillHistory.length) {
  310. const drillStatus = drillHistory[drillHistory.length - 1]
  311. onLoadData('clear', itemId, { pagination, orders, drillStatus })
  312. } else {
  313. onLoadData('clear', itemId, { pagination, orders })
  314. }
  315. }
  316. private turnOffInteract = () => {
  317. const { onTurnOffInteract, itemId } = this.props
  318. onTurnOffInteract(itemId)
  319. }
  320. private toWorkbench = () => {
  321. const { itemId, widget } = this.props
  322. this.props.onEditWidget(itemId, widget.id)
  323. }
  324. private getDataDrillDetail = (position) => {
  325. if (position && position.length) {
  326. try {
  327. const ps = JSON.parse(position)
  328. const { range, brushed, sourceData, sourceGroup } = ps
  329. const dataDrillPanelPosition = void 0
  330. const sourceDataOfBrushed =
  331. sourceData && sourceData.length ? sourceData : []
  332. const whichDataDrillBrushed = brushed && brushed.length ? brushed : []
  333. const sourceDataGroup =
  334. sourceGroup && sourceGroup.length ? sourceGroup : []
  335. this.setState({
  336. dataDrillPanelPosition,
  337. whichDataDrillBrushed,
  338. sourceDataOfBrushed,
  339. sourceDataGroup
  340. })
  341. } catch (error) {
  342. throw error
  343. }
  344. }
  345. }
  346. private drillDataHistory = (history, item: number, itemId) => {
  347. const { onSelectDrillHistory, queryConditions } = this.props
  348. const { widgetProps, cacheWidgetProps } = this.state
  349. const { drillHistory } = queryConditions
  350. if (onSelectDrillHistory) {
  351. if (item === -1 && !history) {
  352. this.setState({ widgetProps: cacheWidgetProps })
  353. } else {
  354. const { cols, rows } = drillHistory[item]
  355. this.setState({ widgetProps: { ...widgetProps, cols, rows } })
  356. }
  357. onSelectDrillHistory(history, item, itemId)
  358. }
  359. }
  360. private receiveWidgetId() {
  361. const { widget } = this.props
  362. operationWidgetProps.receive(widget.id)
  363. }
  364. private isHasDrillHistory(): boolean {
  365. const { queryConditions } = this.props
  366. const { drillHistory } = queryConditions
  367. return !!(drillHistory && drillHistory.length !== 0)
  368. }
  369. private getLastDrillHistory() {
  370. const { queryConditions } = this.props
  371. const { drillHistory } = queryConditions
  372. return [...drillHistory].pop()
  373. }
  374. private sendDrillDetail(e) {
  375. const { itemId, widget, onDrillData } = this.props
  376. if (onDrillData) {
  377. onDrillData({
  378. ...e,
  379. itemId,
  380. widgetId: widget.id
  381. })
  382. }
  383. }
  384. private drillUp = (name: string) => {
  385. // a group name by drill
  386. this.receiveWidgetId()
  387. const { sourceDataOfBrushed } = this.state
  388. const isHasDrillHistory = this.isHasDrillHistory()
  389. let set
  390. if (isHasDrillHistory) {
  391. const getLastDrillHistory = this.getLastDrillHistory()
  392. set = strategiesOfDrillUpHasDrillHistory(
  393. getLastDrillHistory,
  394. this.state.widgetProps
  395. )({ name }, sourceDataOfBrushed)
  396. } else {
  397. set = strategiesOfDrillUpNullDrillHistory(
  398. operationWidgetProps,
  399. this.state.widgetProps
  400. )({ name }, sourceDataOfBrushed)
  401. }
  402. const { pivot, coustomTable } = set
  403. if (operationWidgetProps.isPivot()) {
  404. const {
  405. cols,
  406. rows,
  407. type,
  408. groups,
  409. filters,
  410. widgetProps,
  411. currentGroup
  412. } = pivot()
  413. this.setState({ widgetProps })
  414. this.sendDrillDetail({ cols, rows, type, groups, filters, currentGroup })
  415. return
  416. }
  417. if (this.isCoustomTable()) {
  418. const {
  419. cols,
  420. rows,
  421. type,
  422. groups,
  423. filters,
  424. widgetProps,
  425. currentGroup
  426. } = coustomTable()
  427. this.setState({ widgetProps })
  428. this.sendDrillDetail({ cols, rows, type, groups, filters, currentGroup })
  429. return
  430. }
  431. }
  432. private isCoustomTable() {
  433. return operationWidgetProps.isCoustomTable()
  434. }
  435. private drillDown = (name: string, dimensions?: WidgetDimension) => {
  436. const { sourceDataOfBrushed, sourceDataGroup } = this.state
  437. this.receiveWidgetId()
  438. const isHasDrillHistory = this.isHasDrillHistory()
  439. const dimetionAxis = operationWidgetProps.getDimetionAxis()
  440. let set
  441. let strategiteStream
  442. if (isHasDrillHistory) {
  443. const getLastDrillHistory = this.getLastDrillHistory()
  444. set = strategiesOfDrillDownHasDrillHistory(
  445. getLastDrillHistory,
  446. this.state.widgetProps
  447. )({ name }, sourceDataOfBrushed, sourceDataGroup)
  448. } else {
  449. set = strategiesOfDrillDownNullDrillHistory(
  450. operationWidgetProps,
  451. this.state.widgetProps
  452. )({ name }, sourceDataOfBrushed, sourceDataGroup)
  453. }
  454. const {
  455. dimetionAxisCol,
  456. dimetionAxisRow,
  457. pivotCol,
  458. pivotRow,
  459. coustomTable,
  460. defaultScenes
  461. } = set
  462. if (dimensions && dimensions.length) {
  463. if (dimensions === WidgetDimension.COL) {
  464. strategiteStream = pivotCol()
  465. } else if (dimensions === WidgetDimension.ROW) {
  466. strategiteStream = pivotRow()
  467. }
  468. } else if (operationWidgetProps.isCoustomTable()) {
  469. strategiteStream = coustomTable()
  470. } else if (dimetionAxis && dimetionAxis.length) {
  471. if (dimetionAxis === WidgetDimension.ROW) {
  472. strategiteStream = dimetionAxisRow()
  473. } else if (dimetionAxis === WidgetDimension.COL) {
  474. strategiteStream = dimetionAxisCol()
  475. }
  476. } else {
  477. strategiteStream = defaultScenes()
  478. }
  479. const {
  480. cols,
  481. rows,
  482. type,
  483. groups,
  484. filters,
  485. widgetProps,
  486. currentGroup
  487. } = strategiteStream
  488. this.sendDrillDetail({ cols, rows, type, groups, filters, currentGroup })
  489. this.setState({ widgetProps })
  490. }
  491. private selectChartsItems = (selectedItems) => {
  492. const { onSelectChartsItems, itemId } = this.props
  493. if (onSelectChartsItems) {
  494. onSelectChartsItems(itemId, 'select', selectedItems)
  495. }
  496. }
  497. private renderChartError = (error: Error) => {
  498. const { itemId, onRenderChartError } = this.props
  499. onRenderChartError(itemId, error)
  500. }
  501. public render() {
  502. const {
  503. alias,
  504. itemId,
  505. widget,
  506. formedViews,
  507. datasource,
  508. loading,
  509. interacting,
  510. shareToken,
  511. shareLoading,
  512. downloadCsvLoading,
  513. renderType,
  514. currentProject,
  515. queryConditions,
  516. onLoadData,
  517. onShowEdit,
  518. onShowDrillEdit,
  519. onSelectDrillHistory,
  520. onGetControlOptions,
  521. onControlSearch,
  522. onDeleteDashboardItem,
  523. onMonitoredSearchDataAction,
  524. container,
  525. errorMessage
  526. } = this.props
  527. const { drillHistory } = queryConditions
  528. const data = datasource.resultList
  529. const {
  530. controlPanelVisible,
  531. queryVariables,
  532. widgetProps,
  533. isDrilling,
  534. model,
  535. sourceDataGroup,
  536. sourceDataOfBrushed
  537. } = this.state
  538. let downloadButton
  539. let shareButton
  540. let widgetButton
  541. let dropdownMenu
  542. if (currentProject) {
  543. const DownloadButton = ShareDownloadPermission<IDownloadCsvProps>(
  544. currentProject,
  545. 'download'
  546. )(DownloadCsv)
  547. downloadButton = (
  548. <Tooltip title="下载数据">
  549. <DownloadButton
  550. shareLoading={shareLoading}
  551. downloadCsvLoading={downloadCsvLoading}
  552. onDownloadCsv={this.downloadCsv}
  553. />
  554. </Tooltip>
  555. )
  556. const ShareButton = ShareDownloadPermission<IconProps>(
  557. currentProject,
  558. 'share'
  559. )(Icon)
  560. shareButton = (
  561. <Tooltip title="分享">
  562. <ShareButton type="share-alt" onClick={this.openSharePanel} />
  563. </Tooltip>
  564. )
  565. const EditButton = ModulePermission<
  566. React.DetailedHTMLProps<
  567. React.HTMLAttributes<HTMLSpanElement>,
  568. HTMLSpanElement
  569. >
  570. >(
  571. currentProject,
  572. 'viz',
  573. false
  574. )(Span)
  575. widgetButton = (
  576. <Tooltip title="编辑">
  577. {/* <i className="iconfont icon-edit-2" onClick={this.toWorkbench} /> */}
  578. <i>
  579. <EditButton
  580. className="iconfont icon-edit-2"
  581. onClick={this.toWorkbench}
  582. />
  583. </i>
  584. </Tooltip>
  585. )
  586. }
  587. if (container === 'share') {
  588. downloadButton = (
  589. <Tooltip title="下载数据">
  590. <DownloadCsv
  591. shareLoading={shareLoading}
  592. downloadCsvLoading={downloadCsvLoading}
  593. onDownloadCsv={this.downloadCsv}
  594. />
  595. </Tooltip>
  596. )
  597. } else {
  598. const InfoButton = ModulePermission<
  599. React.DetailedHTMLProps<
  600. React.HTMLAttributes<HTMLSpanElement>,
  601. HTMLSpanElement
  602. >
  603. >(
  604. currentProject,
  605. 'viz',
  606. false
  607. )(Span)
  608. const DeleteButton = ModulePermission<
  609. React.DetailedHTMLProps<
  610. React.HTMLAttributes<HTMLSpanElement>,
  611. HTMLSpanElement
  612. >
  613. >(
  614. currentProject,
  615. 'viz',
  616. true
  617. )(Span)
  618. const menu = (
  619. <Menu>
  620. <Menu.Item className={styles.menuItem}>
  621. <InfoButton
  622. className={styles.menuText}
  623. onClick={onShowEdit(itemId)}
  624. >
  625. 基本信息
  626. </InfoButton>
  627. </Menu.Item>
  628. <Menu.Item className={styles.menuItem}>
  629. <Popconfirm
  630. title="确定删除?"
  631. placement="bottom"
  632. onConfirm={onDeleteDashboardItem(itemId)}
  633. >
  634. <DeleteButton className={styles.menuText}>删除</DeleteButton>
  635. </Popconfirm>
  636. </Menu.Item>
  637. </Menu>
  638. )
  639. dropdownMenu = (
  640. <Dropdown overlay={menu} placement="bottomRight" trigger={['click']}>
  641. <Icon type="ellipsis" />
  642. </Dropdown>
  643. )
  644. }
  645. const controlToggle = !!widget.config.controls.length && (
  646. <Tooltip title="控制器面板">
  647. <Icon
  648. type="control"
  649. className={classnames({
  650. [styles.activated]: controlPanelVisible
  651. })}
  652. onClick={this.toggleControlPanel}
  653. />
  654. </Tooltip>
  655. )
  656. const loadingIcon = loading && <Icon type="loading" />
  657. const descIcon = widget.description && (
  658. <Popover
  659. title="备注"
  660. content={widget.description}
  661. placement="bottom"
  662. overlayClassName={styles.widgetInfoContent}
  663. >
  664. <Icon type="info-circle" />
  665. </Popover>
  666. )
  667. const errorIcon = errorMessage && (
  668. <Tooltip
  669. title={
  670. <>
  671. <p>错误信息:</p>
  672. <p>{errorMessage}</p>
  673. </>
  674. }
  675. placement="bottomLeft"
  676. overlayClassName={styles.widgetInfoContent}
  677. >
  678. <Icon className={styles.error} type="warning" />
  679. </Tooltip>
  680. )
  681. const gridItemClass = classnames({
  682. [styles.gridItem]: true,
  683. [styles.interact]: interacting
  684. })
  685. const isDrillableChart = DrillableChart.some(
  686. (drillable) => drillable === widget.config.selectedChart
  687. )
  688. const drillInteractIcon =
  689. this.props.isTrigger === false ? (
  690. isDrillableChart ? (
  691. <Tooltip title="可钻取">
  692. <i className="iconfont icon-xiazuan" />
  693. </Tooltip>
  694. ) : (
  695. void 0
  696. )
  697. ) : (
  698. <Tooltip title="可联动">
  699. <i className="iconfont icon-liandong1" />
  700. </Tooltip>
  701. )
  702. const dataDrillPanel = (
  703. <EnhancerPanel
  704. currentData={data}
  705. widgetConfig={widget.config}
  706. onDataDrillDown={this.drillDown}
  707. onDataDrillUp={this.drillUp}
  708. drillHistory={drillHistory}
  709. isSelectedfilter={sourceDataOfBrushed}
  710. isSelectedGroup={sourceDataGroup}
  711. chartStyle={widget.config.selectedChart}
  712. />
  713. )
  714. const dataDrillHistoryClass = classnames({
  715. [styles.dataDrillHistory]: true,
  716. [utilStyles.hide]: !(drillHistory && drillHistory.length > 0)
  717. })
  718. const dataDrillHistory = (
  719. <div className={dataDrillHistoryClass}>
  720. <DataDrillHistory
  721. itemId={itemId}
  722. widgetId={widget.id}
  723. drillHistory={drillHistory}
  724. onSelectDrillHistory={this.drillDataHistory}
  725. />
  726. </div>
  727. )
  728. const { selectedChart, cols, rows, metrics } = widget.config
  729. const hasDataConfig = !!(cols.length || rows.length || metrics.length)
  730. const empty = (
  731. <DashboardItemMask.Empty
  732. loading={loading}
  733. chartType={selectedChart}
  734. empty={!data.length}
  735. hasDataConfig={hasDataConfig}
  736. />
  737. )
  738. const widgetName = alias || widget.name
  739. return (
  740. <div className={gridItemClass} ref={(f) => (this.container = f)}>
  741. <div className={styles.header}>
  742. <div className={styles.title}>
  743. <h4>{widgetName}</h4>
  744. {descIcon}
  745. {errorIcon}
  746. {controlToggle}
  747. {loadingIcon}
  748. {}
  749. </div>
  750. <div className={styles.tools}>
  751. <Tooltip title="同步数据">
  752. {!loading && <Icon type="reload" onClick={this.syncData} />}
  753. </Tooltip>
  754. {widgetButton}
  755. <Tooltip title="全屏">
  756. <Icon
  757. type="fullscreen"
  758. onClick={this.onFullScreen}
  759. className={styles.fullScreen}
  760. />
  761. </Tooltip>
  762. {shareButton}
  763. {downloadButton}
  764. {dropdownMenu}
  765. </div>
  766. </div>
  767. <div className={styles.trigger}>{drillInteractIcon}</div>
  768. <div className={styles.offInteract} onClick={this.turnOffInteract}>
  769. <i className="iconfont icon-unlink" />
  770. <h3>点击取消联动</h3>
  771. </div>
  772. <div
  773. className={classnames({ [utilStyles.hide]: !controlPanelVisible })}
  774. >
  775. <LocalControlPanel
  776. formedViews={formedViews}
  777. itemId={itemId}
  778. widget={widget}
  779. layoutType={ControlPanelLayoutTypes.DashboardItem}
  780. onGetOptions={onGetControlOptions}
  781. onSearch={onControlSearch}
  782. onMonitoredSearchDataAction={onMonitoredSearchDataAction}
  783. />
  784. </div>
  785. <Dropdown
  786. overlay={dataDrillPanel}
  787. placement="topCenter"
  788. trigger={['contextMenu']}
  789. >
  790. <div className={styles.block}>
  791. <Widget
  792. {...widgetProps}
  793. renderType={loading ? 'loading' : renderType}
  794. data={data}
  795. interacting={this.props.interacting}
  796. queryVariables={queryVariables}
  797. pagination={queryConditions.pagination}
  798. empty={empty}
  799. model={model}
  800. onCheckTableInteract={this.checkTableInteract}
  801. onDoInteract={this.doInteract}
  802. onPaginationChange={this.paginationChange}
  803. getDataDrillDetail={this.getDataDrillDetail}
  804. isDrilling={this.state.isDrilling}
  805. whichDataDrillBrushed={this.state.whichDataDrillBrushed}
  806. onSelectChartsItems={this.selectChartsItems}
  807. selectedItems={this.props.selectedItems}
  808. // onHideDrillPanel={this.onHideDrillPanel}
  809. onError={this.renderChartError}
  810. />
  811. {dataDrillHistory}
  812. </div>
  813. </Dropdown>
  814. </div>
  815. )
  816. }
  817. }
  818. function Span(
  819. props: React.DetailedHTMLProps<
  820. React.HTMLAttributes<HTMLSpanElement>,
  821. HTMLSpanElement
  822. >
  823. ) {
  824. return <span {...props}>{props.children}</span>
  825. }
  826. export default DashboardItem