king
2024-08-28 c10fc10d5fd307a7b334bcde6af5c1175aaa0410
src/templates/sharecomponent/searchcomponent/searchform/index.jsx
@@ -1,19 +1,19 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Icon, Radio, notification, Tooltip, InputNumber, Checkbox, Cascader } from 'antd'
import { Form, Row, Col, Input, Select, Radio, notification, Tooltip, InputNumber, Checkbox, Cascader, AutoComplete } from 'antd'
import { QuestionCircleOutlined } from '@ant-design/icons'
import { dateOptions, matchReg, formRule } from '@/utils/option.js'
import EditTable from '../searcheditable'
import Utils from '@/utils/utils.js'
import { checkSQL } from '@/utils/utils-custom.js'
import CodeMirror from '@/templates/zshare/codemirror'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const FieldsTable = asyncComponent(() => import('@/templates/zshare/modalform/fieldtable'))
const DataTable = asyncComponent(() => import('@/templates/zshare/modalform/datatable'))
const EditTable = asyncComponent(() => import('@/templates/zshare/modalform/modaleditable'))
const FieldsTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const groupOptions = [
  {
@@ -26,8 +26,13 @@
      {value: 3, label: '前三天'},
      {value: 7, label: '前七天'},
      {value: 30, label: '前30天'},
      {value: -1, label: '明天'},
      {value: -2, label: '后天'}
      {value: 90, text: '前90天'},
      {value: -1, text: '明天'},
      {value: -2, text: '后天'},
      {value: -3, text: '后三天'},
      {value: -7, text: '后七天'},
      {value: -30, text: '后30天'},
      {value: -90, text: '后90天'},
    ]
  },
  {
@@ -79,9 +84,12 @@
      {value: '[3, 0]', label: '近三天'},
      {value: '[7, 0]', label: '近七天'},
      {value: '[30, 0]', label: '近30天'},
      {value: '[90, 0]', label: '近90天'},
      {value: '[7, -7]', label: '前后七天'},
      {value: '[30, -30]', label: '前后30天'},
      {value: '[90, -90]', label: '前后90天'},
      {value: '[180, -180]', label: '前后180天'},
      {value: '[365, -365]', label: '前后365天'},
      {value: '[-1, -1]', label: '明天'},
      {value: '[-2, -2]', label: '后天'}
    ]
@@ -89,105 +97,64 @@
]
const searchTypeOptions = {
  text: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'inputType', 'advanced', 'query'],
  select: ['label', 'field', 'resourceType', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'setAll', 'dropdown', 'query'],
  multiselect: ['label', 'field', 'resourceType', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  link: ['label', 'field', 'resourceType', 'initval', 'type', 'linkField', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'setAll', 'dropdown', 'query'],
  date: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  checkcard: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'resourceType', 'display', 'width', 'multiple', 'borderColor', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  dateweek: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  datemonth: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  daterange: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query'],
  group: ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'labelShow', 'query'],
  range: ['label', 'type', 'field', 'initval', 'match', 'blacklist', 'Hide', 'required', 'maxValue', 'minValue', 'step', 'labelShow', 'query']
  text: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'inputType', 'advanced', 'query', 'labelwidth'],
  select: ['label', 'field', 'resourceType', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'dropdown', 'query', 'labelwidth'],
  radio: ['label', 'field', 'resourceType', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  multiselect: ['label', 'field', 'resourceType', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  link: ['label', 'field', 'resourceType', 'initval', 'type', 'linkField', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'dropdown', 'query', 'labelwidth'],
  date: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'precision', 'labelwidth'],
  checkcard: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'resourceType', 'display', 'width', 'multiple', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  dateweek: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  datemonth: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  daterange: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'required', 'Hide', 'labelShow', 'advanced', 'query', 'precision', 'labelwidth'],
  group: ['label', 'type', 'field', 'datefield', 'initval', 'blacklist', 'ratio', 'items', 'required', 'labelShow', 'query', 'labelwidth'],
  switch: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'openVal', 'closeVal', 'openText', 'closeText', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  check: ['label', 'field', 'initval', 'type', 'match', 'ratio', 'blacklist', 'openVal', 'closeVal', 'checkTip', 'Hide', 'labelShow', 'advanced', 'query', 'labelwidth'],
  range: ['label', 'type', 'field', 'initval', 'match', 'ratio', 'blacklist', 'Hide', 'required', 'maxValue', 'minValue', 'step', 'labelShow', 'query', 'labelwidth', 'advanced']
}
class MainSearch extends Component {
  static propTpyes = {
    dict: PropTypes.object,     // 字典项
    formlist: PropTypes.any,    // 表单
    card: PropTypes.object,     // 搜索条件信息
    inputSubmit: PropTypes.any  // 回车提交事件
  }
  state = {
    openType: null,          // 搜索条件显示类型
    resourceType: null,      // 下拉搜索时,选项来源类型
    formlist: null,          // 表单
    display: null,
    cFields: [],
    textTooltip: '字段名可以使用逗号分隔,进行综合搜索',
    formlist: null
  }
  /**
   * @description 表单预处理
   * 1、根据表单类型,显示表单可编辑项
   * 2、下拉选择,根据数据源类型显示相关配置
   */
  record = {}
  UNSAFE_componentWillMount () {
    const { formlist, dict } = this.props
    let type = ''
    let _items = []
    let resourceType = ''
    let display = ''
    let cFields = []
    let multiple = 'false'
    formlist.forEach(cell => {
      if (cell.key === 'type') {
        type = cell.initVal
      } else if (cell.key === 'items') {
        _items = cell.initVal
      } else if (cell.key === 'display') {
        display = cell.initVal
      } else if (cell.key === 'resourceType') {
        resourceType = cell.initVal
      } else if (cell.key === 'fields') {
        cFields = cell.initVal
      } else if (cell.key === 'multiple') {
        multiple = cell.initVal
      }
    this.props.formlist.forEach(item => {
      this.record[item.key] = item.initVal
    })
    let _options = this.getOptions(type, resourceType, display)
    let { shows, reOptions, reTypes, reTooltip, reLabel, reRequired } = this.getMutilOptions()
    this.setState({
      display,
      cFields,
      openType: type,
      items: _items,
      resourceType,
      formlist: formlist.map(form => {
        // 表单为初始值字段,且数据类型属于时间类型时,设置初始值为下拉选择,并重置选择项
        if (form.key === 'initval' && dateOptions.hasOwnProperty(type)) {
          form.options = dateOptions[type]
          form.type = 'select'
        } else if (form.key === 'initval' && type === 'group') {
          form.options = groupOptions.filter(op => _items.includes(op.value))
          form.type = 'cascader'
        } else if (form.key === 'match') { // 表单为匹配字段时,根据不同的类型,显示对应的匹配规则
          if (type === 'text') {
            form.options = matchReg.text
          } else if (type === 'multiselect' || (type === 'checkcard' && multiple === 'true')) {
            form.options = matchReg.multiselect
          } else if (type === 'select' || type === 'link' || type === 'checkcard') {
            form.options = matchReg.select
          } else if (type === 'date') {
            form.options = matchReg.date
          } else if (type === 'datemonth') {
            form.options = matchReg.datemonth
          } else if (type === 'dateweek' || type === 'daterange' || type === 'range') {
            form.options = matchReg.daterange
          }
        } else if (form.key === 'field' && (type === 'text' || type === 'select')) {
          form.tooltip = this.state.textTooltip
        } else if (form.key === 'field' && type === 'group') {
          form.tooltip = '查询数据时(自定义脚本或统计数据源),类型字段将用作替换脚本中的 @字段@ ,类型字段对应值为 {"日": "day", "周": "week", "月": "month", "季": "quarter", "年": "year", "自定义": "customized"}。'
          form.label = dict['model.form.type'] + dict['model.form.field']
      formlist: this.props.formlist.map(item => {
        item.hidden = !shows.includes(item.key)
        item.initVal = this.record[item.key]
        if (reOptions[item.key]) {
          item.options = reOptions[item.key]
        }
        form.hidden = !_options.includes(form.key)
        return form
        if (reTypes[item.key]) {
          item.type = reTypes[item.key]
        }
        if (reTooltip[item.key] !== undefined) {
          item.tooltip = reTooltip[item.key]
        }
        if (reLabel[item.key] !== undefined) {
          item.label = reLabel[item.key]
        }
        if (reRequired[item.key] !== undefined) {
          item.required = reRequired[item.key]
        }
        return item
      })
    })
  }
@@ -205,182 +172,287 @@
    }
  }
  getOptions = (type, resourceType, display) => {
    let _options = fromJS(searchTypeOptions[type]).toJS() // 选项列表
    if (['multiselect', 'select', 'link'].includes(type) && resourceType === '0') {        // 下拉选择类型、选项为自定义资源
      _options.push('options')
    } else if (['multiselect', 'select', 'link'].includes(type) && resourceType === '1') { // 下拉选择类型、选项为后台数据源中获取
      _options.push('dataSource', 'valueField', 'valueText', 'orderBy', 'orderType', 'database')
  getMutilOptions = () => {
    let type = this.record.type
    let shows = fromJS(searchTypeOptions[type]).toJS()
    let reOptions = {}
    let reTypes = {}
    let reTooltip = {}
    let reRequired = {}
    let reLabel = {}
    if (['multiselect', 'select', 'link', 'radio'].includes(type)) {
      reRequired.linkField = true
      if (this.record.resourceType === '0') {        // 自定义资源
        shows.push('options')
      } else if (this.record.resourceType === '1') { // 数据源
        shows.push('dataSource', 'valueField', 'valueText', 'orderBy', 'orderType', 'database')
      }
    } else if (type === 'checkcard') {
      if (display === 'picture') {
        if (resourceType === '0') {        // 自定义资源
          _options.push('options', 'picratio')
        } else if (resourceType === '1') { // 数据源
          _options.push('dataSource', 'cardValField', 'urlField', 'orderBy', 'orderType', 'database', 'picratio')
      reRequired.fields = false
      reOptions.multiple = [{
        value: 'false',
        text: '单选'
      }, {
        value: 'true',
        text: '多选'
      }]
      if (this.record.display === 'picture') {
        if (this.record.resourceType === '0') {        // 自定义资源
          shows.push('options', 'fields', 'picratio')
        } else if (this.record.resourceType === '1') { // 数据源
          shows.push('dataSource', 'cardValField', 'fields', 'urlField', 'orderBy', 'orderType', 'database', 'picratio')
        }
      } else if (this.record.display === 'color') {
        if (this.record.resourceType === '0') {        // 自定义资源
          shows.push('options', 'fields')
        } else if (this.record.resourceType === '1') { // 数据源
          shows.push('dataSource', 'cardValField', 'colorField', 'fields', 'orderBy', 'orderType', 'database')
        }
      } else {
        if (resourceType === '0') {        // 自定义资源
          _options.push('options', 'fields', 'backgroundColor')
        } else if (resourceType === '1') { // 数据源
          _options.push('dataSource', 'cardValField', 'fields', 'orderBy', 'orderType', 'database', 'backgroundColor')
        let appType = sessionStorage.getItem('appType')
        if (appType === '') {
          reOptions.multiple = [{
            value: 'false',
            text: '单选'
          }, {
            value: 'true',
            text: '多选'
          }, {
            value: 'dropdown',
            text: '下拉菜单'
          }]
        }
        reRequired.fields = true
        if (this.record.resourceType === '0') {        // 自定义资源
          shows.push('options', 'fields', 'selectStyle', 'border')
        } else if (this.record.resourceType === '1') { // 数据源
          shows.push('dataSource', 'cardValField', 'fields', 'orderBy', 'orderType', 'database', 'selectStyle', 'border')
        }
        if (this.record.selectStyle === 'custom') {
          shows.push('backgroundColor')
        }
        if (this.record.multiple === 'dropdown') {
          shows.push('mark')
          if (this.record.resourceType === '1') {
            shows.push('parentField')
          }
        }
      }
      shows.push('linkField')
      reRequired.linkField = false
    } else if (type === 'daterange' || type === 'datemonth') {
      if (this.record.initval) {
        shows.push('dateShift')
      }
    }
    return _options
    if (dateOptions.hasOwnProperty(type)) { // 根据搜索条件类型,选择初始值的类型及数据
      reOptions.initval = dateOptions[type]
      reTypes.initval = 'select'
    } else if (type === 'group') {
      reOptions.initval = groupOptions.filter(op => this.record.items.includes(op.value))
      reTypes.initval = 'cascader'
    } else {
      reTypes.initval = 'text'
    }
    reTooltip.match = ''
    if (type === 'text') {
      reOptions.match = matchReg.class1
    } else if (type === 'multiselect') {
      reOptions.match = matchReg.class3
    } else if (type === 'select' || type === 'link') {
      reOptions.match = matchReg.class1
    } else if (type === 'switch' || type === 'check') {
      reOptions.match = matchReg.class2
      if (type === 'switch') {
        reLabel.openVal = '开启值'
        reLabel.closeVal = '关闭值'
      } else {
        reLabel.openVal = '勾选值'
        reLabel.closeVal = '不勾选值'
      }
    } else if (type === 'date') {
      reOptions.match = matchReg.class4
    } else if (type === 'datemonth') {
      reTooltip.match = '匹配模式为 between 时,搜索条件为大于月初小于月末,匹配模式为 = 时,搜索条件为等于当前月(YYYY-MM)。'
      reOptions.match = matchReg.class6
    } else if (type === 'dateweek' || type === 'daterange' || type === 'range') {
      reOptions.match = matchReg.class5
    } else if (type === 'checkcard') {
      if (this.record.multiple === 'false' || this.record.multiple === 'dropdown') {
        reOptions.match = matchReg.class1
      } else if (this.record.multiple === 'true') {
        reOptions.match = matchReg.class3
      }
    }
    reTooltip.field = ''
    reLabel.field = '字段'
    if (type === 'text' || type === 'select') {
      reTooltip.field = '字段名可以使用逗号分隔,进行综合搜索。'
    } else if (type === 'daterange') {
      reTooltip.field = '字段名可以使用逗号分隔,例如startTime,endTime。'
    } else if (type === 'group') {
      reTooltip.field = '查询数据时(自定义脚本或统计数据源),类型字段将用作替换脚本中的 @字段@ ,类型字段对应值为:日 -> day;周 -> week;月 -> month;季 -> quarter;年 -> year;自定义 -> customized'
      reLabel.field = '类型字段'
    }
    reTooltip.initval = ''
    if (type === 'select') {
      if (this.record.resourceType === '0') {
        reTooltip.initval = '初始值应为数据的Value值,可使用@username@、@fullName@'
      } else if (this.record.resourceType === '1') {
        reTooltip.initval = '初始值应为《值·字段》的值,可使用@username@、@fullName@、$first。注:使用$first时,搜索条件应为必填。'
      }
    } else if (type === 'link') {
      if (this.record.resourceType === '0') {
        reTooltip.initval = '初始值应为数据的Value值。'
      } else if (this.record.resourceType === '1') {
        reTooltip.initval = '初始值应为《值·字段》的值,可使用$first。注:使用$first时,搜索条件应为必填。'
      }
    } else if (type === 'text') {
      reTooltip.initval = '可使用@username@、@fullName@。'
    } else if (type === 'range') {
      reTooltip.initval = '使用逗号拼接,例如 3,10'
    }
    return {
      shows,
      reOptions,
      reTypes,
      reTooltip,
      reRequired,
      reLabel
    }
  }
  /**
   * @description 搜索条件类型切换
   */
  openTypeChange = (key, value) => {
    const { dict } = this.props
    const { resourceType, items, display } = this.state
  optionChange = (key, value) => {
    this.record[key] = value
    let _fieldval = {}
    if (key === 'type') {
      let _options = this.getOptions(value, resourceType, display)
      let matchs = []
      this.record.initval = ''
      _fieldval.initval = ''
      this.setState({
        openType: value,
        formlist: this.state.formlist.map(form => {
          form.hidden = !_options.includes(form.key)            // 隐藏表单
      if (value === 'text' || value === 'multiselect') {
        this.record.match = 'like'
        _fieldval.match = 'like'
      } else if (value === 'select' || value === 'link' || value === 'checkcard') {
        this.record.match = '='
        _fieldval.match = '='
      } else if (value === 'date') {
        this.record.match = '>='
        _fieldval.match = '>='
      } else if (value === 'datemonth' || value === 'dateweek' || value === 'daterange' || value === 'range') {
        this.record.match = 'between'
        _fieldval.match = 'between'
      }
          if (form.key === 'initval') {
            if (dateOptions.hasOwnProperty(value)) { // 根据搜索条件类型,选择初始值的类型及数据
              form.options = dateOptions[value]
              form.type = 'select'
            } else if (value === 'group') {
              form.options = groupOptions.filter(op => items.includes(op.value))
              form.type = 'cascader'
            } else {
              form.type = 'text'
            }
          } else if (form.key === 'match') {                     // 搜索条件类型切换时,匹配规则类型对应切换
            if (value === 'text') {
              form.options = matchReg.text
            } else if (value === 'multiselect') {
              form.options = matchReg.multiselect
            } else if (value === 'select' || value === 'link' || value === 'checkcard') {
              form.options = matchReg.select
            } else if (value === 'date') {
              form.options = matchReg.date
            } else if (value === 'datemonth') {
              form.options = matchReg.datemonth
            } else if (value === 'dateweek' || value === 'daterange' || value === 'range') {
              form.options = matchReg.daterange
            }
            matchs = form.options
          } else if (form.key === 'field') {
            form.tooltip = ''
            form.label = dict['model.form.field']
            if (value === 'text' || value === 'select') {
              form.tooltip = this.state.textTooltip
            } else if (value === 'group') {
              form.tooltip = '查询数据时(自定义脚本或统计数据源),类型字段将用作替换脚本中的 @字段@ ,类型字段对应值为 {"日": "day", "周": "week", "月": "month", "季": "quarter", "年": "year", "自定义": "customized"}。'
              form.label = dict['model.form.type'] + dict['model.form.field']
            }
      if (value === 'checkcard') {
        this.record.multiple = 'false'
        _fieldval.multiple = 'false'
      }
      if (this.record.options.length > 0) {
        if (value === 'checkcard') {
          this.record.options = this.record.options.map(cell => {
            cell.$value = cell.Value || ''
            delete cell.Value
            return cell
          })
          if (this.record.options[0].Text) {
            let key = Utils.getuuid()
            this.record.fields = [{
              $index: 1,
              align: 'left',
              color: 'rgba(0, 0, 0, 0.85)',
              field: 'Text',
              fontSize: 14,
              key: key,
              uuid: key
            }]
          }
          return form
        })
      }, () => {
        if (this.props.form.getFieldValue('initval') !== undefined) {
          this.props.form.setFieldsValue({initval: ''})
        } else if (['multiselect', 'select', 'link', 'radio'].includes(value)) {
          if (!this.record.options[0].Text && this.record.fields.length > 0) {
            let field = this.record.fields[0].field
            this.record.options = this.record.options.map(cell => {
              cell.Value = cell.Value || cell.$value || ''
              cell.Text = cell[field] || ''
              return cell
            })
          } else {
            this.record.options = this.record.options.map(cell => {
              cell.Value = cell.Value || cell.$value || ''
              return cell
            })
          }
        }
        if (this.props.form.getFieldValue('match') !== undefined) {
          this.props.form.setFieldsValue({match: matchs[0].value})
        }
        if (this.props.form.getFieldValue('multiple') !== undefined) {
          this.props.form.setFieldsValue({multiple: 'false'})
        }
      })
    }
  }
  /**
   * @description 数据源类型切换
   */
  onChange = (e, key) => {
    const { openType, display, resourceType } = this.state
    let value = e.target.value
    if (key === 'resourceType') {
      let _options = this.getOptions(openType, value, display)
      this.setState({
        resourceType: value,
        formlist: this.state.formlist.map(form => {
          form.hidden = !_options.includes(form.key)
          return form
        })
      })
    } else if (key === 'display') {
      let _options = this.getOptions(openType, resourceType, value)
      this.setState({
        display: value,
        formlist: this.state.formlist.map(form => {
          form.hidden = !_options.includes(form.key)
          return form
        })
      })
      }
    } else if (key === 'multiple') {
      let matchs = []
      this.setState({
        formlist: this.state.formlist.map(form => {
          if (form.key === 'match') {
            if (value === 'true') {
              form.options = matchReg.multiselect
            } else {
              form.options = matchReg.select
            }
            matchs = form.options
          }
          return form
        })
      }, () => {
        if (this.props.form.getFieldValue('match') !== undefined) {
          this.props.form.setFieldsValue({match: matchs[0].value})
        }
      })
      if (value === 'false') {
        this.record.match = '='
        _fieldval.match = '='
      } else if (value === 'true') {
        this.record.match = 'like'
        _fieldval.match = 'like'
      }
    } else if (key === 'display') {
      this.record.multiple = 'false'
      _fieldval.multiple = 'false'
    } else if (key === 'items') {
      let _initval = this.props.form.getFieldValue('initval')
      if (_initval && !value.includes(_initval[0])) {
        this.record.initval = ''
        _fieldval.initval = ''
      }
    }
  }
  changeField = (data) => {
    let { shows, reOptions, reTypes, reLabel, reTooltip, reRequired } = this.getMutilOptions()
    this.setState({
      cFields: data,
      formlist: this.state.formlist.map(form => {
        if (form.key === 'fields') {
          form.initVal = data
      formlist: this.state.formlist.map(item => {
        item.hidden = !shows.includes(item.key)
        item.initVal = this.record[item.key]
        if (reOptions[item.key]) {
          item.options = reOptions[item.key]
        }
        return form
        if (reTypes[item.key]) {
          item.type = reTypes[item.key]
        }
        if (reTooltip[item.key] !== undefined) {
          item.tooltip = reTooltip[item.key]
        }
        if (reLabel[item.key] !== undefined) {
          item.label = reLabel[item.key]
        }
        if (reRequired[item.key] !== undefined) {
          item.required = reRequired[item.key]
        }
        return item
      })
    }, () => {
      this.props.form.setFieldsValue(_fieldval)
    })
  }
  checkChange = (values, key) => {
    const { openType, formlist } = this.state
  changeField = (data) => {
    this.record.fields = data || []
  }
    if (key === 'items') {
      this.setState({
        items: values,
        formlist: formlist.map(form => {
          if (form.key === 'initval' && openType === 'group') {
            form.options = groupOptions.filter(op => values.includes(op.value))
          }
          return form
        })
      })
      let _initval = this.props.form.getFieldValue('initval')
      if (_initval && !values.includes(_initval[0])) {
        this.props.form.setFieldsValue({initval: ''})
      }
    }
  changeOptions = (data) => {
    this.record.options = data || []
  }
  handleSubmit = (e) => {
@@ -391,244 +463,247 @@
    }
  }
  handleEmpty = () => {
    let field = this.props.form.getFieldValue('valueField')
    if (!field) {
      notification.warning({
        top: 92,
        message: '请填写值·字段。',
        duration: 5
      })
      return
    }
    let text = this.props.form.getFieldValue('valueText')
    if (!text) {
      notification.warning({
        top: 92,
        message: '请填写文本·字段。',
        duration: 5
      })
      return
    }
    let resource = this.props.form.getFieldValue('dataSource') || ''
    if (field === text) {
      resource = `select '' as ${field} union all \n${resource}`
    } else {
      resource = `select '' as ${field},'全部' as ${text} union all \n${resource}`
    }
    this.props.form.setFieldsValue({dataSource: resource})
  }
  complete = (key, option) => {
    let label = option.props.label
    this.props.form.setFieldsValue({label: label})
  }
  getFields() {
    const { openType } = this.state
    const { getFieldDecorator } = this.props.form
    const { formlist } = this.state
    const fields = []
    this.state.formlist.forEach((item, index) => {
    formlist.forEach((item, index) => {
      if (item.hidden || item.forbid) return
      if (item.type === 'text') { // 文本搜索
        let rules = []
        if (item.key === 'field' || item.key === 'datefield') {
          rules = [{
            pattern: (openType === 'text' || openType === 'select') ? formRule.field.multipattern : formRule.field.pattern,
      let span = 12
      let rules = []
      let className = ''
      let content = null
      let extra = null
      let initVal = item.initVal || ''
      if (item.type === 'text') {
        let type = this.record.type
        rules = [
          { required: item.required, message: '请输入' + item.label + '!' }
        ]
        if (item.key === 'field' || item.key === 'datefield' || item.key === 'dateShift') {
          rules.push({
            pattern: (type === 'text' || type === 'select' || type === 'daterange') ? formRule.field.multipattern : formRule.field.pattern,
            message: formRule.field.message
          }, {
            max: formRule.field.max,
            message: formRule.field.maxMessage
          }]
          })
        } else {
          rules = [{
          rules.push({
            max: formRule.input.max,
            message: formRule.input.message
          }]
          })
        }
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  },
                  ...rules
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(item.max ?
                  <InputNumber min={item.min} max={item.max} precision={0} onPressEnter={this.handleSubmit}/> :
                  <InputNumber onPressEnter={this.handleSubmit}/>
                )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉搜索
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Select
                  showSearch
                  filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  onChange={(value) => {this.openTypeChange(item.key, value)}}
                  getPopupContainer={() => document.getElementById('commontable-search-form-box')}
                >
                  {item.options.map(option =>
                    <Select.Option id={option.value} title={option.text} key={option.value} value={option.value}>
                      {item.key === 'icon' && <Icon type={option.text} />} {option.text}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <QuestionCircleOutlined className="mk-form-tip" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Radio.Group onChange={(e) => {this.onChange(e, item.key)}}>
                  {
                    item.options.map(option => {
                      return (
                        <Radio key={option.value} value={option.value}>{option.text}</Radio>
                      )
                    })
                  }
                </Radio.Group>,
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'textarea') {
        fields.push(
          <Col span={24} key={index}>
            <Form.Item className="text-area" label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<CodeMirror />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'options') {
        if (openType !== 'checkcard') {
          fields.push(
            <Col span={24} key={index}>
              <Form.Item label={item.label} className="text-area">
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal
                })(<EditTable dict={this.props.dict} type={this.state.openType} data={item.initVal}/>)}
              </Form.Item>
            </Col>
          )
        if (item.key === 'field' && item.options && item.options.length > 0) {
          content = <AutoComplete
            dataSource={item.options.map((cell) => <AutoComplete.Option label={cell.label} value={cell.value} key={cell.key}>
              {cell.text}
            </AutoComplete.Option>)}
            filterOption={(input, option) => option.props.children.indexOf(input) > -1}
            onSelect={this.complete}
            placeholder=""
          >
            <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />
          </AutoComplete>
        } else {
          fields.push(
            <Col span={24} key={index}>
              <Form.Item label={item.label} className="text-area">
                {getFieldDecorator(item.key, {
                  initialValue: item.initVal
                })(<DataTable dict={this.props.dict} type={this.state.display} fields={this.state.cFields}/>)}
              </Form.Item>
            </Col>
          )
          content = <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} onChange={(e) => {this.optionChange(item.key, e.target.value)}}/>
        }
      } else if (item.type === 'number') {
        rules = [
          { required: item.required, message: '请输入' + item.label + '!' }
        ]
        initVal = item.initVal
        if (item.max) {
          content = <InputNumber min={item.min} max={item.max} precision={item.precision || 0} onPressEnter={this.handleSubmit}/>
        } else {
          content = <InputNumber onPressEnter={this.handleSubmit}/>
        }
      } else if (item.type === 'select') { // 下拉搜索
        rules = [
          { required: item.required, message: '请选择' + item.label + '!' }
        ]
        content = <Select
          showSearch
          allowClear={item.allowClear === true}
          filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
          onChange={(value) => {this.optionChange(item.key, value)}}
          getPopupContainer={() => document.getElementById('commontable-search-form-box')}
        >
          {item.options.map((option, i) =>
            <Select.Option key={`${i}`} value={option.value}>
              {option.text || option.label}
            </Select.Option>
          )}
        </Select>
      } else if (item.type === 'radio') {
        rules = [
          { required: item.required, message: '请选择' + item.label + '!' }
        ]
        content = <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => {this.optionChange(item.key, e.target.value)}}>
          {item.options.map(option => {
            return (
              <Radio key={option.value} value={option.value}>{option.text}</Radio>
            )
          })}
        </Radio.Group>
      } else if (item.type === 'codemirror') {
        rules = [
          { required: item.required, message: '请输入' + item.label + '!' }
        ]
        span = 24
        if (this.record.type === 'select' || this.record.type === 'link') {
          extra = <span className="add-resource-empty" onClick={this.handleEmpty}>全部</span>
        }
        if (item.placeholder) {
          className = 'show-public-var'
          extra = <><span className="resource-public-var">{item.placeholder}</span>{extra}</>
        }
        content = <CodeMirror />
      } else if (item.type === 'options') {
        span = 24
        let type = this.record.type
        if (type !== 'checkcard') {
          let columns = []
          if (type === 'link') {
            columns.push({ title: 'ParentID', key: 'ParentID', strict: true })
          }
          columns.push({ title: 'Value', key: 'Value', strict: true })
          columns.push({ title: 'Text', key: 'Text' })
          content = <EditTable columns={columns} module="search" onChange={this.changeOptions}/>
        } else {
          if (this.record.linkField) {
            type = 'link'
          }
          let columns = []
          let fields = this.record.fields || []
          let keys = ['ParentID', 'pid']
          if (type === 'link') {
            columns.push({ title: 'ParentID', key: 'ParentID', strict: true })
          } else if (this.record.multiple === 'dropdown' && this.record.display === 'text') {
            columns.push({ title: 'pid', key: 'pid', strict: true })
          }
          columns.push({ title: 'Value', key: '$value', strict: true })
          if (this.record.display === 'picture') {
            columns.push({ title: 'url', key: '$url', type: 'file' })
          } else if (this.record.display === 'color') {
            columns.push({ title: 'Color', key: '$color' })
            extra = <span>使用十六进制色彩代码(HEX)时,请在色值前添加 #</span>
          }
          fields.forEach(item => {
            keys.push(item.field)
            columns.push({ title: item.field, key: item.field })
          })
          content = <EditTable columns={columns} onChange={this.changeOptions}/>
        }
      } else if (item.type === 'fields') {
        fields.push(
          <Col span={24} key={index}>
            <Form.Item label={item.label} className="text-area">
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(<FieldsTable dict={this.props.dict} onChange={this.changeField}/>)}
            </Form.Item>
          </Col>
        )
        span = 24
        rules = [
          { required: item.required, message: '请添加' + item.label + '!' }
        ]
        content = <FieldsTable indexShow={false} actions={['edit', 'move', 'del', 'add']} columns={item.columns} data={this.record.fields || []} onChange={this.changeField}/>
      } else if (item.type === 'checkbox') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Checkbox.Group style={{width: '105%'}} options={item.options} onChange={(values) => this.checkChange(values, item.key)}/>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'multiselect') { // 多选
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || []
              })(
                <Select
                  showSearch
                  mode="multiple"
                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                >
                  {item.options.map((option, i) =>
                    <Select.Option id={i} key={i} value={option.value}>{option.text}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'cascader') { // 多选
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <Cascader options={item.options} placeholder="" />
              )}
            </Form.Item>
          </Col>
        )
        rules = [
          { required: item.required, message: '请选择' + item.label + '!' }
        ]
        content = <Checkbox.Group style={{width: '105%'}} options={item.options} onChange={(values) => this.optionChange(item.key, values)}/>
      } else if (item.type === 'multiselect') {
        content = <Select
          showSearch
          mode="multiple"
          filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
        >
          {item.options.map((option, i) =>
            <Select.Option id={i} key={i} value={option.value}>{option.text}</Select.Option>
          )}
        </Select>
      } else if (item.type === 'cascader') {
        content = <Cascader options={item.options} />
      } else if (item.type === 'color') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label} className="color-form-item">
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(<ColorSketch allowClear={true}/>)}
            </Form.Item>
          </Col>
        )
        className = 'color-form-item'
        rules = [
          { required: item.required, message: '请选择' + item.label + '!' }
        ]
        content = <ColorSketch allowClear={true}/>
      }
      fields.push(
        <Col span={span} key={index}>
          <Form.Item className={className} extra={extra} label={item.tooltip ?
            <Tooltip placement="topLeft" title={<div onClick={(e) => e.stopPropagation()}>{item.tooltip}</div>}>
              <QuestionCircleOutlined className="mk-form-tip" />
              {item.label}
            </Tooltip> : item.label
          }>
            {getFieldDecorator(item.key, {
              initialValue: initVal,
              rules: rules
            })(content)}
          </Form.Item>
        </Col>
      )
    })
    return fields
@@ -639,56 +714,166 @@
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let isvalid = true
          values.uuid = this.props.card.uuid
          if (/,/.test(values.field)) {
            values.field = values.field.split(',').filter(Boolean)
            if (values.type === 'daterange' && values.field.length > 2) {
              values.field.length = 2
            }
            values.field = values.field.join(',')
          }
          if (['select', 'link'].includes(values.type)) {
            if (values.resourceType === '1') {
              if (/\$first/.test(values.initval) && values.initval.replace(/\s/g, '') === '$first') {
                values.initval = '$first'
              }
              if (values.initval === '$first' && values.required !== 'true') {
                notification.warning({
                  top: 92,
                  message: '使用$first时,搜索条件应为必填!',
                  duration: 5
                })
                return
              }
            }
          }
          // 下拉菜单或联动菜单
          if (['multiselect', 'select', 'link', 'checkcard'].includes(values.type) && values.resourceType === '0') {
            values.options = values.options || []
            values.dataSource = ''
            let emptys = []
            if (['multiselect', 'select'].includes(values.type)) {
              emptys = values.options.filter(op => !(op.Value && op.Text))
            } else if (values.type === 'link') {
              emptys = values.options.filter(op => !(op.Value && op.Text && op.ParentID))
          if (['multiselect', 'select', 'link', 'radio'].includes(values.type)) {
            if (values.resourceType === '0') {
              values.options = values.options || []
              values.dataSource = ''
              if (values.options.filter(op => op.Text === '').length > 0) {
                notification.warning({
                  top: 92,
                  message: '提示文本(Text)不可为空!',
                  duration: 5
                })
                return
              } else if (values.options.filter(op => op.Value === '').length > 1) {
                notification.warning({
                  top: 92,
                  message: 'Value为空最多只可添加一行(在关联菜单中,Value为空时不区分ParentID)!',
                  duration: 5
                })
                return
              } else if (values.type === 'link') {
                let arr = values.options.map(m => m.ParentID + m.Value)
                let _arr = Array.from(new Set(arr))
                if (arr.length > _arr.length) {
                  notification.warning({
                    top: 92,
                    message: '同一ParentID中,Value值不可重复!',
                    duration: 5
                  })
                  return
                }
              } else {
                let arr = values.options.map(m => m.Value)
                let _arr = Array.from(new Set(arr))
                if (arr.length > _arr.length) {
                  notification.warning({
                    top: 92,
                    message: 'Value值不可重复!',
                    duration: 5
                  })
                  return
                }
              }
            } else {
              values.options = []
            }
            if (emptys.length > 0) {
              isvalid = false
          } else if (values.type === 'checkcard') {
            if (values.multiple === 'dropdown' && values.display !== 'text') {
              values.multiple = 'false'
            }
          } else if (['multiselect', 'select', 'link', 'checkcard'].includes(values.type) && values.resourceType === '1') {
            values.options = []
            if (values.resourceType === '0') {
              values.options = values.options || []
              values.options = values.options.map(m => {
                m.ParentID = m.ParentID || ''
                m.pid = m.pid || ''
                return m
              })
              let type = values.type
              if (values.linkField) {
                type = 'link'
              }
              if (type === 'link') {
                let arr = values.options.map(m => m.ParentID + m.$value)
                let _arr = Array.from(new Set(arr))
                if (arr.length > _arr.length) {
                  notification.warning({
                    top: 92,
                    message: '同一ParentID中,Value值不可重复!',
                    duration: 5
                  })
                  return
                }
              } else {
                let arr = values.options.map(m => m.$value)
                let _arr = Array.from(new Set(arr))
                if (arr.length > _arr.length) {
                  notification.warning({
                    top: 92,
                    message: 'Value值不可重复!',
                    duration: 5
                  })
                  return
                }
              }
            } else {
              values.options = []
            }
          }
          if (values.type === 'range') {
            let error = ''
            if (values.maxValue <= values.minValue) {
              error = '最大值必须大于最小值'
            } else if (values.step <= 0) {
              error = '步长必须大于0'
            } else {
              let s = (values.maxValue - values.minValue) / values.step
              if (s !== parseInt(s)) {
                error = '步长必须被 (max - min) 整除'
            if (sessionStorage.getItem('appType') === 'mob') {
              if (values.maxValue <= values.minValue) {
                error = '最大值必须大于最小值'
              } else if (values.step <= 0) {
                error = '步长必须大于0'
              } else {
                let s = (values.maxValue - values.minValue) / values.step
                if (s !== parseInt(s)) {
                  error = '步长必须被 (max - min) 整除'
                }
              }
            }
            if (!error && values.initval) {
              if (!error && values.initval) {
                let vals = values.initval.split(',')
                if (vals.length !== 2) {
                  error = '初始值设置错误!'
                } else if (isNaN(parseFloat(vals[0])) || isNaN(parseFloat(vals[1]))) {
                  error = '初始值设置错误!'
                } else {
                  let start = parseFloat(vals[0])
                  let end = parseFloat(vals[1])
                  let s = (values.maxValue - start) / values.step
                  let e = (values.maxValue - end) / values.step
                  if (start > end || start < values.minValue || end > values.maxValue) {
                    error = '初始值设置错误!'
                  } else if (s !== parseInt(s) || e !== parseInt(e)) {
                    error = '初始值设置错误!'
                  }
                }
              }
            } else if (values.initval) {
              let vals = values.initval.split(',')
              if (vals.length !== 2) {
                error = '初始值设置错误!'
              } else if (isNaN(parseFloat(vals[0])) || isNaN(parseFloat(vals[1]))) {
                error = '初始值设置错误!'
              } else {
                let start = parseFloat(vals[0])
                let end = parseFloat(vals[1])
                let s = (values.maxValue - start) / values.step
                let e = (values.maxValue - end) / values.step
                if (start > end || start < values.minValue || end > values.maxValue) {
                  error = '初始值设置错误!'
                } else if (s !== parseInt(s) || e !== parseInt(e)) {
                  error = '初始值设置错误!'
                }
              }
            }
            if (error) {
              notification.warning({
                top: 92,
@@ -697,34 +882,21 @@
              })
              return
            }
          } else if (values.type === 'switch' || values.type === 'check') {
            values.initval = values.initval === values.openVal ? values.openVal : values.closeVal
          }
          if (isvalid) {
            ['linkField', 'valueField', 'valueText', 'orderBy'].forEach(item => {
              if (values[item]) {
                values[item] = values[item].replace(/\s* | \t* | \v* | \r*/ig, '')
              }
            })
            let error = Utils.verifySql(values.dataSource)
            if (error) {
              notification.warning({
                top: 92,
                message: '数据源中不可使用' + error,
                duration: 5
              })
              return
          ['linkField', 'valueField', 'valueText', 'orderBy'].forEach(item => {
            if (values[item]) {
              values[item] = values[item].replace(/\s+|\t+|\v+|\r+/ig, '')
            }
          })
            resolve(values)
          } else {
            notification.warning({
              top: 92,
              message: this.props.dict['model.form.selectItem.error'],
              duration: 5
            })
          }
          let pass = checkSQL(values.dataSource)
          if (!pass) return
          resolve(values)
        } else {
          reject(err)
        }