index.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809
  1. import React, { Suspense } from 'react'
  2. import { compose } from 'redux'
  3. import { connect } from 'react-redux'
  4. import { createStructuredSelector } from 'reselect'
  5. import injectReducer from 'utils/injectReducer'
  6. import injectSaga from 'utils/injectSaga'
  7. import reducer from 'containers/Widget/reducer'
  8. import viewReducer from 'containers/View/reducer'
  9. import saga from 'containers/Widget/sagas'
  10. import viewSaga from 'containers/View/sagas'
  11. import controlReducer from 'containers/ControlPanel/reducer'
  12. import { hideNavigator } from 'containers/App/actions'
  13. import { ViewActions } from 'containers/View/actions'
  14. const {
  15. loadViews,
  16. loadViewsDetail,
  17. loadViewData,
  18. loadColumnDistinctValue
  19. } = ViewActions
  20. import { WidgetActions } from 'containers/Widget/actions'
  21. import {
  22. makeSelectCurrentWidget,
  23. makeSelectLoading,
  24. makeSelectDataLoading
  25. } from 'containers/Widget/selectors'
  26. import {
  27. makeSelectViews,
  28. makeSelectFormedViews
  29. } from 'containers/View/selectors'
  30. import { RouteComponentWithParams } from 'utils/types'
  31. import { IViewBase, IFormedViews, IView } from 'containers/View/types'
  32. import OperatingPanel from './OperatingPanel'
  33. import Widget, { IWidgetProps } from '../Widget'
  34. import { IDataRequestBody } from 'app/containers/Dashboard/types'
  35. import EditorHeader from 'components/EditorHeader'
  36. import WorkbenchSettingForm from './WorkbenchSettingForm'
  37. import DashboardItemMask, {
  38. IDashboardItemMaskProps
  39. } from 'containers/Dashboard/components/DashboardItemMask'
  40. import { DEFAULT_SPLITER, DEFAULT_CACHE_EXPIRED } from 'app/globalConstants'
  41. import { getStyleConfig } from 'containers/Widget/components/util'
  42. import ChartTypes from '../../config/chart/ChartTypes'
  43. import { FieldSortTypes, fieldGroupedSort } from '../Config/Sort'
  44. import { message } from 'antd'
  45. import 'assets/less/resizer.less'
  46. import {
  47. IDistinctValueReqeustParams,
  48. IControl
  49. } from 'app/components/Control/types'
  50. import { IReference } from './Reference/types'
  51. import { IWorkbenchSettings, WorkbenchQueryMode } from './types'
  52. import { IWidgetFormed, IWidgetRaw } from '../../types'
  53. import { ControlQueryMode } from 'app/components/Control/constants'
  54. const styles = require('./Workbench.less')
  55. interface IWorkbenchProps {
  56. views: IViewBase[]
  57. formedViews: IFormedViews
  58. currentWidget: IWidgetFormed
  59. loading: boolean
  60. dataLoading: boolean
  61. onHideNavigator: () => void
  62. onLoadViews: (projectId: number, resolve?: () => void) => void
  63. onLoadViewDetail: (
  64. viewIds: number[],
  65. resolve?: (views: IView[]) => void
  66. ) => void
  67. onLoadWidgetDetail: (id: number) => void
  68. onLoadViewData: (
  69. viewId: number,
  70. requestParams: IDataRequestBody,
  71. resolve: (data) => void,
  72. reject: (error) => void
  73. ) => void
  74. onAddWidget: (widget: Omit<IWidgetRaw, 'id'>, resolve: () => void) => void
  75. onEditWidget: (widget: IWidgetRaw, resolve: () => void) => void
  76. onLoadColumnDistinctValue: (
  77. paramsByViewId: {
  78. [viewId: string]: Omit<IDistinctValueReqeustParams, 'cache' | 'expired'>
  79. },
  80. callback: (options?: object[]) => void
  81. ) => void
  82. onClearCurrentWidget: () => void
  83. onExecuteComputed: (sql: string) => void
  84. }
  85. interface IWorkbenchStates {
  86. id: number
  87. name: string
  88. description: string
  89. selectedViewId: number
  90. controls: any[]
  91. references: IReference[]
  92. computed: any[]
  93. autoLoadData: boolean
  94. controlQueryMode: ControlQueryMode
  95. limit: number
  96. cache: boolean
  97. expired: number
  98. splitSize: number
  99. originalWidgetProps: IWidgetProps
  100. originalComputed: any[]
  101. widgetProps: IWidgetProps
  102. settingFormVisible: boolean
  103. settings: IWorkbenchSettings
  104. }
  105. const SplitPane = React.lazy(() => import('react-split-pane'))
  106. export class Workbench extends React.Component<
  107. IWorkbenchProps & RouteComponentWithParams,
  108. IWorkbenchStates
  109. > {
  110. private operatingPanel: OperatingPanel = null
  111. private defaultSplitSize = 440
  112. private maxSplitSize = this.defaultSplitSize * 1.5
  113. constructor(props) {
  114. super(props)
  115. const splitSize =
  116. +localStorage.getItem('workbenchSplitSize') || this.defaultSplitSize
  117. this.state = {
  118. id: 0,
  119. name: '',
  120. description: '',
  121. selectedViewId: null,
  122. controls: [],
  123. references: [],
  124. computed: [],
  125. originalComputed: [],
  126. cache: false,
  127. autoLoadData: true,
  128. controlQueryMode: ControlQueryMode.Immediately,
  129. limit: null,
  130. expired: DEFAULT_CACHE_EXPIRED,
  131. splitSize,
  132. originalWidgetProps: null,
  133. widgetProps: {
  134. data: [],
  135. pagination: {
  136. pageNo: 0,
  137. pageSize: 0,
  138. totalCount: 0,
  139. withPaging: false
  140. },
  141. cols: [],
  142. rows: [],
  143. metrics: [],
  144. secondaryMetrics: [],
  145. filters: [],
  146. chartStyles: getStyleConfig({}),
  147. selectedChart: ChartTypes.Table,
  148. orders: [],
  149. mode: 'pivot',
  150. model: {},
  151. onPaginationChange: this.paginationChange
  152. },
  153. settingFormVisible: false,
  154. settings: this.initSettings()
  155. }
  156. }
  157. private placeholder = {
  158. name: '请输入可视化组件名称',
  159. description: '请输入描述…'
  160. }
  161. public componentWillMount() {
  162. const { match, onLoadWidgetDetail } = this.props
  163. const { widgetId } = match.params
  164. this.loadViews(() => {
  165. if (widgetId !== 'add' && !Number.isNaN(Number(widgetId))) {
  166. onLoadWidgetDetail(+widgetId)
  167. }
  168. })
  169. }
  170. public componentDidMount() {
  171. this.props.onHideNavigator()
  172. }
  173. public componentWillReceiveProps(nextProps: IWorkbenchProps) {
  174. const { currentWidget } = nextProps
  175. if (currentWidget && currentWidget !== this.props.currentWidget) {
  176. const { id, name, description, viewId, config } = currentWidget
  177. const {
  178. controls,
  179. references,
  180. limit,
  181. cache,
  182. expired,
  183. computed,
  184. autoLoadData,
  185. queryMode,
  186. ...rest
  187. } = config
  188. this.setState({
  189. id,
  190. name,
  191. description,
  192. controls,
  193. references,
  194. cache,
  195. autoLoadData,
  196. controlQueryMode: queryMode,
  197. limit,
  198. expired,
  199. selectedViewId: viewId,
  200. originalWidgetProps: { ...rest },
  201. widgetProps: { ...rest },
  202. originalComputed: computed
  203. })
  204. }
  205. }
  206. public componentWillUnmount() {
  207. this.props.onClearCurrentWidget()
  208. }
  209. private initSettings = (): IWorkbenchSettings => {
  210. let workbenchSettings = {
  211. queryMode: WorkbenchQueryMode.Immediately,
  212. multiDrag: false
  213. }
  214. try {
  215. const loginUser = JSON.parse(localStorage.getItem('loginUser'))
  216. const currentUserWorkbenchSetting = JSON.parse(
  217. localStorage.getItem(`${loginUser.id}_workbench_settings`)
  218. )
  219. if (currentUserWorkbenchSetting) {
  220. workbenchSettings = currentUserWorkbenchSetting
  221. }
  222. } catch (err) {
  223. throw new Error(err)
  224. }
  225. return workbenchSettings
  226. }
  227. private loadViews = (callback?: () => void) => {
  228. const { match, onLoadViews } = this.props
  229. const { projectId } = match.params
  230. onLoadViews(Number(projectId), () => {
  231. if (callback) {
  232. callback()
  233. }
  234. })
  235. }
  236. private changeName = (e) => {
  237. this.setState({
  238. name: e.currentTarget.value
  239. })
  240. }
  241. private changeDesc = (e) => {
  242. this.setState({
  243. description: e.currentTarget.value
  244. })
  245. }
  246. private viewSelect = (viewId: number) => {
  247. const { formedViews } = this.props
  248. const nextState = {
  249. selectedViewId: viewId,
  250. controls: [],
  251. controlQueryMode: ControlQueryMode.Immediately,
  252. references: [],
  253. cache: false,
  254. expired: DEFAULT_CACHE_EXPIRED
  255. }
  256. if (formedViews[viewId]) {
  257. this.setState(nextState)
  258. } else {
  259. this.props.onLoadViewDetail([viewId], () => {
  260. this.setState(nextState)
  261. })
  262. }
  263. }
  264. private setControls = (controls: IControl[], queryMode: ControlQueryMode) => {
  265. this.setState({
  266. controls,
  267. controlQueryMode: queryMode
  268. })
  269. }
  270. private setReferences = (references: IReference[]) => {
  271. this.setState({
  272. references,
  273. widgetProps: {
  274. ...this.state.widgetProps,
  275. references
  276. }
  277. })
  278. }
  279. private deleteComputed = (computeField) => {
  280. const { from } = computeField
  281. const { match, onEditWidget } = this.props
  282. const {
  283. id,
  284. name,
  285. description,
  286. selectedViewId,
  287. controls,
  288. references,
  289. cache,
  290. autoLoadData,
  291. limit,
  292. expired,
  293. widgetProps,
  294. computed,
  295. originalWidgetProps,
  296. originalComputed
  297. } = this.state
  298. if (from === 'originalComputed') {
  299. this.setState(
  300. {
  301. originalComputed: originalComputed.filter(
  302. (oc) => oc.id !== computeField.id
  303. )
  304. },
  305. () => {
  306. const { originalComputed, computed } = this.state
  307. const widget = {
  308. name,
  309. description,
  310. type: 1,
  311. viewId: selectedViewId,
  312. projectId: Number(match.params.projectId),
  313. config: JSON.stringify({
  314. ...widgetProps,
  315. controls,
  316. references,
  317. computed:
  318. originalComputed && originalComputed
  319. ? [...computed, ...originalComputed]
  320. : [...computed],
  321. limit,
  322. cache,
  323. autoLoadData,
  324. expired,
  325. data: []
  326. }),
  327. publish: true
  328. }
  329. if (id) {
  330. onEditWidget({ ...widget, id }, () => void 0)
  331. }
  332. }
  333. )
  334. } else if (from === 'computed') {
  335. this.setState(
  336. {
  337. computed: computed.filter((cm) => cm.id !== computeField.id)
  338. },
  339. () => {
  340. const { originalComputed, computed } = this.state
  341. const widget = {
  342. name,
  343. description,
  344. type: 1,
  345. viewId: selectedViewId,
  346. projectId: Number(match.params.projectId),
  347. config: JSON.stringify({
  348. ...widgetProps,
  349. controls,
  350. references,
  351. computed:
  352. originalComputed && originalComputed
  353. ? [...computed, ...originalComputed]
  354. : [...computed],
  355. limit,
  356. cache,
  357. autoLoadData,
  358. expired,
  359. data: []
  360. }),
  361. publish: true
  362. }
  363. if (id) {
  364. onEditWidget({ ...widget, id }, () => void 0)
  365. }
  366. }
  367. )
  368. }
  369. }
  370. private setComputed = (computeField) => {
  371. const { computed, originalComputed } = this.state
  372. const { from, sqlExpression } = computeField
  373. // todo 首先做sql合法校验; sqlExpression
  374. let isEdit = void 0
  375. let newComputed = null
  376. if (from === 'originalComputed') {
  377. isEdit = originalComputed
  378. ? originalComputed.some((cm) => cm.id === computeField.id)
  379. : false
  380. newComputed = isEdit
  381. ? originalComputed.map((cm) => {
  382. if (cm.id === computeField.id) {
  383. return computeField
  384. } else {
  385. return cm
  386. }
  387. })
  388. : originalComputed.concat(computeField)
  389. this.setState({
  390. originalComputed: newComputed
  391. })
  392. } else if (from === 'computed') {
  393. isEdit = computed.some((cm) => cm.id === computeField.id)
  394. newComputed = isEdit
  395. ? computed.map((cm) => {
  396. if (cm.id === computeField.id) {
  397. return computeField
  398. } else {
  399. return cm
  400. }
  401. })
  402. : computed.concat(computeField)
  403. this.setState({
  404. computed: newComputed
  405. })
  406. } else {
  407. this.setState({
  408. computed: computed.concat(computeField)
  409. })
  410. }
  411. }
  412. private limitChange = (value) => {
  413. this.setState({
  414. limit: value
  415. })
  416. }
  417. private cacheChange = (e) => {
  418. this.setState({
  419. cache: e.target.value
  420. })
  421. }
  422. private expiredChange = (value) => {
  423. this.setState({
  424. expired: value
  425. })
  426. }
  427. private setWidgetProps = (widgetProps: IWidgetProps) => {
  428. const { cols, rows } = widgetProps
  429. const data = [...(widgetProps.data || this.state.widgetProps.data)]
  430. const customOrders = cols
  431. .concat(rows)
  432. .filter(({ sort }) => sort && sort.sortType === FieldSortTypes.Custom)
  433. .map(({ name, sort }) => ({
  434. name,
  435. list: sort[FieldSortTypes.Custom].sortList
  436. }))
  437. fieldGroupedSort(data, customOrders)
  438. this.setState({
  439. widgetProps: {
  440. ...widgetProps,
  441. data,
  442. references: this.state.references
  443. }
  444. })
  445. }
  446. private saveWidget = () => {
  447. const { match, onAddWidget, onEditWidget } = this.props
  448. const {
  449. id,
  450. name,
  451. description,
  452. selectedViewId,
  453. controls,
  454. controlQueryMode,
  455. references,
  456. limit,
  457. cache,
  458. expired,
  459. widgetProps,
  460. computed,
  461. originalComputed,
  462. autoLoadData
  463. } = this.state
  464. if (!name.trim()) {
  465. message.error('Widget名称不能为空')
  466. return
  467. }
  468. if (!selectedViewId) {
  469. message.error('请选择一个View')
  470. return
  471. }
  472. const widget = {
  473. name,
  474. description,
  475. type: 1,
  476. viewId: selectedViewId,
  477. projectId: Number(match.params.projectId),
  478. config: JSON.stringify({
  479. ...widgetProps,
  480. controls,
  481. queryMode: controlQueryMode,
  482. references,
  483. computed:
  484. originalComputed && originalComputed
  485. ? [...computed, ...originalComputed]
  486. : [...computed],
  487. limit,
  488. cache,
  489. expired,
  490. autoLoadData,
  491. data: []
  492. }),
  493. publish: true
  494. }
  495. if (id) {
  496. onEditWidget({ ...widget, id }, () => {
  497. message.success('修改成功')
  498. const editSignDashboard = sessionStorage.getItem(
  499. 'editWidgetFromDashboard'
  500. )
  501. const editSignDisplay = sessionStorage.getItem('editWidgetFromDisplay')
  502. if (editSignDashboard) {
  503. sessionStorage.removeItem('editWidgetFromDashboard')
  504. const [
  505. projectId,
  506. portalId,
  507. dashboardId,
  508. itemId
  509. ] = editSignDashboard.split(DEFAULT_SPLITER)
  510. this.props.history.replace(
  511. `/project/${projectId}/portal/${portalId}/dashboard/${dashboardId}`
  512. )
  513. } else if (editSignDisplay) {
  514. sessionStorage.removeItem('editWidgetFromDisplay')
  515. const [projectId, displayId] = editSignDisplay.split(DEFAULT_SPLITER)
  516. this.props.history.replace(
  517. `/project/${projectId}/display/${displayId}`
  518. )
  519. } else {
  520. this.props.history.replace(
  521. `/project/${match.params.projectId}/widgets`
  522. )
  523. }
  524. })
  525. } else {
  526. onAddWidget(widget, () => {
  527. message.success('添加成功')
  528. this.props.history.replace(`/project/${match.params.projectId}/widgets`)
  529. })
  530. }
  531. }
  532. private cancel = () => {
  533. sessionStorage.removeItem('editWidgetFromDashboard')
  534. sessionStorage.removeItem('editWidgetFromDisplay')
  535. this.props.history.goBack()
  536. }
  537. private paginationChange = (pageNo: number, pageSize: number, orders) => {
  538. this.operatingPanel.flipPage(pageNo, pageSize, orders)
  539. }
  540. private chartStylesChange = (propPath: string[], value: string) => {
  541. const { widgetProps } = this.state
  542. const { chartStyles } = widgetProps
  543. const updatedChartStyles = { ...chartStyles }
  544. propPath.reduce((subObj, propName, idx) => {
  545. if (idx === propPath.length - 1) {
  546. subObj[propName] = value
  547. }
  548. return subObj[propName]
  549. }, updatedChartStyles)
  550. this.setWidgetProps({
  551. ...widgetProps,
  552. chartStyles: updatedChartStyles
  553. })
  554. }
  555. private saveSplitSize(newSize: number) {
  556. localStorage.setItem('workbenchSplitSize', newSize.toString())
  557. }
  558. private resizeChart = () => {
  559. this.setState({
  560. widgetProps: {
  561. ...this.state.widgetProps,
  562. renderType: 'resize'
  563. }
  564. })
  565. }
  566. private changeAutoLoadData = (e) => {
  567. this.setState({
  568. autoLoadData: e.target.value
  569. })
  570. }
  571. private openSettingForm = () => {
  572. this.setState({
  573. settingFormVisible: true
  574. })
  575. }
  576. private saveSettingForm = (values: IWorkbenchSettings) => {
  577. try {
  578. const loginUser = JSON.parse(localStorage.getItem('loginUser'))
  579. localStorage.setItem(
  580. `${loginUser.id}_workbench_settings`,
  581. JSON.stringify(values)
  582. )
  583. this.setState({
  584. settings: values
  585. })
  586. } catch (err) {
  587. throw new Error(err)
  588. }
  589. this.closeSettingForm()
  590. }
  591. private closeSettingForm = () => {
  592. this.setState({
  593. settingFormVisible: false
  594. })
  595. }
  596. public render() {
  597. const {
  598. views,
  599. formedViews,
  600. loading,
  601. dataLoading,
  602. onLoadViewData,
  603. onLoadColumnDistinctValue,
  604. onLoadViewDetail
  605. } = this.props
  606. const {
  607. name,
  608. description,
  609. selectedViewId,
  610. controls,
  611. controlQueryMode,
  612. references,
  613. limit,
  614. cache,
  615. autoLoadData,
  616. expired,
  617. computed,
  618. splitSize,
  619. originalWidgetProps,
  620. originalComputed,
  621. widgetProps,
  622. settingFormVisible,
  623. settings
  624. } = this.state
  625. const { queryMode: workbenchQueryMode, multiDrag } = settings
  626. const { selectedChart, cols, rows, metrics, data } = widgetProps
  627. const hasDataConfig = !!(cols.length || rows.length || metrics.length)
  628. const maskProps: IDashboardItemMaskProps = {
  629. loading: dataLoading,
  630. chartType: selectedChart,
  631. empty: !data.length,
  632. hasDataConfig
  633. }
  634. return (
  635. <div className={styles.workbench}>
  636. <EditorHeader
  637. currentType="workbench"
  638. className={styles.header}
  639. name={name}
  640. description={description}
  641. placeholder={this.placeholder}
  642. onNameChange={this.changeName}
  643. onDescriptionChange={this.changeDesc}
  644. onSave={this.saveWidget}
  645. onCancel={this.cancel}
  646. onSetting={this.openSettingForm}
  647. loading={loading}
  648. />
  649. <div className={styles.body}>
  650. <Suspense fallback={null}>
  651. <SplitPane
  652. split="vertical"
  653. defaultSize={splitSize}
  654. minSize={this.defaultSplitSize}
  655. maxSize={this.maxSplitSize}
  656. onChange={this.saveSplitSize}
  657. onDragFinished={this.resizeChart}
  658. >
  659. <OperatingPanel
  660. ref={(f) => (this.operatingPanel = f)}
  661. views={views}
  662. formedViews={formedViews}
  663. selectedViewId={selectedViewId}
  664. originalWidgetProps={originalWidgetProps}
  665. originalComputed={originalComputed}
  666. controls={controls}
  667. controlQueryMode={controlQueryMode}
  668. references={references}
  669. limit={limit}
  670. cache={cache}
  671. autoLoadData={autoLoadData}
  672. expired={expired}
  673. workbenchQueryMode={workbenchQueryMode}
  674. multiDrag={multiDrag}
  675. computed={computed}
  676. onViewSelect={this.viewSelect}
  677. onChangeAutoLoadData={this.changeAutoLoadData}
  678. onSetControls={this.setControls}
  679. onSetReferences={this.setReferences}
  680. onLimitChange={this.limitChange}
  681. onCacheChange={this.cacheChange}
  682. onExpiredChange={this.expiredChange}
  683. onSetWidgetProps={this.setWidgetProps}
  684. onSetComputed={this.setComputed}
  685. onDeleteComputed={this.deleteComputed}
  686. onLoadData={onLoadViewData}
  687. onLoadColumnDistinctValue={onLoadColumnDistinctValue}
  688. onLoadViews={this.loadViews}
  689. onLoadViewDetail={onLoadViewDetail}
  690. />
  691. <div className={styles.viewPanel}>
  692. <div className={styles.widgetBlock}>
  693. <Widget
  694. {...widgetProps}
  695. loading={<DashboardItemMask.Loading {...maskProps} />}
  696. empty={<DashboardItemMask.Empty {...maskProps} />}
  697. editing={true}
  698. onPaginationChange={this.paginationChange}
  699. onChartStylesChange={this.chartStylesChange}
  700. />
  701. </div>
  702. </div>
  703. </SplitPane>
  704. </Suspense>
  705. <WorkbenchSettingForm
  706. visible={settingFormVisible}
  707. settings={settings}
  708. onSave={this.saveSettingForm}
  709. onClose={this.closeSettingForm}
  710. />
  711. </div>
  712. </div>
  713. )
  714. }
  715. }
  716. const mapStateToProps = createStructuredSelector({
  717. views: makeSelectViews(),
  718. formedViews: makeSelectFormedViews(),
  719. currentWidget: makeSelectCurrentWidget(),
  720. loading: makeSelectLoading(),
  721. dataLoading: makeSelectDataLoading()
  722. })
  723. export function mapDispatchToProps(dispatch) {
  724. return {
  725. onHideNavigator: () => dispatch(hideNavigator()),
  726. onLoadViews: (projectId, resolve) =>
  727. dispatch(loadViews(projectId, resolve)),
  728. onLoadViewDetail: (viewIds, resolve) =>
  729. dispatch(loadViewsDetail(viewIds, resolve)),
  730. onLoadWidgetDetail: (id) => dispatch(WidgetActions.loadWidgetDetail(id)),
  731. onLoadViewData: (viewId, requestParams, resolve, reject) =>
  732. dispatch(loadViewData(viewId, requestParams, resolve, reject)),
  733. onAddWidget: (widget, resolve) =>
  734. dispatch(WidgetActions.addWidget(widget, resolve)),
  735. onEditWidget: (widget, resolve) =>
  736. dispatch(WidgetActions.editWidget(widget, resolve)),
  737. onLoadColumnDistinctValue: (
  738. paramsByViewId: {
  739. [viewId: string]: Omit<IDistinctValueReqeustParams, 'cache' | 'expired'>
  740. },
  741. callback: (options?: object[]) => void
  742. ) => dispatch(loadColumnDistinctValue(paramsByViewId, callback)),
  743. onClearCurrentWidget: () => dispatch(WidgetActions.clearCurrentWidget()),
  744. onExecuteComputed: (sql) => dispatch(WidgetActions.executeComputed(sql))
  745. }
  746. }
  747. const withConnect = connect<{}, {}>(mapStateToProps, mapDispatchToProps)
  748. const withReducerWidget = injectReducer({ key: 'widget', reducer })
  749. const withSagaWidget = injectSaga({ key: 'widget', saga })
  750. const withReducerView = injectReducer({ key: 'view', reducer: viewReducer })
  751. const withSagaView = injectSaga({ key: 'view', saga: viewSaga })
  752. const withControlReducer = injectReducer({
  753. key: 'control',
  754. reducer: controlReducer
  755. })
  756. export default compose(
  757. withReducerWidget,
  758. withReducerView,
  759. withControlReducer,
  760. withSagaView,
  761. withSagaWidget,
  762. withConnect
  763. )(Workbench)