123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- import React from 'react'
- import classnames from 'classnames'
- import { findDOMNode } from 'react-dom'
- import { Icon, Row, Col, Form, Input, Checkbox, Select, Button, Popover, Modal, message } from 'antd'
- import { FormComponentProps } from 'antd/lib/form'
- import { WrappedFormUtils } from 'antd/lib/form/Form'
- import { CheckboxChangeEvent } from 'antd/lib/checkbox'
- const FormItem = Form.Item
- const { TextArea } = Input
- const { Option } = Select
- import 'codemirror/lib/codemirror.css'
- import 'assets/override/codemirror_theme.css'
- import 'codemirror/addon/hint/show-hint.css'
- const codeMirror = require('codemirror/lib/codemirror')
- require('codemirror/addon/edit/matchbrackets')
- require('codemirror/mode/javascript/javascript')
- require('codemirror/addon/hint/show-hint')
- require('codemirror/addon/hint/javascript-hint')
- require('codemirror/addon/display/placeholder')
- import { IFieldConfig } from './types'
- import { getDefaultFieldConfig, extractQueryVariableNames, getFieldAlias } from './util'
- import { IQueryVariableMap } from 'containers/Dashboard/types'
- import AliasExpressionTestModal from './AliasExpressionTest'
- const utilStyles = require('assets/less/util.less')
- interface IFieldConfigProps extends FormComponentProps {
- visible: boolean
- fieldConfig: IFieldConfig
- queryInfo: string[]
- onSave: (config: IFieldConfig) => void
- onCancel: () => void
- }
- interface IFieldConfigStates {
- localConfig: IFieldConfig
- queryVariableNames: string[]
- testResult: string
- testModalVisible: boolean
- }
- class FieldConfig extends React.PureComponent<IFieldConfigProps, IFieldConfigStates> {
- private codeEditor = React.createRef<any>()
- private codeMirrorInst: any
- private isInitEditor: boolean = true
- constructor (props: IFieldConfigProps) {
- super(props)
- const { fieldConfig } = this.props
- this.state = {
- localConfig: fieldConfig ? { ...fieldConfig } : getDefaultFieldConfig(),
- queryVariableNames: [],
- testResult: '',
- testModalVisible: false
- }
- }
- public componentDidMount () {
- const { form } = this.props
- const { localConfig } = this.state
- this.setFieldsValue(form, localConfig)
- }
- public componentDidUpdate () {
- if (this.state.localConfig.useExpression) {
- // @FIXME refactor to remove setTimeout usage
- const tId = setTimeout(() => {
- this.isInitEditor = this.initCodeEditor(this.isInitEditor)
- clearTimeout(tId)
- }, 0)
- }
- }
- public componentWillReceiveProps (nextProps: IFieldConfigProps) {
- const { fieldConfig, form } = nextProps
- if (fieldConfig !== this.props.fieldConfig) {
- this.isInitEditor = true
- form.resetFields()
- this.setState({
- localConfig: fieldConfig ? { ...fieldConfig } : getDefaultFieldConfig(),
- queryVariableNames: [],
- testResult: ''
- }, () => {
- this.setFieldsValue(form, this.state.localConfig)
- })
- }
- }
- private useExpressionChange = (e: CheckboxChangeEvent) => {
- const useExpression = e.target.checked
- this.setState({
- localConfig: {
- ...this.state.localConfig,
- useExpression
- }
- })
- }
- private setFieldsValue = (form: WrappedFormUtils, config: IFieldConfig) => {
- const { alias, desc, useExpression } = config
- form.setFieldsValue({
- [`alias_${useExpression ? 1 : 0}`]: alias,
- desc,
- useExpression
- })
- }
- private getFieldsValue (form: WrappedFormUtils): IFieldConfig {
- const fieldsValue: any = form.getFieldsValue()
- const { useExpression, desc } = fieldsValue
- const alias = useExpression
- ? this.codeMirrorInst.doc.getValue()
- : fieldsValue['alias_0']
- const config: IFieldConfig = {
- alias,
- useExpression,
- desc
- }
- return config
- }
- private initCodeEditor = (isInit: boolean) => {
- if (!this.codeMirrorInst) {
- // @FIXME ref is null in componentDidUpdate
- if (!this.codeEditor.current) { return true }
- const codeEditorDom = findDOMNode(this.codeEditor.current)
- this.codeMirrorInst = codeMirror.fromTextArea(codeEditorDom, {
- mode: 'text/javascript',
- theme: '3024-day',
- lineNumbers: true,
- lineWrapping: true
- })
- this.codeMirrorInst.setSize('100%', 300)
- }
- if (isInit && this.state.localConfig.useExpression) {
- this.codeMirrorInst.doc.setValue(this.state.localConfig.alias)
- }
- return false
- }
- private addQueryVariable = () => {
- const { form } = this.props
- const queryVariable = form.getFieldValue('queryVariable')
- this.codeMirrorInst.replaceSelection(` $${queryVariable}$ `)
- }
- private testExpression = () => {
- const expression: string = this.codeMirrorInst.doc.getValue()
- const queryVariableNames = extractQueryVariableNames(expression)
- const testModalVisible = queryVariableNames.length > 0
- this.setState({
- queryVariableNames,
- testModalVisible
- })
- if (!testModalVisible) {
- this.testExpressionResult()
- }
- }
- private testExpressionResult = (queryVariableMap: IQueryVariableMap = {}) => {
- const { form } = this.props
- const fieldConfig = this.getFieldsValue(form)
- const testResult = getFieldAlias(fieldConfig, queryVariableMap)
- this.setState({
- testResult,
- testModalVisible: false
- })
- return testResult
- }
- private clearExpressionResult = () => {
- this.setState({
- testResult: ''
- })
- }
- private closeTestModal = () => {
- this.setState({ testModalVisible: false })
- }
- private save = () => {
- const { form, onSave } = this.props
- form.validateFieldsAndScroll((err) => {
- if (err) { return }
- const config = this.getFieldsValue(form)
- onSave(config)
- })
- }
- private cancel = () => {
- this.props.onCancel()
- }
- private modalFooter = [(
- <Button
- key="cancel"
- size="large"
- onClick={this.cancel}
- >
- 取 消
- </Button>
- ), (
- <Button
- key="submit"
- size="large"
- type="primary"
- onClick={this.save}
- >
- 保 存
- </Button>
- )]
- private useExprInstr = (
- <div>
- <pre>
- {`1. 编辑器中编辑 JavaScript Function 代码片段,最后需要 return 别名值。
- 2. 若当前编辑的 Widget 对应的 View 中含有查询变量 queryVar 的定义,则字段别名中可添加查询变量。
- 在使用该 Widget 时,由界面中提供的查询变量值参与动态别名运算,生成对应的别名结果。
- 3. 可使用时间辅助函数生成格式时间字符串参与别名运算。
- Moment() // 取当前时间
- Moment('2018-01-01') // 转换指定的日期
- Moment().add(1, 'days').add(-1, 'months') // 日期推算,可链式调用,其中参数分别为数量和单位('hours', days', 'weeks', 'months', 'years' 等)
- Moment().format('YYYY-MM-DD HH:mm:ss') // 时间的格式化,其中两位的重复字母依次代表“年”、“月”、“日”、“时”、“分”、“秒”
- 4. 编写完毕后,可点击“测试”按钮查看显示效果,若含有参数,则需要在弹窗中需要测试的参数值,若报错,请调整编写。
- 5. 例子
- var province = $province$ // $province 为 View 中查询变量
- var currentYearMonth = Moment().format('YYYY年MM月') // 格式化当前年月
- var alias = province + '(' + currentYearMonth + ')' // 拼写最后显示的别名
- return alias
- `}
- </pre>
- </div>
- )
- public render () {
- const { visible, queryInfo, form } = this.props
- const { getFieldDecorator } = form
- const { localConfig, testResult, testModalVisible, queryVariableNames } = this.state
- const { desc, useExpression } = localConfig
- const variableOptions = (queryInfo || []).map((q) => (
- <Option key={q} value={q}>{q}</Option>
- ))
- const textAreaCls = classnames({ [utilStyles.hide]: !useExpression })
- const inputCls = classnames({ [utilStyles.hide]: useExpression })
- return (
- <Modal
- title="字段设置"
- wrapClassName="ant-modal-medium"
- footer={this.modalFooter}
- visible={visible}
- maskClosable={false}
- onCancel={this.cancel}
- >
- <Form>
- <FormItem label="字段别名" className={inputCls}>
- {getFieldDecorator('alias_0')(<Input />)}
- </FormItem>
- <FormItem label="字段别名" className={textAreaCls} style={{ height: '325px' }}>
- <TextArea ref={this.codeEditor} placeholder="请输入动态表达式" />
- </FormItem>
- {useExpression && <Row type="flex" align="middle" gutter={8}>
- {variableOptions.length > 0 && (
- <>
- <Col span={9}>
- <FormItem label="查询变量" labelCol={{span: 8}} wrapperCol={{span: 16}}>
- {getFieldDecorator('queryVariable', {
- initialValue: queryInfo[0]
- })(
- <Select>
- {variableOptions}
- </Select>
- )}
- </FormItem>
- </Col>
- <Col span={4}>
- <Row type="flex" align="middle">
- <FormItem>
- <Button onClick={this.addQueryVariable}>添加</Button>
- </FormItem>
- </Row>
- </Col>
- </>
- )}
- <Col span={3}>
- <FormItem>
- <Button type="primary" onClick={this.testExpression}>测试</Button>
- </FormItem>
- </Col>
- <Col span={8}>
- <FormItem>
- <Input
- readOnly
- placeholder="别名结果"
- value={testResult}
- addonAfter={<Icon type="close" onClick={this.clearExpressionResult} title="清除结果" />}
- />
- </FormItem>
- </Col>
- </Row>}
- <Row gutter={8} align="middle" justify="center">
- <Col span={9}>
- <FormItem>
- {getFieldDecorator('useExpression', {
- initialValue: useExpression,
- valuePropName: 'checked'
- })(<Checkbox onChange={this.useExpressionChange}>动态别名</Checkbox>)}
- <Popover
- title="动态别名使用说明"
- content={this.useExprInstr}
- >
- <Icon type="info-circle" />
- </Popover>
- </FormItem>
- </Col>
- </Row>
- <FormItem label="字段描述">
- {getFieldDecorator('desc', {
- initialValue: desc
- })(<TextArea rows={4} />)}
- </FormItem>
- </Form>
- <AliasExpressionTestModal
- visible={testModalVisible}
- queryVariableNames={queryVariableNames}
- onClose={this.closeTestModal}
- onTest={this.testExpressionResult}
- />
- </Modal>
- )
- }
- }
- export default Form.create<IFieldConfigProps>()(FieldConfig)
|