| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 | /* * << * Davinci * == * Copyright (C) 2016 - 2017 EDP * == * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * *      http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * >> */import React from 'react'import classnames from 'classnames'import { Radio, Select, Button, Icon, message } from 'antd'const RadioGroup = Radio.Groupconst Option = Select.Optionimport { SQL_NUMBER_TYPES, SQL_DATE_TYPES } from 'app/globalConstants'const utilStyles = require('assets/less/util.less')const styles = require('../Dashboard.less')interface IDrillPathSettingProps {  widgets: any[]  views: any[]  itemId: number | boolean  selectedWidget: number[]  drillpathSetting: any[]  saveDrillPathSetting: (flag: any) => any  cancel: () => any}interface IDrillPathSettingStates {  screenWidth: number  value: number  pathNodes: IPathNode[]  pathOrFree: string}export interface IPathNode {  views: any[]  widget: string  enter: string  out: string  enterType: string  outType: string  nextRelation: string  enterErrorMessage: string  outErrorMessage: string}export class DrillPathSetting extends React.PureComponent<IDrillPathSettingProps, IDrillPathSettingStates> {  constructor (props) {    super(props)    this.state = {      screenWidth: 0,      value: 1,      pathNodes: [],      pathOrFree: 'free'    }  }  private getViewList = (widgetId) => {    const {views, widgets} = this.props    const view = views.find((view) => view.id === (widgets.find((widget) => widget.id === widgetId))['viewId'])    const { model } = view    const modelObj = JSON.parse(model)    const viewLists = []    Object.entries(modelObj).forEach(([key, m]) => {      m['name'] = key      viewLists.push(m)    })    return viewLists  }  private init = () => {    const {selectedWidget, drillpathSetting} = this.props    const viewLists = this.getViewList(selectedWidget[0])    let newPathNodes = void 0    if (drillpathSetting && drillpathSetting.length) {      newPathNodes = drillpathSetting      this.setState({        pathOrFree: 'path'      })    } else {      newPathNodes = [{        widget: `${selectedWidget[0]}`,        views: viewLists,        enter: '',        out: '',        enterType: '',        outType: '',        nextRelation: '=',        enterErrorMessage: '',        outErrorMessage: ''      }]    }    this.setState({      pathNodes: newPathNodes    })  }  public componentWillMount () {    this.init()  }  public componentWillReceiveProps (nextProps) {    window.addEventListener('resize', this.getScreenWidth, false)    const {selectedWidget, drillpathSetting} = this.props    const nextSelectedWidget = nextProps.selectedWidget    const nextDrillpathSetting = nextProps.drillpathSetting    if (selectedWidget[0] !== nextSelectedWidget [0]) {      const startPathObj = this.state.pathNodes[0]      startPathObj['widget'] = nextSelectedWidget[0]      const newPathNodes = [...this.state.pathNodes]      newPathNodes.splice(0, 1, startPathObj)      this.setState({        pathNodes: newPathNodes      })    }  }  public componentWillUnmount () {    window.removeEventListener('resize', this.getScreenWidth, false)  }  private getScreenWidth = () => {    this.setState({ screenWidth: document.documentElement.clientWidth })  }  private  onChange = (e) => {    this.setState({      pathOrFree: e.target.value    })  }  private generateFilterOperatorOptions = (type) => {    const operators = [      ['=', 'like', '>', '<', '>=', '<=', '!='],      ['=', '>', '<', '>=', '<=', '!=']    ]    const stringOptions = operators[0].slice().map((o) => (      <Option key={o} value={o}>{o}</Option>    ))    const numbersAndDateOptions = operators[1].slice().map((o) => (      <Option key={o} value={o}>{o}</Option>    ))    if (SQL_NUMBER_TYPES.indexOf(type) >= 0 || SQL_DATE_TYPES.indexOf(type) >= 0) {      return numbersAndDateOptions    } else {      return stringOptions    }  }  private add = () => {    const obj = {      widget: '',      views: [],      enter: '',      out: '',      enterType: '',      outType: '',      nextRelation: '=',      enterErrorMessage: '',      outErrorMessage: ''    }    const {pathNodes} = this.state    const newNodes = pathNodes.concat(obj)    this.setState({      pathNodes: newNodes    })  }  private remove = (index: number) => () => {    const pathNodes = [...this.state.pathNodes]    pathNodes.splice(index, 1)    this.setState({      pathNodes    })  }  private saveDrillPathSetting = () => {    const {saveDrillPathSetting} = this.props    const result = this.checkDrillPathSettingValidate()    if (result && saveDrillPathSetting) {      if (this.state.pathOrFree === 'free') {        saveDrillPathSetting([])      } else {        saveDrillPathSetting(this.state.pathNodes)      }      this.hideDrillPathSettingModal()    }  }  private getNewPathNodes = (index, field, value) => {    const startPathObj = this.state.pathNodes[index]    startPathObj[field] = value    const newPathNodes = [...this.state.pathNodes]    newPathNodes.splice(index, 1, startPathObj)    return newPathNodes  }  private checkDrillPathSettingValidate = () => {    const { pathNodes, pathOrFree } = this.state    let validate = true    if (pathOrFree === 'path' && pathNodes.length === 1) {      validate = false      message.error('至少设置两个路径')    }    for (let index = 0, l = pathNodes.length;  index < l; index++) {      const node = pathNodes[index]      if (index !== 0) {        if (node.enter === '') {          const newPathNodes = this.getNewPathNodes(index, 'enterErrorMessage', '入参为必填项')          this.setState({            pathNodes: newPathNodes          })          validate = false          return validate        } else {          const newPathNodes = this.getNewPathNodes(index, 'enterErrorMessage', '')          this.setState({            pathNodes: newPathNodes          })        }      }      if (index !== (this.state.pathNodes.length - 1)) {        if (node.out === '') {          const newPathNodes = this.getNewPathNodes(index, 'outErrorMessage', '出参为必填项')          this.setState({            pathNodes: newPathNodes          })          validate = false          return validate        } else {          const newPathNodes = this.getNewPathNodes(index, 'outErrorMessage', '')          this.setState({            pathNodes: newPathNodes          })        }      }    }    return validate  }  private hideDrillPathSettingModal = () => {    const {cancel} = this.props    if (cancel) {      cancel()    }  }  private changeWidget = (index) => (value) => {    const startPathObj = this.state.pathNodes[index]    const viewLists = this.getViewList(Number(value))    startPathObj['widget'] = value    startPathObj['views'] = viewLists    const newPathNodes = [...this.state.pathNodes]    newPathNodes.splice(index, 1, startPathObj)    this.setState({      pathNodes: newPathNodes    })    const result = this.checkDrillPathSettingValidate()  }  private changeParams = (index, category, views?: any[]) => (value) => {    const startPathObj = this.state.pathNodes[index]    startPathObj[category] = value    if (views && views.length) {      startPathObj[`${category}Type`] = (views.find((view) => view.name === value))['sqlType']    }    const newPathNodes = [...this.state.pathNodes]    newPathNodes.splice(index, 1, startPathObj)    this.setState({      pathNodes: newPathNodes    })    const result = this.checkDrillPathSettingValidate()  }  public render () {    const {      itemId,      widgets,      views,      selectedWidget,      drillpathSetting    } = this.props    const {      screenWidth,      pathNodes    } = this.state    const formItemStyle = {      marginBottom: '8px'    }    const drillSettingButtons =    [(      <Button        key="forward"        size="large"        type="primary"        onClick={this.hideDrillPathSettingModal}      >        取 消      </Button>    ), (      <Button        key="submit"        size="large"        type="primary"        onClick={this.saveDrillPathSetting}      >        保 存      </Button>    )]    const widgetOptions = widgets.length && widgets.map((widget) => (      <Option value={`${widget.id}`} key={`widgetoption${widget.viewId}`}>{widget.name}</Option>    ))    const pathDrill = pathNodes.length && pathNodes.map((node, index) => {      const paramOptions = node && node.views && node.views.map((view) => {        return (        <Option value={`${view.name}`} key={`widgetoption${view.name}`}>{view.name}</Option>      )})      const relationOptions = this.generateFilterOperatorOptions(node.outType)      return (      <div key={`pathnodes${index}`} className={styles.pathNodeWrap}>         <div className={styles.pathNode}>          <div className={styles.pathBox}>            <p className={styles.delete}>              {this.state.pathNodes.length > 1 ? <Icon type="delete" onClick={this.remove(index)}/> : null}            </p>            <h4 className={styles.title}>              {index + 1}            </h4>            <div              style={{...formItemStyle}}            >              <Select                defaultValue={`${node['widget']}`}                placeholder="初始 可视化组件"                style={{width: '100%'}}                onChange={this.changeWidget(index)}              >                {widgetOptions}              </Select>            </div>            <div              style={{...formItemStyle}}            >              <Select                placeholder="入参"                style={{width: '100%'}}                disabled={index === 0}                defaultValue={node.enter}                onChange={this.changeParams(index, 'enter', node.views)}              >                {paramOptions}              </Select>            </div>            <div className={styles.errorMessage}>              {node.enterErrorMessage && node.enterErrorMessage.length ? node.enterErrorMessage : ''}            </div>            <div              style={{...formItemStyle}}            >              <Select                placeholder="出参"                style={{width: '100%'}}                defaultValue={node.out}                disabled={pathNodes.length > 0 && index === (pathNodes.length - 1)}                onChange={this.changeParams(index, 'out', node.views)}              >                {paramOptions}              </Select>            </div>            <div className={styles.errorMessage}>              {node.outErrorMessage && node.outErrorMessage.length ? node.outErrorMessage : ''}            </div>          </div>          <div className={styles.relation}>            <div              style={{margin: '0px'}}            >              <Select                defaultValue={`=`}                style={{width: '60px'}}                onChange={this.changeParams(index, 'nextRelation')}              >                {relationOptions}              </Select>            </div>          </div>          </div>      </div>    )})    const pathStyle = classnames({      [utilStyles.hide]: this.state.pathOrFree !== 'path'    })    return (      <div className={styles.drillPathSetting}>        <div className={styles.drillStyle}>          <b className={styles.label}>钻取模式: </b>          <RadioGroup value={this.state.pathOrFree} onChange={this.onChange}>            <Radio value={`free`} checked>自由钻取</Radio>            <Radio value={`path`}>路径钻取</Radio>          </RadioGroup>        </div>        <div className={pathStyle}>          <div className={styles.path}>            {pathDrill}            <div className={styles.add}>              <Button type="dashed" onClick={this.add} style={{ width: '60px', height: '60px' }}>                <Icon type="plus"/>              </Button>            </div>          </div>          <div style={{height: '30px'}}/>          {/* <div className={styles.footer}>            {drillSettingButtons}          </div> */}        </div>        <div className={styles.footer}>            {drillSettingButtons}          </div>      </div>    )  }}export default DrillPathSetting
 |