king
2020-01-08 669d7cc31eb3728ad09bfb7ce6e615f5c571c14e
2020-01-08
12个文件已修改
12个文件已添加
6067 ■■■■■ 已修改文件
src/tabviews/commontable/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/tableshare/actionList/index.jsx 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/modalform/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/source.jsx 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/actionform/index.jsx 510 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/actionform/index.scss 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/index.jsx 3132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/index.scss 515 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/settingform/index.jsx 312 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/settingform/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/source.jsx 375 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/tabdragelement/card.jsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/tabdragelement/index.jsx 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/tabdragelement/index.scss 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/tabform/index.jsx 227 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalviewconfig/tabform/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/verifycard/billcodeform/index.jsx 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/verifycard/customform/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/verifycard/index.jsx 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/verifycard/uniqueform/index.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/tableshare/verifycard/voucherform/index.jsx 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 163 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx
@@ -693,8 +693,8 @@
          <div className="main-table-box">
            {isLinkMain ?
              <div className="pickchange">
                {setting.tableType === 'checkbox' ? <Switch title="单选切换" checkedChildren="多" unCheckedChildren="单" defaultChecked={setsingle} onChange={this.checkChange} /> : null}
                {this.state.BIDs.mainTable && (setting.tableType === 'radio' || setsingle) ? <Switch title="收起" checkedChildren="关" unCheckedChildren="开" defaultChecked={pickup} onChange={this.pickupChange} /> : null}
                {setting.tableType === 'checkbox' ? <Switch title="单选切换" checkedChildren="单" unCheckedChildren="多" defaultChecked={setsingle} onChange={this.checkChange} /> : null}
                {this.state.BIDs.mainTable && (setting.tableType === 'radio' || setsingle) ? <Switch title="收起" checkedChildren="开" unCheckedChildren="关" defaultChecked={pickup} onChange={this.pickupChange} /> : null}
              </div> : null
            }
            <MainTable
src/tabviews/tableshare/actionList/index.jsx
@@ -131,18 +131,31 @@
        return
      }
      // 执行方式为多行拼接,且打开方式为表单时,会转为循环发送请求
      // 打开方式为模态框,使用内部函数添加
      if (
        btn.Ot === 'notRequired' ||
        btn.Ot === 'requiredSgl' ||
        (btn.Ot === 'requiredOnce' && btn.OpenType !== 'pop') ||
        (btn.OpenType === 'pop' && !btn.innerFunc && btn.sql && btn.sqlType === 'insert')
      ) {
        // 创建凭证时,需要选择行时
        if (data.length === 0 && !btn.innerFunc && btn.verify && btn.verify.voucher && btn.verify.voucher.enabled) {
          notification.warning({
            top: 92,
            message: '使用创建凭证函数,需要选择行!',
            duration: 10
          })
          return
        }
        let param = { // 系统存储过程
          func: 'sPC_TableData_InUpDe',
          BID: this.props.BID
        }
        if (setting.primaryKey) {
        if (setting.primaryKey) { // 主键值预设,从表格数据第一项选取
          param[setting.primaryKey] = (data[0] && data[0][setting.primaryKey]) || ''
        }
@@ -152,7 +165,7 @@
            
          } else if (btn.Ot === 'requiredSgl') {
            ID = data[0][setting.primaryKey]
          } else if (btn.Ot === 'requiredOnce') {
          } else if (btn.Ot === 'requiredOnce') { // id值拼接
            let ids = data.map(d => { return d[setting.primaryKey]})
            ID = ids.join(',')
          }
@@ -161,10 +174,10 @@
            param[setting.primaryKey] = ID
          }
          if (btn.innerFunc) {
          if (btn.innerFunc) { // 使用自定义函数
            param.func = btn.innerFunc
          } else if (btn.sql) {
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, '', param[setting.primaryKey])) // 数据源
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, '', param[setting.primaryKey], data[0])) // 数据源
            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
            param.secretkey = Utils.encrypt(param.LText, param.timestamp)
          }
@@ -176,16 +189,16 @@
              param[_data.key] = _data.value
            })
          } else if (btn.sql && btn.sqlType === 'insert') {
          } else if (btn.sql && btn.sqlType === 'insert') { // 系统函数添加时,生成uuid
            if (setting.primaryKey) {
              param[setting.primaryKey] = Utils.getguid()
            }
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey])) // 数据源
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey], data[0])) // 数据源
            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
            param.secretkey = Utils.encrypt(param.LText, param.timestamp)
          } else if (btn.sql) {
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey])) // 数据源
            param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey], data[0])) // 数据源
            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
            param.secretkey = Utils.encrypt(param.LText, param.timestamp)
          }
@@ -216,7 +229,7 @@
            if (btn.innerFunc) {
              param.func = btn.innerFunc
            } else if (btn.sql) {
              param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, '', param[setting.primaryKey])) // 数据源
              param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, '', param[setting.primaryKey], cell)) // 数据源
              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
              param.secretkey = Utils.encrypt(param.LText, param.timestamp)
            }
@@ -232,7 +245,7 @@
              if (setting.primaryKey) {
                param[setting.primaryKey] = cell[setting.primaryKey]
              }
              param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey])) // 数据源
              param.LText = Utils.formatOptions(Utils.getSysDefaultSql(btn, setting, formdata, param[setting.primaryKey], cell)) // 数据源
              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
              param.secretkey = Utils.encrypt(param.LText, param.timestamp)
            }
@@ -666,6 +679,14 @@
    const { configMap, execAction } = this.state
    let subfields = []
    if (execAction.setting.display === 'prompt') { // 如果表单以是否框展示,不请求下拉菜单信息
      this.setState({
        btnloading: false
      })
      this.modelconfirm()
      return
    }
    if (execAction.groups.length > 0) {
      execAction.groups.forEach(group => {
        group.sublist.forEach(field => {
@@ -788,6 +809,37 @@
    })
  }
  modelconfirm = () => {
    const { execAction, tabledata } = this.state
    let _this = this
    let _fields = []
    if (execAction.groups.length > 0) {
      execAction.groups.forEach(group => {
        _fields = [..._fields, ...group.sublist]
      })
    } else {
      _fields = execAction.fields
    }
    let result = _fields.map(item => {
      return {
        key: item.field,
        readonly: item.readonly === 'true',
        type: item.type,
        value: item.initval
      }
    })
    confirm({
      title: this.props.dict['main.action.confirm.tip'],
      onOk() {
        return new Promise(resolve => {
          _this.execSubmit(execAction, tabledata, resolve, result)
        })
      },
      onCancel() {}
    })
  }
  /**
   * @description 显示模态框
   */
src/templates/modalconfig/index.jsx
@@ -53,9 +53,7 @@
    selectedTables: [],    // 已选表名
    originConfig: null,    // 原始菜单
    groupVisible: false,   // 全局配置模态框
    curgroup: null,        // 当前组,新建或编辑
    formItems: [],         // 表单可选类型
    billCodes: null        // 按钮中定义的单号生成函数字段集
    curgroup: null         // 当前组,新建或编辑
  }
  /**
@@ -90,21 +88,9 @@
    // 主菜单已有选择的表名,模态框没有表名时,复制主菜单表名
    _config.tables = _config.tables.length === 0 ? _menu.tables : _config.tables
    let _formItems = JSON.parse(JSON.stringify((Source.searchItems)))
    if (editAction && editAction.intertype === 'inner' && !editAction.innerFunc) {
      _formItems.push({
        type: 'form',
        label: '函数变量',
        subType: 'funcvar',
        url: ''
      })
    }
    this.setState({
      menu: _menu,
      config: _config,
      formItems: _formItems,
      billCodes: editAction && editAction.verify && editAction.verify.billcodes ? editAction.verify.billcodes : [],
      selectedTables: _config.tables || [],
      originConfig: JSON.parse(JSON.stringify(_config)),
      modalformlist: [
@@ -328,8 +314,6 @@
   * 3、设置编辑参数项-formlist
   */
  handleForm = (card) => {
    const { billCodes } = this.state
    this.setState({
      visible: true,
      card: card,
@@ -515,18 +499,6 @@
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'select',
          key: 'linkfield',
          label: '关联变量',
          initVal: card.linkfield || '',
          required: true,
          options: billCodes.map(code => {
            code.value = code.field
            code.text = code.field
            return code
          })
        }
      ]
    })
@@ -613,7 +585,7 @@
  submitConfig = () => {
    const { editAction } = this.props
    const { config, menu } = this.state
    if ((!config.groups[0] && !config.fields[0]) || (config.fields[0] && config.fields[0].origin)) {
      notification.warning({
        top: 92,
@@ -621,6 +593,27 @@
        duration: 10
      })
      return
    }
    if (config.setting.display === 'prompt') {
      let _fields = []
      if (config.groups.length > 0) {
        config.groups.forEach(group => {
          _fields = [..._fields, ...group.sublist]
        })
      } else {
        _fields = config.fields
      }
      let readfields = _fields.filter(item => item.initval || item.initval === 0)
      if (readfields.length < _fields.length) {
        notification.warning({
          top: 92,
          message: '以《是否框》显示的弹框,所有表单必须含有默认值!',
          duration: 10
        })
        return
      }
    }
    let _LongParam = ''
@@ -1088,7 +1081,7 @@
  }
  render () {
    const { menu, config, formItems } = this.state
    const { menu, config } = this.state
    return (
      <div className="modal-form-board">
@@ -1135,7 +1128,7 @@
              </Panel>
              <Panel header={this.state.dict['header.menu.form']} key="1">
                <div className="search-element">
                  {formItems.map((item, index) => {
                  {Source.searchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
@@ -1221,7 +1214,6 @@
          {<ModalForm
            dict={this.state.dict}
            card={this.state.card}
            billCodes={this.state.billCodes}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.formRef = inst}
          />}
src/templates/modalconfig/modalform/index.jsx
@@ -11,8 +11,7 @@
  static propTpyes = {
    dict: PropTypes.object, // 字典项
    formlist: PropTypes.any,
    card: PropTypes.object,
    billCodes: PropTypes.array
    card: PropTypes.object
  }
  state = {
@@ -23,7 +22,7 @@
  UNSAFE_componentWillMount () {
    let formlist = JSON.parse(JSON.stringify(this.props.formlist))
    console.log(formlist)
    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
    let resourceType = formlist.filter(cell => cell.key === 'resourceType')[0].initVal
    let _options = ['label', 'field', 'initval', 'type', 'readonly', 'required'] // 默认显示项
@@ -45,9 +44,9 @@
    if (type === 'link') { // 关联类型、增加关联字段
      _options = [..._options, 'linkField']
    }
    console.log(type)
    if (type === 'funcvar') {
      _options = ['label', 'field', 'linkfield']
    if (type === 'funcvar') { // 设置为函数变量时,不需要其他信息
      _options = ['label', 'field', 'type']
    }
    
    this.setState({
@@ -103,7 +102,7 @@
      }
      if (value === 'funcvar') {
        _options = ['label', 'field', 'linkfield']
        _options = ['label', 'field', 'type']
      }
      
      this.setState({
@@ -341,12 +340,10 @@
            }
          } else if ((values.type === 'multiselect' || values.type === 'select' || values.type === 'link') && values.resourceType === '1') {
            values.options = []
          }
          if (!values.type) {
            values.type = 'funcvar'
          } else if (values.type === 'funcvar') { // 函数变量为只读元素
            values.readonly = 'true'
          }
          if (isvalid) {
            resolve(values)
          } else {
src/templates/modalconfig/settingform/index.jsx
@@ -154,6 +154,18 @@
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="显示方式">
              {getFieldDecorator('display', {
                initialValue: config.setting.display || 'modal'
              })(
                <Radio.Group>
                  <Radio value="modal">模态框</Radio>
                  <Radio value="prompt">是否框</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
src/templates/modalconfig/source.jsx
@@ -14,7 +14,8 @@
      container: 'tab',
      focus: '',
      finish: 'close',
      clickouter: 'unclose'
      clickouter: 'unclose',
      display: 'modal'
    },
    tables: [],
    groups: [],
@@ -119,6 +120,12 @@
      label: '时间框(秒)',
      subType: 'datetime',
      url: ''
    },
    {
      type: 'form',
      label: '函数变量',
      subType: 'funcvar',
      url: ''
    }
  ]
}
src/templates/modalviewconfig/actionform/index.jsx
New file
@@ -0,0 +1,510 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Select, Icon, Radio, notification, Tooltip, InputNumber } from 'antd'
import { btnIcons, btnClasses } from '@/utils/option.js'
import Utils from '@/utils/utils.js'
import './index.scss'
const { TextArea } = Input
class MainSearch extends Component {
  static propTpyes = {
    dict: PropTypes.object,  // 字典项
    formlist: PropTypes.any, // 表单信息
    card: PropTypes.any,     // 按钮信息
    tabs: PropTypes.array    // 所有标签页
  }
  state = {
    formlist: null,  // 表单信息
    openType: null,  // 打开方式
    interType: null, // 接口类型:内部、外部
    position: null,  // 按钮位置
    reqOptionSgl: [{
      value: 'requiredSgl',
      text: this.props.dict['header.form.requiredSgl']
    }],
    reqOptions: [{
      value: 'notRequired',
      text: this.props.dict['header.form.notRequired']
    }, {
      value: 'requiredSgl',
      text: this.props.dict['header.form.requiredSgl']
    }],
    reqOptionsMutil: [{
      value: 'notRequired',
      text: this.props.dict['header.form.notRequired']
    }, {
      value: 'requiredSgl',
      text: this.props.dict['header.form.requiredSgl']
    }, {
      value: 'required',
      text: this.props.dict['header.form.required']
    }, {
      value: 'requiredOnce',
      text: this.props.dict['header.form.requiredOnce']
    }],
    insertUpdateOptions: [{
      value: '',
      text: this.props.dict['header.form.empty']
    }, {
      value: 'insert',
      text: this.props.dict['header.form.action.insert']
    }, {
      value: 'update',
      text: this.props.dict['header.form.action.update']
    }],
    deleteOptions: [{
      value: '',
      text: this.props.dict['header.form.empty']
    }, {
      value: 'LogicDelete',
      text: this.props.dict['header.form.action.LogicDelete']
    }, {
      value: 'delete',
      text: this.props.dict['header.form.action.delete']
    }]
  }
  UNSAFE_componentWillMount () {
    let _opentype = ''
    let _intertype = ''
    let _position = ''
    let _tabType = ''
    let _linkTab = ''
    let _options = null
    this.props.formlist.forEach(form => {
      if (form.key === 'OpenType') {
        _opentype = form.initVal
      } else if (form.key === 'intertype') {
        _intertype = form.initVal
      } else if (form.key === 'position') {
        _position = form.initVal
      } else if (form.key === 'tabType') {
        _tabType = form.initVal
      } else if (form.key === 'linkTab') {
        _linkTab = form.initVal
      }
    })
    let _tabs = this.props.tabs.filter(tab => tab.type === _tabType)
    let initTab = _tabs.filter(tab => tab.uuid === _linkTab)[0]
    if (_opentype === 'innerpage') {                                     // 新页面(内部),可选模板
      _options = ['label', 'Ot', 'OpenType', 'pageTemplate', 'icon', 'class', 'position']
    } else if (_opentype === 'outerpage') {                              // 新页面(外部),需要页面地址
      _options = ['label', 'Ot', 'OpenType', 'url', 'icon', 'class', 'position']
    } else if (_opentype === 'blank' || _opentype === 'tab') {           // 新标签或当前页面替换
      _options = ['label', 'Ot', 'OpenType', 'icon', 'class', 'position']
    } else if (_opentype === 'popview') {                                // 模态框标签页
      _options = ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'tabType', 'linkTab', 'popClose']
    } else if (_opentype === 'excelIn' || _opentype === 'excelOut') {    // 导入导出
      if (_intertype === 'outer') {
        _options = ['label', 'OpenType', 'intertype', 'innerFunc', 'interface', 'outerFunc', 'callbackFunc', 'icon', 'class', 'execSuccess', 'execError', 'method']
      } else {
        _options = ['label', 'OpenType', 'intertype', 'innerFunc', 'icon', 'class', 'execSuccess', 'execError']
      }
    } else {
      if (_intertype === 'outer') {
        _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'interface', 'outerFunc', 'callbackFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'method']
      } else {
        _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'sql', 'sqlType']
      }
    }
    this.setState({
      openType: _opentype,
      interType: _intertype,
      position: _position,
      formlist: this.props.formlist.map(item => {
        if (item.key === 'class') {
          item.options = btnClasses
        } else if (item.key === 'icon') {
          item.options = btnIcons
        } else if (item.key === 'Ot') {
          if (_opentype === 'innerpage' || _position === 'grid') {
            item.options = this.state.reqOptionSgl
          } else if (['outerpage', 'blank', 'tab', 'popview'].includes(_opentype)) {
            item.options = this.state.reqOptions
          } else {
            item.options = this.state.reqOptionsMutil
          }
        } else if (item.key === 'sqlType') {
          if (['prompt', 'exec'].includes(_opentype)) {
            item.options = this.state.deleteOptions
          } else {
            item.options = this.state.insertUpdateOptions
          }
        } else if (item.key === 'linkTab') {
          item.options = [
            {
              value: '',
              text: '新建'
            },
            ..._tabs
          ]
          if (!initTab) {
            item.initVal = ''
          }
        }
        item.hidden = !_options.includes(item.key)
        return item
      })
    })
  }
  componentDidMount () {
    const { card } = this.props
    if (card.focus) {
      try {
        let _form = document.getElementById('label')
        _form.select()
      } catch {
        console.warn('表单focus失败!')
      }
    }
  }
  // 打开方式或显示位置变化
  openTypeChange = (key, value) => {
    if (key === 'OpenType') {
      let _options = null
      if (value === 'innerpage') {
        _options = ['label', 'Ot', 'OpenType', 'pageTemplate', 'icon', 'class', 'position']
      } else if (value === 'outerpage') {
        _options = ['label', 'Ot', 'OpenType', 'url', 'icon', 'class', 'position']
      } else if (value === 'blank' || value === 'tab') {
        _options = ['label', 'Ot', 'OpenType', 'icon', 'class', 'position']
      } else if (value === 'popview') {
        _options = ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'tabType', 'linkTab', 'popClose']
      } else if (value === 'excelIn' || value === 'excelOut') {
        if (this.state.interType === 'outer') {
          _options = ['label', 'OpenType', 'intertype', 'innerFunc', 'interface', 'outerFunc', 'callbackFunc', 'icon', 'class', 'execSuccess', 'execError', 'method']
        } else {
          _options = ['label', 'OpenType', 'intertype', 'innerFunc', 'icon', 'class', 'execSuccess', 'execError']
        }
      } else {
        if (this.state.interType === 'inner') {
          _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'sql', 'sqlType']
        } else {
          _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'interface', 'outerFunc', 'callbackFunc', 'method']
        }
      }
      this.setState({
        openType: value,
        formlist: this.state.formlist.map(item => {
          if (_options) {
            item.hidden = !_options.includes(item.key)
            if (item.key === 'intertype') {
              item.initVal = this.state.interType
            }
          }
          if (item.key === 'Ot') {
            if (value === 'innerpage' || this.state.position === 'grid') {
              item.options = this.state.reqOptionSgl
              item.initVal = 'requiredSgl'
            } else if (['outerpage', 'blank', 'tab', 'popview'].includes(value)) {
              item.options = this.state.reqOptions
              item.initVal = 'requiredSgl'
            } else {
              item.options = this.state.reqOptionsMutil
            }
            item.hidden = true
          } else if (item.key === 'sqlType') {
            if (['prompt', 'exec'].includes(value)) {
              item.options = this.state.deleteOptions
            } else {
              item.options = this.state.insertUpdateOptions
            }
            item.initVal = ''
            item.hidden = true
          }
          return item
        })
      }, () => {
        if (['excelIn', 'excelOut'].includes(value)) return
        this.setState({
          formlist: this.state.formlist.map(item => {
            if (item.key === 'Ot') {
              item.hidden = false
            } else if (item.key === 'sqlType' && ['prompt', 'exec', 'pop'].includes(value)) {
              item.hidden = false
            }
            return item
          })
        })
      })
    } else if (key === 'position') {
      this.setState({
        position: value,
        formlist: this.state.formlist.map(item => {
          if (item.key === 'Ot') {
            if (this.state.openType === 'innerpage' || value === 'grid') {
              item.options = this.state.reqOptionSgl
              item.initVal = 'requiredSgl'
              item.hidden = true
            } else if (['outerpage', 'blank', 'tab', 'popview'].includes(this.state.openType)) {
              item.options = this.state.reqOptions
              item.initVal = 'requiredSgl'
              item.hidden = true
            } else {
              item.options = this.state.reqOptionsMutil
            }
          }
          return item
        })
      }, () => {
        this.setState({
          formlist: this.state.formlist.map(item => {
            if (item.key === 'Ot') {
              item.hidden = false
            }
            return item
          })
        })
      })
    } else if (key === 'tabType') {
      let _tabs = this.props.tabs.filter(tab => tab.type === value)
      this.setState({
        formlist: this.state.formlist.map(item => {
          if (item.key === 'linkTab') {
            item.options = [
              {
                value: '',
                text: '新建'
              },
              ..._tabs
            ]
            item.initVal = ''
            item.hidden = true
          }
          return item
        })
      }, () => {
        this.setState({
          formlist: this.state.formlist.map(item => {
            if (item.key === 'linkTab') {
              item.hidden = false
            }
            return item
          })
        })
      })
    }
  }
  onChange = (e, key) => {
    let value = e.target.value
    if (key === 'intertype') {
      let _options = null
      if (value === 'inner') {
        _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'sql', 'sqlType']
      } else {
        _options = ['label', 'position', 'OpenType', 'intertype', 'innerFunc', 'Ot', 'icon', 'class', 'execSuccess', 'execError', 'interface', 'outerFunc', 'callbackFunc', 'method']
      }
      this.setState({
        interType: value,
        formlist: this.state.formlist.map(item => {
          item.hidden = !_options.includes(item.key)
          return item
        })
      })
    }
  }
  getFields() {
    const { getFieldDecorator } = this.props.form
    const fields = []
    this.state.formlist.forEach((item, index) => {
      if (item.hidden) return
      if (item.type === 'text') { // 文本搜索
        let _rules = []
        if (item.key === 'innerFunc') {
          let str = '^(' + item.fields.join('|') + ')'
          let _patten = new RegExp(str + '[0-9a-zA-Z_]*$', 'g')
          _rules = [{
            pattern: _patten,
            message: '名称只允许包含数字、字母和下划线,且以指定字符开始。'
          }, {
            max: 50,
            message: '内部函数名称不超过50个字符。'
          }]
        }
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal || '',
                rules: [
                  {
                    required: item.readonly ? false : !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  },
                  ..._rules
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') { // 文本搜索
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(<InputNumber min={1} max={10000} precision={0} />)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉搜索
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : 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('winter')}
                >
                  {item.options.map((option, index) =>
                    <Select.Option id={`${index}`} title={option.text} key={`${index}`} value={option.value}>
                      {item.key === 'icon' && option.value && <Icon type={option.value} />} {option.text}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        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 + '!'
                  }
                ]
              })(
                <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 label={item.label} className="textarea">
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(<TextArea rows={4} />)}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.id = this.props.card.id
          values.uuid = this.props.card.uuid
          values.verify = this.props.card.verify || null
          if (values.OpenType === 'excelIn' || values.OpenType === 'excelOut') {
            values.position = 'toolbar'
            values.Ot = 'notRequired'
          } else if (values.OpenType === 'popview' && !values.linkTab) { // 没有关联标签(新建时),创建新标签Id
            values.linkTab = Utils.getuuid()
          }
          if (values.innerFunc === '' && values.sql === '') {
            notification.warning({
              top: 92,
              message: this.props.dict['header.form.actionhelp.datasource'],
              duration: 10
            })
          } else if (values.innerFunc === '' && values.sql !== '' && values.sqlType === '') {
            notification.warning({
              top: 92,
              message: this.props.dict['header.form.actionhelp.sqlType'],
              duration: 10
            })
          } else {
            resolve({
              type: 'action',
              values
            })
          }
        } else {
          reject(err)
        }
      })
    })
  }
  render() {
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 7 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 17 }
      }
    }
    return (
      <Form {...formItemLayout} className="ant-advanced-search-form commontable-action-form" id="winter">
        <Row gutter={24}>{this.getFields()}</Row>
      </Form>
    )
  }
}
export default Form.create()(MainSearch)
src/templates/modalviewconfig/actionform/index.scss
New file
@@ -0,0 +1,33 @@
.ant-advanced-search-form.commontable-action-form {
  min-height: 190px;
  .superconfig {
    color: #1890ff;
    cursor: pointer;
  }
  .textarea {
    .ant-col-sm-7 {
      width: 14%;
    }
    .ant-col-sm-17 {
      width: 86%;
    }
  }
  .ant-input-number {
    width: 100%;
  }
  .anticon-question-circle {
    color: #c49f47;
    position: relative;
    left: -3px;
  }
  .with-button {
    .ant-form-item-control-wrapper {
      padding-right: 63px;
    }
    .ant-btn {
      position: absolute;
      right: 12px;
      top: 4.5px;
    }
  }
}
src/templates/modalviewconfig/index.jsx
New file
@@ -0,0 +1,3132 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import { Button, Card, Modal, Collapse, notification, Spin, Select, List, Icon, Empty, Switch, Tooltip } from 'antd'
import moment from 'moment'
import ActionForm from './actionform'
import SettingForm from './settingform'
import TabForm from './tabform'
import TabDragElement from './tabdragelement'
import Api from '@/api'
import SearchForm from '@/templates/tableshare/searchform'
import ColumnForm from '@/templates/tableshare/columnform'
import DragElement from '@/templates/tableshare/dragelement'
import ColspanForm from '@/templates/tableshare/colspanform'
import GridBtnForm from '@/templates/tableshare/gridbtnform'
import EditCard from '@/templates/tableshare/editcard'
import VerifyCard from '@/templates/tableshare/verifycard'
import MenuForm from '@/templates/tableshare/menuform'
import SourceElement from '@/templates/tableshare/dragelement/source'
import zhCN from '@/locales/zh-CN/comtable.js'
import enUS from '@/locales/en-US/comtable.js'
import Utils from '@/utils/utils.js'
import Source from './source'
import './index.scss'
const { Panel } = Collapse
const { Option } = Select
const { confirm } = Modal
const CommonDict = (!localStorage.getItem('lang') || localStorage.getItem('lang') === 'zh-CN') ? zhCN : enUS
class ComTableConfig extends Component {
  static propTpyes = {
    type: PropTypes.string,
    menu: PropTypes.any,
    reloadmenu: PropTypes.func,
    handleConfig: PropTypes.func,
    handleSubConfig: PropTypes.func,
    supMenuList: PropTypes.array
  }
  state = {
    dict: CommonDict,        // 字典
    config: null,            // 页面配置
    visible: false,          // 搜索条件、按钮、显示列,模态框显示控制
    modalTitle: '',          // 模态框的标题
    tableVisible: false,     // 数据表字段模态框
    addType: '',             // 添加类型-搜索条件或显示列
    tableColumns: [],        // 表格显示列
    fields: null,            // 搜索条件及显示列,可选字段
    menuformlist: null,      // 基本信息表单字段
    formlist: null,          // 搜索条件、按钮、显示列表单字段
    formtemp: '',            // 表单类型,显示列、按钮、搜索条件
    card: null,              // 编辑元素
    searchloading: false,    // 搜索条件加载中
    actionloading: false,    // 按钮加载中
    columnsloading: false,   // 显示列加载中
    tabloading: false,       // 标签页加载中
    menuloading: false,      // 菜单保存中
    menucloseloading: false, // 菜单关闭时,选择保存
    loading: false,          // 加载中,页面spin
    settingVisible: false,   // 全局配置模态框
    closeVisible: false,     // 关闭模态框
    tables: [],              // 可用表名
    selectedTables: [],      // 已选表名
    originMenu: null,        // 原始菜单
    originActions: null,     // 原始按钮信息,使用已有用户模板
    delActions: [],          // 删除按钮列表
    funcLoading: false,      // 存储过程创建中
    showColumnName: false,   // 显示列字段名控制
    tabviews: [],            // 所有标签页
    profileVisible: false    // 验证信息模态框
  }
  /**
   * @description 数据预处理
   * 1、设置页面配置信息,新建或无配置信息时(切换模板后无配置信息),使用模板默认配置
   * 2、设置操作类型、原始菜单信息(每次保存后重置)、已使用表及基本信息表单
   */
  UNSAFE_componentWillMount () {
    const { menu } = this.props
    let _LongParam = menu.LongParam
    let _config = ''
    if (!_LongParam) {
      _config = JSON.parse(JSON.stringify(Source.baseConfig))
      if (!menu.isSubtable) { // 不是选择主子表时,隐藏标签页
        _config.tabs = []
      }
    } else {
      _config = _LongParam
    }
    let _oriActions = []
    if (_config.type === 'user') {
      _config.action = _config.action.map(item => {
        let uuid = Utils.getuuid()
        if (item.OpenType === 'pop') { // 含有子配置项的按钮。。。
          _oriActions.push({
            prebtn: JSON.parse(JSON.stringify(item)),
            curuuid: uuid,
            Template: 'Modal'
          })
        }
        item.uuid = uuid
        return item
      })
    }
    _config.tabs = _config.tabs || []
    _config.tabgroups = _config.tabgroups || ['tabs']
    _config.setting.subtabs = _config.setting.subtabs || []
    this.setState({
      config: _config,
      originActions: _oriActions,
      originMenu: JSON.parse(JSON.stringify(menu)),
      selectedTables: _config.tables || [],
      menuformlist: [
        {
          type: 'select',
          key: 'parentId',
          label: this.state.dict['header.menu.supMenu'],
          initVal: menu.ParentID,
          required: true,
          readonly: false,
          options: this.props.supMenuList
        },
        {
          type: 'text',
          key: 'menuName',
          label: this.state.dict['header.menu.menuName'],
          initVal: menu.MenuName,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'menuNo',
          label: this.state.dict['header.menu.menuNo'],
          initVal: menu.MenuNo,
          required: true,
          readonly: false
        },
        {
          type: 'select',
          key: 'opentype',
          label: this.state.dict['header.menu.openType'],
          initVal: menu.PageParam.OpenType,
          required: true,
          options: [{
            MenuID: 'newtab',
            text: this.state.dict['header.form.tab']
          }, {
            MenuID: 'newpage',
            text: this.state.dict['header.form.newpage']
          }, {
            MenuID: 'currenttab',
            text: this.state.dict['header.form.currenttab']
          }]
        }
      ]
    })
  }
  /**
   * @description 加载完成后
   * 1、获取系统可使用表
   * 2、根据配置信息中已使用表获取相关字段信息
   * 3、获取所有标签页信息
   */
  componentDidMount () {
    let param = {
      func: 'sPC_Get_SelectedList',
      LText: 'select TbName ,Remark from sDataDictionary where IsKey!=\'\' and Deleted =0',
      obj_name: 'data',
      arr_field: 'TbName,Remark'
    }
    param.LText = Utils.formatOptions(param.LText)
    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
    Api.getSystemConfig(param).then(res => {
      if (res.status) {
        this.setState({
          tables: res.data
        })
      } else {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 10
        })
      }
    })
    let deffers = this.state.selectedTables.map(item => {
      return new Promise(resolve => {
        Api.getSystemConfig({func: 'sPC_Get_FieldName', TBName: item.TbName}).then(res => {
          res.TBName = item.TbName
          resolve(res)
        })
      })
    })
    Promise.all(deffers).then(response => {
      let _columns = []
      response.forEach(res => {
        if (res.status) {
          let tabmsg = {
            tableName: res.TBName,
            columns: res.FDName.map(item => {
              let _type = item.FieldType.toLowerCase()
              let _decimal = 0
              if (/^nvarchar/.test(_type)) {
                _type = 'text'
              } else if (/^int/.test(_type)) {
                _type = 'number'
              } else if (/^decimal/.test(_type)) {
                _decimal = _type.split(',')[1]
                _decimal = parseInt(_decimal)
                if (_decimal > 4) {
                  _decimal = 4
                }
                _type = 'number'
              } else if (/^decimal/.test(_type)) {
                _decimal = _type.split(',')[1]
                _decimal = parseInt(_decimal)
                if (_decimal > 4) {
                  _decimal = 4
                }
                _type = 'number'
              } else if (/^datetime/.test(_type)) {
                _type = 'datetime'
              } else if (/^date/.test(_type)) {
                _type = 'date'
              } else {
                _type = 'text'
              }
              return {
                field: item.FieldName,
                label: item.FieldDec,
                type: _type,
                datatype: _type,
                decimal: _decimal
              }
            })
          }
          _columns.push(tabmsg)
        } else {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
        }
      })
      this.setState({
        tableColumns: _columns
      })
    })
    Api.getSystemConfig({func: 'sPC_Get_UserTemp', TypeCharTwo: 'tab'}).then(res => {
      if (res.status) {
        this.setState({
          tabviews: res.UserTemp.map(temp => {
            return {
              uuid: temp.MenuID,
              value: temp.MenuID,
              text: temp.MenuName,
              type: temp.Template,
              MenuNo: temp.MenuNo
            }
          })
        })
      }
    })
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = (state, callback) => {
      return
    }
  }
  handleList = (type, list, card) => {
    const { config } = this.state
    if (type === 'tabs') { // 标签页调整顺序或添加元素
      if (list.length > config[card.groupId].length) {
        list = list.filter(item => !item.origin)
        this.setState({
          tabloading: true,
          config: {...config, [card.groupId]: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            tabloading: false
          })
          this.handleTab(card)
        })
      } else {
        this.setState({config: {...config, [card.groupId]: list}})
      }
    } else {
      if (list.length > config[type].length) {
        list = list.filter(item => !item.origin)
        this.setState({
          [type + 'loading']: true,
          config: {...config, [type]: list }
        }, () => {
          // 刷新对应的配置信息
          this.setState({
            [type + 'loading']: false
          })
          if (type === 'search') {
            this.handleSearch(card)
          } else if (type === 'action') {
            this.handleAction(card)
          } else if (type === 'columns') {
            this.handleColumn(card)
          } else if (type === 'tabs') {
            this.handleTab(card)
          }
        })
      } else {
        this.setState({config: {...config, [type]: list}})
      }
    }
  }
  handleSearch = (card) => {
    this.setState({
      visible: true,
      formtemp: 'search',
      modalTitle: '编辑-搜索条件',
      card: card,
      formlist: [
        {
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'field',
          label: this.state.dict['header.form.field'],
          initVal: card.field,
          tooltip: '字段名可以使用逗号分隔,进行多字段综合搜索,注:综合搜索仅在文本类型时有效',
          tooltipClass: 'middle',
          required: true,
          rules: [{
            pattern: /^[\u4E00-\u9FA50-9a-zA-Z,_]*$/ig,
            message: '字段名只允许包含数字、字母、汉字以及_'
          }, {
            max: 50,
            message: '字段名最多50个字符!'
          }],
          readonly: false
        },
        {
          type: 'select',
          key: 'type',
          label: this.state.dict['header.form.type'],
          initVal: card.type,
          required: true,
          options: [{
            value: 'text',
            text: this.state.dict['header.form.text']
          }, {
            value: 'select',
            text: this.state.dict['header.form.select']
          }, {
            value: 'multiselect',
            text: this.state.dict['header.form.multiselect']
          }, {
            value: 'link',
            text: this.state.dict['header.form.link']
          }, {
            value: 'date',
            text: this.state.dict['header.form.dateday']
          }, {
            value: 'dateweek',
            text: this.state.dict['header.form.dateweek']
          }, {
            value: 'datemonth',
            text: this.state.dict['header.form.datemonth']
          }, {
            value: 'daterange',
            text: this.state.dict['header.form.daterange']
          }]
        },
        {
          type: 'text',
          key: 'initval',
          label: this.state.dict['header.form.initval'],
          initVal: card.initval,
          required: false
        },
        {
          type: 'radio',
          key: 'resourceType',
          label: this.state.dict['header.form.resourceType'],
          initVal: card.resourceType || '0',
          required: true,
          options: [{
            value: '0',
            text: this.state.dict['header.form.custom']
          }, {
            value: '1',
            text: this.state.dict['header.form.datasource']
          }]
        },
        {
          type: 'radio',
          key: 'setAll',
          label: this.state.dict['header.form.setAll'],
          initVal: card.setAll || 'false',
          options: [{
            value: 'true',
            text: this.state.dict['header.form.true']
          }, {
            value: 'false',
            text: this.state.dict['header.form.false']
          }]
        },
        {
          type: 'textarea',
          key: 'dataSource',
          label: this.state.dict['header.form.datasource'],
          initVal: card.dataSource || '',
          required: true,
          readonly: false
        },
        {
          type: 'options',
          key: 'options',
          label: '',
          initVal: card.options || [],
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'linkField',
          label: this.state.dict['header.form.linkField'],
          initVal: card.linkField || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'valueField',
          label: this.state.dict['header.form.valueField'],
          initVal: card.valueField || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'valueText',
          label: this.state.dict['header.form.valueText'],
          initVal: card.valueText || '',
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'orderBy',
          label: this.state.dict['header.form.orderBy'],
          initVal: card.orderBy || '',
          required: false,
          readonly: false
        },
        {
          type: 'select',
          key: 'orderType',
          label: this.state.dict['header.form.orderType'],
          initVal: card.orderType || 'asc',
          options: [{
            value: 'asc',
            text: this.state.dict['header.form.asc']
          }, {
            value: 'desc',
            text: this.state.dict['header.form.desc']
          }]
        },
        {
          type: 'select',
          key: 'match',
          label: this.state.dict['header.form.match'],
          initVal: card.match || 'like',
          required: true,
          options: [{
            value: 'like',
            text: 'like'
          }, {
            value: 'equal',
            text: 'equal'
          }, {
            value: 'greater',
            text: '>'
          }, {
            value: 'less',
            text: '<'
          }, {
            value: 'greaterequal',
            text: '>='
          }]
        },
        {
          type: 'select',
          key: 'display',
          label: this.state.dict['header.form.display'],
          initVal: card.display || 'dropdown',
          required: true,
          options: [{
            value: 'dropdown',
            text: this.state.dict['header.form.dropdown']
          }, {
            value: 'button',
            text: this.state.dict['header.form.button']
          }]
        }
      ]
    })
  }
  handleAction = (card, type) => {
    let ableField = this.props.permFuncField.join(', ')
    this.setState({
      visible: true,
      formtemp: 'action',
      modalTitle: type === 'copy' ? '复制-按钮' : '编辑-按钮',
      card: card,
      formlist: [
        {
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label,
          required: true,
          readonly: false
        },
        {
          type: 'select',
          key: 'OpenType',
          label: this.state.dict['header.form.openType'],
          initVal: card.OpenType,
          required: true,
          options: [{
            value: 'pop',
            text: this.state.dict['header.form.popform']
          }, {
            value: 'prompt',
            text: this.state.dict['header.form.prompt']
          }, {
            value: 'exec',
            text: this.state.dict['header.form.exec']
          }, {
            value: 'excelIn',
            text: this.state.dict['header.form.excelIn']
          }, {
            value: 'excelOut',
            text: this.state.dict['header.form.excelOut']
          }, {
            value: 'popview',
            text: this.state.dict['header.form.popview']
          }, {
            value: 'tab',
            text: this.state.dict['header.form.tab']
          }, {
            value: 'blank',
            text: this.state.dict['header.form.blank']
          }, {
            value: 'innerpage',
            text: this.state.dict['header.form.newpage.inner']
          }, {
            value: 'outerpage',
            text: this.state.dict['header.form.newpage.outer']
          }]
        }, {
          type: 'select',
          key: 'tabType',
          label: this.state.dict['header.form.tabType'],
          initVal: card.tabType || 'SubTable',
          required: true,
          options: [{
            value: 'SubTable',
            text: this.state.dict['header.menu.tab.subtable']
          }]
        },
        {
          type: 'select',
          key: 'linkTab',
          label: '关联标签',
          initVal: card.linkTab || '',
          required: false,
          options: []
        },
        {
          type: 'select',
          key: 'pageTemplate',
          label: this.state.dict['header.form.pageTemplate'],
          initVal: card.pageTemplate,
          required: true,
          options: []
        },
        {
          type: 'text',
          key: 'url',
          label: this.state.dict['header.form.newpage.url'],
          initVal: card.url || '',
          required: true
        },
        {
          type: 'radio',
          key: 'intertype',
          label: this.state.dict['header.form.intertype'],
          initVal: card.intertype || 'inner',
          required: true,
          options: [{
            value: 'inner',
            text: this.state.dict['header.form.interface.inner']
          }, {
            value: 'outer',
            text: this.state.dict['header.form.interface.outer']
          }]
        },
        {
          type: 'text',
          key: 'innerFunc',
          label: this.state.dict['header.form.innerFunc'],
          initVal: card.innerFunc,
          tooltip: <div>
            <p>内部接口: 可自定义数据处理函数,函数名称需以{ableField}等字符开始;未设置时会调用系统函数,使用系统函数需完善数据源及操作类型;</p>
            <p>外部接口: 可自定义数据处理函数,提交数据经过内部函数处理后,传入外部接口,未设置时,数据会直接传入外部接口。</p>
          </div>,
          fields: this.props.permFuncField,
          tooltipClass: 'middle',
          required: false,
          readonly: false
        },
        {
          type: 'text',
          key: 'interface',
          label: this.state.dict['header.form.interface'],
          initVal: card.interface,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'outerFunc',
          label: this.state.dict['header.form.outerFunc'],
          initVal: card.outerFunc,
          required: false,
          readonly: false
        },
        {
          type: 'text',
          key: 'callbackFunc',
          label: this.state.dict['header.form.callbackFunc'],
          initVal: card.callbackFunc,
          required: false,
          readonly: false
        },
        {
          type: 'select',
          key: 'position',
          label: this.state.dict['header.form.position'],
          initVal: card.position || 'toolbar',
          required: true,
          options: [{
            value: 'toolbar',
            text: this.state.dict['header.form.toolbar']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.grid']
          }]
        },
        {
          type: 'select',
          key: 'Ot',
          label: this.state.dict['header.form.isRequired'],
          initVal: card.Ot || 'requiredSgl',
          required: true,
          options: []
        },
        {
          type: 'select',
          key: 'execSuccess',
          label: this.state.dict['header.form.execSuccess'],
          initVal: card.execSuccess || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'execError',
          label: this.state.dict['header.form.execError'],
          initVal: card.execError || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'popClose',
          label: this.state.dict['header.form.popClose'],
          initVal: card.popClose || 'never',
          required: true,
          options: [{
            value: 'never',
            text: this.state.dict['header.form.refresh.never']
          }, {
            value: 'grid',
            text: this.state.dict['header.form.refresh.grid']
          }, {
            value: 'view',
            text: this.state.dict['header.form.refresh.view']
          }]
        },
        {
          type: 'select',
          key: 'icon',
          label: this.state.dict['header.form.icon'],
          initVal: card.icon,
          required: false,
          options: []
        },
        {
          type: 'select',
          key: 'class',
          label: this.state.dict['header.form.class'],
          initVal: card.class,
          required: false,
          options: []
        },
        {
          type: 'text',
          key: 'sql',
          label: this.state.dict['header.form.datasource'],
          initVal: card.sql || this.state.config.setting.tableName || '',
          tooltip: this.state.dict['header.form.actionhelp.datasource'],
          required: false
        },
        {
          type: 'select',
          key: 'sqlType',
          label: this.state.dict['header.form.action.type'],
          initVal: card.sqlType || '',
          tooltip: this.state.dict['header.form.actionhelp.sqlType'],
          required: false,
          options: []
        }
      ]
    })
  }
  handleColumn = (card) => {
    if (card.type !== 'colspan') {
      this.setState({
        visible: true,
        formtemp: 'columns',
        modalTitle: '编辑-显示列',
        card: card,
        formlist: [
          {
            type: 'text',
            key: 'label',
            label: this.state.dict['header.form.name'],
            initVal: card.label,
            required: true
          },
          {
            type: 'text',
            key: 'field',
            label: this.state.dict['header.form.field'],
            initVal: card.field,
            required: true,
            rules: [{
              pattern: /^[\u4E00-\u9FA50-9a-zA-Z_]*$/ig,
              message: '字段名只允许包含数字、字母、汉字以及_'
            }, {
              max: 50,
              message: '字段名最多50个字符!'
            }],
            readonly: false
          },
          {
            type: 'select',
            key: 'type',
            label: this.state.dict['header.form.type'],
            initVal: card.type,
            required: true,
            options: [{
              value: 'text',
              text: this.state.dict['header.form.text']
            }, {
              value: 'number',
              text: this.state.dict['header.form.number']
            }, {
              value: 'picture',
              text: this.state.dict['header.form.picture']
            }, {
              value: 'textarea',
              text: this.state.dict['header.form.textarea']
            }]
          },
          {
            type: 'select',
            key: 'Align',
            label: this.state.dict['header.form.align'],
            initVal: card.Align,
            required: true,
            options: [{
              value: 'left',
              text: this.state.dict['header.form.alignLeft']
            }, {
              value: 'right',
              text: this.state.dict['header.form.alignRight']
            }, {
              value: 'center',
              text: this.state.dict['header.form.alignCenter']
            }]
          },
          {
            type: 'radio',
            key: 'Hide',
            label: this.state.dict['header.form.Hide'],
            initVal: card.Hide,
            required: true,
            options: [{
              value: 'true',
              text: this.state.dict['header.form.true']
            }, {
              value: 'false',
              text: this.state.dict['header.form.false']
            }]
          },
          {
            type: 'radio',
            key: 'IsSort',
            label: this.state.dict['header.form.IsSort'],
            initVal: card.IsSort,
            required: true,
            options: [{
              value: 'true',
              text: this.state.dict['header.form.true']
            }, {
              value: 'false',
              text: this.state.dict['header.form.false']
            }]
          },
          {
            type: 'number',
            key: 'Width',
            min: 1,
            max: 1000,
            decimal: 0,
            label: this.state.dict['header.form.columnWidth'],
            initVal: card.Width,
            required: true
          },
          {
            type: 'number',
            key: 'decimal',
            min: 0,
            max: 18,
            decimal: 0,
            label: this.state.dict['header.form.decimal'],
            initVal: card.decimal,
            required: false
          },
          {
            type: 'select',
            key: 'format',
            label: this.state.dict['header.form.format'],
            initVal: card.format || '',
            options: [{
              value: '',
              text: this.state.dict['header.form.empty']
            }, {
              value: 'thdSeparator',
              text: this.state.dict['header.form.thdSeparator']
            }],
            required: false
          },
          {
            type: 'text',
            key: 'prefix',
            label: this.state.dict['header.form.prefix'],
            initVal: card.prefix || '',
            required: false,
            readonly: false
          },
          {
            type: 'text',
            key: 'postfix',
            label: this.state.dict['header.form.postfix'],
            initVal: card.postfix || '',
            // tooltip: '后缀值设置为"\\n",表示换行',
            tooltipClass: 'middle',
            required: false,
            readonly: false
          },
          {
            type: 'select',
            key: 'match',
            label: this.state.dict['header.form.match'],
            initVal: card.match || '',
            options: [{
              value: '',
              text: this.state.dict['header.form.empty']
            }, {
              value: '>',
              text: '>'
            }, {
              value: '<',
              text: '<'
            }, {
              value: '>=',
              text: '>='
            }, {
              value: '<=',
              text: '<='
            }],
            required: false
          },
          {
            type: 'text',
            key: 'matchVal',
            min: -Infinity,
            max: Infinity,
            decimal: 0,
            label: this.state.dict['header.form.matchVal'],
            initVal: card.matchVal || '',
            required: false,
            readonly: false
          },
          {
            type: 'select',
            key: 'color',
            label: this.state.dict['header.form.color'],
            initVal: card.color || '',
            options: [{
              value: '',
              text: this.state.dict['header.form.empty']
            }, {
              value: 'red',
              text: '红色(内容)'
            }, {
              value: 'redbg',
              text: '红色(背景)'
            }, {
              value: 'orange',
              text: '橙色(内容)'
            }, {
              value: 'orangebg',
              text: '橙色(背景)'
            }, {
              value: 'green',
              text: '绿色(内容)'
            }, {
              value: 'greenbg',
              text: '绿色(背景)'
            }],
            required: false
          }
        ]
      })
    } else {
      this.setState({
        visible: true,
        formtemp: 'columns',
        modalTitle: '编辑-合并列',
        card: card
      })
    }
  }
  handleTab = (card) => {
    const { config } = this.state
    let index = 0 // 筛选下一组
    config.tabgroups.forEach((groupId, i) => {
      if (groupId === card.groupId) {
        index = i
      }
    })
    let menus = []
    let subtabs = card.subtabs || []
    let nextTabId = config.tabgroups[index + 1]
    if (nextTabId) {
      let _tabMap = new Map()
      let _usedTabMap = new Map()
      config[nextTabId].forEach(tab => { // 下级所有的标签
        menus.push(tab)
        _tabMap.set(tab.uuid, true)
      })
      config[card.groupId].forEach(tab => { // 同级标签已选的下级标签
        if (tab.uuid === card.uuid) return
        tab.subtabs.forEach(subtab => {
          _usedTabMap.set(subtab, true)
        })
      })
      config.setting.subtabs.forEach(subtab => { // 主表已选的下级标签
        _usedTabMap.set(subtab, true)
      })
      subtabs = subtabs.filter(tab => _tabMap.has(tab.uuid) && !_usedTabMap.has(tab.uuid))
      menus = menus.filter(tab => !_usedTabMap.has(tab.uuid))
    } else {
      subtabs = []
    }
    this.setState({
      visible: true,
      formtemp: 'tabs',
      modalTitle: '编辑-标签页',
      card: card,
      formlist: [
        {
          type: 'text',
          key: 'label',
          label: this.state.dict['header.form.name'],
          initVal: card.label || '',
          required: true
        },
        {
          type: 'select',
          key: 'type',
          label: this.state.dict['header.form.tabType'],
          initVal: card.type || 'SubTable',
          required: true,
          options: [{
            value: 'SubTable',
            text: this.state.dict['header.menu.tab.subtable']
          }]
        },
        {
          type: 'select',
          key: 'linkTab',
          label: '关联标签',
          initVal: card.linkTab || '',
          required: false,
          options: []
        },
        {
          type: 'select',
          key: 'icon',
          label: this.state.dict['header.menu.icon'],
          initVal: card.icon || '',
          required: false,
          options: [{
            value: '',
            text: this.state.dict['header.form.empty']
          }, {
            value: 'table',
            text: 'table'
          }, {
            value: 'bar-chart',
            text: 'bar-chart'
          }, {
            value: 'pie-chart',
            text: 'pie-chart'
          }, {
            value: 'line-chart',
            text: 'line-chart'
          }]
        },
        {
          type: 'mutilselect',
          key: 'subtabs',
          label: '下级标签',
          initVal: subtabs,
          required: false,
          options: menus
        }
      ]
    })
  }
  handleGridBtn = () => {
    this.setState({
      visible: true,
      formtemp: 'gridbtn',
      modalTitle: '编辑-操作列',
    })
  }
  /**
   * @description 搜索、按钮、显示列修改后提交保存
   * 1、搜索条件保存,当类型为下拉框且存在数据源时,将查询条件拼接为sql,并用base64转码
   * 2、按钮包括正常编辑和复制,复制时,按钮列末尾添加
   * 3、添加或编辑列,保存时,如按钮位置设置为表格,则修改操作列显示状态
   */
  handleSubmit = () => {
    const { menu } = this.props
    const { card } = this.state
    let _config = JSON.parse(JSON.stringify(this.state.config))
    if (this.state.formtemp !== 'gridbtn') {
      this.formRef.handleConfirm().then(res => {
        let isupdate = false
        if (res.type === 'action' && card.originCard && res.values.OpenType === 'pop') {
          Api.getSystemConfig({
            func: 'sPC_Get_LongParam',
            MenuID: card.originCard.uuid
          }).then(result => {
            if (result.status && result.LongParam) {
              let param = {
                func: 'sPC_ButtonParam_AddUpt',
                ParentID: menu.MenuID,
                MenuID: res.values.uuid,
                MenuNo: menu.MenuNo,
                Template: 'Modal',
                MenuName: res.values.label,
                PageParam: JSON.stringify({Template: 'Modal'}),
                LongParam: result.LongParam
              }
              Api.getSystemConfig(param).then(response => {
                if (!response.status) {
                  notification.warning({
                    top: 92,
                    message: response.message,
                    duration: 10
                  })
                }
              })
            }
          })
        }
        if (res.type !== 'tabs') {
          _config[res.type] = _config[res.type].map(item => {
            if (item.uuid === res.values.uuid) {
              isupdate = true
              return res.values
            } else {
              return item
            }
          })
          _config[res.type] = _config[res.type].filter(item => !item.origin)
          if (!isupdate) { // 操作不是修改,添加元素至列表
            _config[res.type].push(res.values)
          }
        } else { // 标签页的添加与修改
          _config[res.values.groupId] = _config[res.values.groupId].map(item => {
            if (item.uuid === res.values.uuid) {
              isupdate = true
              return res.values
            } else {
              return item
            }
          })
          _config[res.values.groupId] = _config[res.values.groupId].filter(item => !item.origin)
          if (!isupdate) { // 操作不是修改,添加元素至列表
            _config[res.values.groupId].push(res.values)
          }
        }
        if (res.type === 'action') {
          let gridbtn = _config.action.filter(act => act.position === 'grid')
          let _display = false
          if (gridbtn.length > 0) {
            _display = true
          }
          if (_config.gridBtn) {
            _config.gridBtn.display = _display
          } else {
            _config.gridBtn = {
              display: _display,
              Align: 'center',
              IsSort: 'false',
              uuid: Utils.getuuid(),
              label: this.state.dict['header.form.column.action'],
              type: 'action',
              style: 'button',
              show: 'horizontal',
              Width: 120
            }
          }
        }
        this.setState({
          config: _config,
          searchloading: true,
          actionloading: true,
          columnsloading: true,
          tabloading: true,
          visible: false
        }, () => {
          this.setState({
            searchloading: false,
            actionloading: false,
            columnsloading: false,
            tabloading: false
          })
        })
      })
    } else {
      this.formRef.handleConfirm().then(res => {
        _config.gridBtn = res
        this.setState({
          config: _config,
          visible: false
        })
      })
    }
  }
  /**
   * @description 创建按钮存储过程
   */
  creatFunc = () => {
    const { menu } = this.props
    let _config = JSON.parse(JSON.stringify(this.state.config))
    this.formRef.handleConfirm().then(res => {
      let btn = res.values  // 按钮信息
      let newLText = ''     // 创建存储过程sql
      let DelText = ''      // 删除存储过程sql
      let isExit = false    // 存储过程是否存在
      let sysTVPText = ''   // 已有的存储过程语句(云端)
      let localTVPText = '' // 已有的存储过程语句(本地)
      // 创建存储过程,必须填写内部函数名
      if (!btn.innerFunc) {
        notification.warning({
          top: 92,
          message: '请填写内部函数!',
          duration: 10
        })
        return
      }
      // 创建中
      this.setState({
        funcLoading: true
      })
      new Promise(resolve => {
        // 弹窗(表单)类按钮,先获取按钮配置信息,如果尚未配置按钮则会报错并终止。
        // 获取信息后生成删除和创建存储过程的语句
        if (btn.OpenType === 'pop') {
          Api.getSystemConfig({
            func: 'sPC_Get_LongParam',
            MenuID: btn.uuid
          }).then(res => {
            let _LongParam = ''
            if (res.status && res.LongParam) {
              _LongParam = window.decodeURIComponent(window.atob(res.LongParam))
              try {
                _LongParam = JSON.parse(_LongParam)
              } catch (e) {
                _LongParam = ''
              }
            }
            if (_LongParam) {
              let fields = []
              if (_LongParam.groups.length > 0) {
                _LongParam.groups.forEach(group => {
                  fields = [...fields, ...group.sublist]
                })
              } else {
                fields = _LongParam.fields
              }
              let _param = {
                funcName: btn.innerFunc,
                name: _config.setting.tableName || '',
                fields: fields,
                menuNo: menu.MenuNo
              }
              newLText = Utils.formatOptions(Utils.getfunc(_param, btn, menu, _config))
              DelText = Utils.formatOptions(Utils.dropfunc(_param.funcName))
              resolve(true)
            } else {
              resolve(false)
              notification.warning({
                top: 92,
                message: '弹窗(表单)按钮,请先配置表单信息!',
                duration: 10
              })
            }
          })
        } else {
          let _param = {
            funcName: btn.innerFunc,
            name: _config.setting.tableName || '',
            fields: '',
            menuNo: menu.MenuNo
          }
          newLText = Utils.formatOptions(Utils.getfunc(_param, btn, menu, _config))
          DelText = Utils.formatOptions(Utils.dropfunc(_param.funcName))
          resolve(true)
        }
      }).then(res => {
        // 获取云端及本地,是否已存在该存储过程的信息
        if (res === false) return res
        let sysDefer = new Promise(resolve => {
          Api.getSystemConfig({
            func: 'sPC_Get_TVP', // 云端获取存储结果
            TVPName: btn.innerFunc
          }).then(result => {
            resolve(result)
          })
        })
        let localDefer = new Promise(resolve => {
          let _param = { // 获取本地存储过程信息
            func: 's_get_userproc',
            LText: btn.innerFunc
          }
          _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
          _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
          Api.getLocalConfig(_param).then(result => {
            resolve(result)
          })
        })
        return Promise.all([sysDefer, localDefer])
      }).then(res => {
        // 云端结果与新语句不同时,更新云端信息
        if (res === false) return res
        let isError = false
        res.forEach((result, index) => {
          if (!result.status) {
            notification.warning({
              top: 92,
              message: result.message,
              duration: 10
            })
            isError = true
          } else if (index === 0) {
            sysTVPText = result.TVPText
          } else {
            if (result.Ltext) { // 本地存储过程是否存在
              isExit = true
            }
            localTVPText = Utils.formatOptions(result.Ltext)
          }
        })
        if (isError) return false
        if ((newLText === localTVPText) && (newLText === sysTVPText)) {
          return 'drop'
        } else if (!localTVPText || (localTVPText === sysTVPText)) {
          // 本地存储过程不存在,将新的存储过程更新至云端
          return Api.getSystemConfig({
            func: 'sPC_TVP_InUp',
            TVPName: btn.innerFunc,
            TVPText: newLText,
            TypeName: 'P'
          })
        } else {
          return new Promise(resolve => {
            Api.getSystemConfig({ // 添加现有的本地存储过程至云端
              func: 'sPC_TVP_InUp',
              TVPName: btn.innerFunc,
              TVPText: localTVPText,
              TypeName: 'P'
            }).then(result => {
              if (result.status) {
                Api.getSystemConfig({
                  func: 'sPC_TVP_InUp', // 添加最新的存储过程至云端
                  TVPName: btn.innerFunc,
                  TVPText: newLText,
                  TypeName: 'P'
                }).then(response => {
                  resolve(response)
                })
              } else {
                resolve(result)
              }
            })
          })
        }
      }).then(res => {
        // 云端信息更新后,判断是删除或是直接新建存储过程
        if (res === false || res === 'drop') return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else if (isExit) {
          return 'drop'
        } else {
          return 'create'
        }
      }).then(res => {
        // 删除存储过程
        if (res === false || res === 'create') return res
        let _param = {
          func: 'sPC_TableData_InUpDe',
          LText: DelText,
          TypeCharOne: 'proc' // 删除或创建存储过程
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
        return Api.getLocalConfig(_param)
      }).then(res => {
        // 根据上述操作结果,判断是否新建存储过程
        if (res === false || res === 'create') return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else {
          return 'create'
        }
      }).then(res => {
        // 新建存储过程
        if (res === false) return res
        let _param = {
          func: 'sPC_TableData_InUpDe',
          LText: newLText,
          TypeCharOne: 'proc' // 删除或创建存储过程
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
        return Api.getLocalConfig(_param)
      }).then(res => {
        // 处理新建结果
        if (res === false) return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else {
          notification.success({
            top: 92,
            message: '创建成功',
            duration: 2
          })
          return true
        }
      }).then(res => {
        // 新建成功后,更新页面按钮信息
        if (res === false) {
          this.setState({
            funcLoading: false
          })
          return
        }
        let isupdate = false
        _config.action = _config.action.map(item => {
          if (item.uuid === btn.uuid) {
            isupdate = true
            return btn
          } else {
            return item
          }
        })
        _config.action = _config.action.filter(item => !item.origin)
        if (!isupdate) { // 操作不是修改,添加元素至列表
          _config.action.push(btn)
        }
        let gridbtn = _config.action.filter(act => act.position === 'grid')
        let _display = false
        if (gridbtn.length > 0) {
          _display = true
        }
        if (_config.gridBtn) {
          _config.gridBtn.display = _display
        } else {
          _config.gridBtn = {
            display: _display,
            Align: 'center',
            IsSort: 'false',
            uuid: Utils.getuuid(),
            label: this.state.dict['header.form.column.action'],
            type: 'action',
            style: 'button',
            show: 'horizontal',
            Width: 120
          }
        }
        this.setState({
          config: _config,
          actionloading: true,
          funcLoading: false
        }, () => {
          this.setState({
            actionloading: false
          })
        })
      })
    })
  }
  /**
   * @description 创建表格存储过程
   */
  tableCreatFunc = () => {
    const { menu } = this.props
    let config = JSON.parse(JSON.stringify(this.state.config))
    this.settingRef.handleConfirm().then(res => {
      const setting = res
      if (!(setting.interType === 'inner') || !setting.innerFunc) {
        notification.warning({
          top: 92,
          message: '接口类型为-内部,且存在内部函数时,才可以创建存储过程!',
          duration: 10
        })
        return
      }
      if (setting.dataresource.length > 50 && config.setting.dataresource !== setting.dataresource) {
        let param = {
          func: 's_DataSrc_Save',
          LText: setting.dataresource,
          MenuID: menu.MenuID
        }
        param.LText = Utils.formatOptions(param.LText)
        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
        Api.getLocalConfig(param)
      }
      this.setState({
        funcLoading: true
      })
      let newLText = Utils.formatOptions(Utils.getTableFunc(setting, menu, config)) // 创建存储过程sql
      let DelText = Utils.formatOptions(Utils.dropfunc(setting.innerFunc))          // 删除存储过程sql
      new Promise(resolve => {
        let sysDefer = new Promise(resolve => {
          Api.getSystemConfig({
            func: 'sPC_Get_TVP', // 云端获取存储结果
            TVPName: setting.innerFunc
          }).then(result => {
            if (!result.status) {
              notification.warning({
                top: 92,
                message: result.message,
                duration: 10
              })
              resolve(false)
            } else {
              resolve(result)
            }
          })
        })
        let localDefer = new Promise(resolve => {
          let _param = { // 获取本地存储过程信息
            func: 's_get_userproc',
            LText: setting.innerFunc
          }
          _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
          _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
          Api.getLocalConfig(_param).then(result => {
            if (!result.status) {
              notification.warning({
                top: 92,
                message: result.message,
                duration: 10
              })
              resolve(false)
            } else {
              resolve(result)
            }
          })
        })
        Promise.all([sysDefer, localDefer]).then(result => {
          resolve(result)
        })
      }).then(res => {
        // 获取云端及本地,是否已存在该存储过程的信息
        if (res === false) return res
        if (res[0] === false || res[1] === false) return false
        let cloudfunc = ''
        let localfunc = ''
        res.forEach((item, index) => {
          if (index === 0 && item.TVPText) {
            cloudfunc = item.TVPText
          } else if (index === 1 && item.Ltext) {
            localfunc = Utils.formatOptions(item.Ltext)
          }
        })
        if ((newLText === localfunc) && (newLText === cloudfunc)) {
          return 'drop'
        } else if (!localfunc || (cloudfunc === localfunc)) {
          // 本地存储过程不存在,或云端和本地存储过程一致时,将新的存储过程更新至云端
          return Api.getSystemConfig({
            func: 'sPC_TVP_InUp',
            TVPName: setting.innerFunc,
            TVPText: newLText,
            TypeName: 'P'
          })
        } else {
          return new Promise(resolve => {
            Api.getSystemConfig({ // 添加现有的本地存储过程至云端
              func: 'sPC_TVP_InUp',
              TVPName: setting.innerFunc,
              TVPText: localfunc,
              TypeName: 'P'
            }).then(result => {
              if (result.status) {
                Api.getSystemConfig({
                  func: 'sPC_TVP_InUp', // 添加最新的存储过程至云端
                  TVPName: setting.innerFunc,
                  TVPText: newLText,
                  TypeName: 'P'
                }).then(response => {
                  resolve(response)
                })
              } else {
                resolve(result)
              }
            })
          })
        }
      }).then(res => {
        // 云端信息更新后,判断是删除或是直接新建存储过程
        if (res === false || res === 'drop') return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else {
          return 'create'
        }
      }).then(res => {
        // 删除存储过程
        if (res === false || res === 'create') return res
        let _param = {
          func: 'sPC_TableData_InUpDe',
          LText: DelText,
          TypeCharOne: 'proc' // 删除或创建存储过程
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
        return Api.getLocalConfig(_param)
      }).then(res => {
        // 根据上述操作结果,判断是否新建存储过程
        if (res === false || res === 'create') return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else {
          return 'create'
        }
      }).then(res => {
        // 新建存储过程
        if (res === false) return res
        let _param = {
          func: 'sPC_TableData_InUpDe',
          LText: newLText,
          TypeCharOne: 'proc' // 删除或创建存储过程
        }
        _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
        return Api.getLocalConfig(_param)
      }).then(res => {
        // 处理新建结果
        if (res === false) return res
        if (!res.status) {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
          return false
        } else {
          notification.success({
            top: 92,
            message: '创建成功',
            duration: 2
          })
          return true
        }
      }).then(res => {
        // 新建成功后,更新页面按钮信息
        if (res === false) {
          this.setState({
            funcLoading: false
          })
          return
        }
        this.setState({
          config: {...config, setting: setting}
        })
      })
    })
  }
  deleteElement = (element) => {
    let _this = this
    confirm({
      content: `确定删除<<${element.card.label}>>吗?`,
      okText: this.state.dict['header.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let _config = JSON.parse(JSON.stringify(_this.state.config))
        if (element.type === 'tabs') {
          _config[element.card.groupId] = _config[element.card.groupId].filter(item => {
            if (item.uuid === element.card.uuid) {
              return false
            } else {
              return true
            }
          })
        } else {
          _config[element.type] = _config[element.type].filter(item => {
            if (item.uuid === element.card.uuid) {
              return false
            } else {
              return true
            }
          })
        }
        let refreshtype = element.type + 'loading'
        if (/^tab/.test(refreshtype)) {
          refreshtype = 'tabloading'
        }
        _this.setState({
          config: _config,
          delActions: [..._this.state.delActions, element.card.uuid],
          [refreshtype]: true
        }, () => {
          _this.setState({
            [refreshtype]: false
          })
        })
      },
      onCancel() {}
    })
  }
  /**
   * @description 验证信息配置
   */
  profileAction = (element) => {
    this.setState({
      profileVisible: true,
      card: element
    })
  }
  /**
   * @description 验证信息保存
   */
  verifySubmit = () => {
    const { card } = this.state
    let config = JSON.parse(JSON.stringify(this.state.config))
    let _verify = this.verifyRef.state.verify
    config.action = config.action.map(item => {
      if (item.uuid === card.uuid) {
        item.verify = _verify
      }
      return item
    })
    this.setState({
      profileVisible: false,
      config: config,
      card: '',
      actionloading: true
    }, () => {
      this.setState({
        actionloading: false
      })
    })
  }
  /**
   * @description 三级菜单切换模板
   */
  changeTemplate = () => {
    this.props.handleConfig('template')
  }
  /**
   * @description 三级菜单保存
   */
  submitConfig = () => {
    const { menu } = this.props
    const { originMenu } = this.state
    let config = JSON.parse(JSON.stringify(this.state.config))
    this.menuformRef.handleConfirm().then(res => {
      if (config.search[0] && config.search[0].origin) {
        config.search = config.search.filter(item => !item.origin)
      }
      if (config.action[0] && config.action[0].origin) {
        config.action = config.action.filter(item => !item.origin)
      }
      if (config.columns[0] && config.columns[0].origin) {
        config.columns = config.columns.filter(item => !item.origin)
      }
      if (config.tabs[0] && config.tabs[0].origin) {
        config.tabs = config.tabs.filter(item => !item.origin)
      }
      let _LongParam = ''
      let _config = {...config, tables: this.state.selectedTables}
      let _pageParam = {...menu.PageParam, OpenType: res.opentype}
      // 未设置数据源或标签不合法时,启用状态为false
      if (_config.setting.interType === 'inner' && !_config.setting.innerFunc && !_config.setting.dataresource) {
        _config.enabled = false
      } else if (_config.tabgroups.length > 1) {
        _config.tabgroups.forEach(group => {
          if (_config[group].length === 0) {
            _config.enabled = false
          }
        })
      }
      // 保存时删除配置类型,system 、user
      delete _config.type
      try {
        _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
      } catch (e) {
        notification.warning({
          top: 92,
          message: '编译错误',
          duration: 10
        })
        return
      }
      let btnParam = { // 添加菜单按钮
        func: 'sPC_Button_AddUpt',
        ParentID: menu.MenuID,
        MenuNo: res.menuNo,
        Template: menu.PageParam.Template || '',
        PageParam: '',
        LongParam: '',
        LText: config.action.map((item, index) => {
          return `select '${item.uuid}' as menuid, '${item.label}' as menuname, '${(index + 1) * 10}' as Sort`
        })
      }
      btnParam.LText = btnParam.LText.join(' union all ')
      btnParam.LText = Utils.formatOptions(btnParam.LText)
      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
      let tabParam = { // 添加菜单tab页
        func: 'sPC_sMenusTab_AddUpt',
        MenuID: menu.MenuID,
        LText: config.tabs.map((item, index) => {
          return `select '${menu.MenuID}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${(index + 1) * 10}' as Sort`
        })
      }
      tabParam.LText = tabParam.LText.join(' union all ')
      tabParam.LText = Utils.formatOptions(tabParam.LText)
      tabParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
      tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
      let param = {
        func: 'sPC_TrdMenu_AddUpt',
        ParentID: res.parentId,
        MenuID: menu.MenuID,
        MenuNo: res.menuNo,
        Template: menu.PageParam.Template || '',
        MenuName: res.menuName,
        Sort: (this.props.supMenuList.length + 1) * 10,
        PageParam: JSON.stringify(_pageParam),
        LongParam: _LongParam
      }
      if (this.state.closeVisible) { // 显示关闭对话框时,模态框中保存按钮,显示保存中状态
        this.setState({
          menucloseloading: true
        })
      } else {
        this.setState({
          menuloading: true
        })
      }
      Api.getSystemConfig(param).then(response => {
        if (response.status) {
          this.setState({
            config: _config,
            originMenu: {
              ...originMenu,
              LongParam: _config,
              PageParam: _pageParam,
              MenuName: res.menuName,
              MenuNo: res.menuNo,
              ParentID: res.parentId
            },
            searchloading: true,
            actionloading: true,
            columnsloading: true
          }, () => {
            this.setState({
              searchloading: false,
              actionloading: false,
              columnsloading: false
            })
          })
          this.props.reloadmenu()
          this.submitAction(btnParam, tabParam)
        } else {
          this.setState({
            menuloading: false,
            menucloseloading: false
          })
          notification.warning({
            top: 92,
            message: response.message,
            duration: 10
          })
        }
      })
    }, () => {
      notification.warning({
        top: 92,
        message: this.state.dict['header.menu.basemsg'],
        duration: 10
      })
    })
  }
  /**
   * @description 保存或修改菜单按钮
   */
  submitAction = (btnParam, tabParam) => {
    const { config } = this.state
    new Promise(resolve => {
      // 内部请求
      if (this.state.delActions.length > 0) {
        let deffers = this.state.delActions.map(item => {
          let _param = {
            func: 'sPC_MainMenu_Del',
            MenuID: item
          }
          return new Promise(resolve => {
            Api.getSystemConfig(_param).then(res => {
              resolve(res)
            })
          })
        })
        Promise.all(deffers).then(result => {
          let error = false
          result.forEach(res => {
            if (!res.status) {
              error = res
            }
          })
          if (error) {
            notification.warning({
              top: 92,
              message: error.message,
              duration: 10
            })
            resolve(false)
          } else {
            this.setState({
              delActions: []
            })
            resolve(true)
          }
        })
      } else if (this.state.delActions.length === 0) {
        resolve(true)
      }
    }).then(res => {
      if (res === false) return res
      if (tabParam.LText) {
        Api.getSystemConfig(tabParam).then(result => {
          if (!result.status) {
            notification.warning({
              top: 92,
              message: result.message,
              duration: 10
            })
          }
        })
      }
      if (btnParam.LText) {
        return Api.getSystemConfig(btnParam)
      } else {
        return 'copy'
      }
    }).then(response => {
      if (response === false || response === 'copy') return response
      if (response.status) {
        return 'copy'
      } else {
        notification.warning({
          top: 92,
          message: response.message,
          duration: 10
        })
        return false
      }
    }).then(response => {
      if (response === false) return response
      let oriActions = []
      this.state.originActions.forEach(item => {
        let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 查看初始化按钮是否存在
        if (!curBtn) return
        if (curBtn.OpenType !== item.prebtn.OpenType) return
        oriActions.push({
          prebtn: item.prebtn,
          curBtn: curBtn
        })
      })
      if (oriActions.length === 0) return 'true'
      oriActions.forEach(action => {
        Api.getSystemConfig({
          func: 'sPC_Get_LongParam',
          MenuID: action.prebtn ? action.prebtn.uuid : ''
        }).then(result => {
          if (result.status && result.LongParam) {
            let _LongParam = ''
            if (result.LongParam) {
              _LongParam = window.decodeURIComponent(window.atob(result.LongParam))
              try {
                _LongParam = JSON.parse(_LongParam)
              } catch (e) {
                _LongParam = ''
              }
            }
            if (_LongParam) {
              let param = {
                func: 'sPC_ButtonParam_AddUpt',
                ParentID: this.props.menu.MenuID,
                MenuID: action.curBtn.uuid,
                MenuNo: this.props.menu.MenuNo,
                Template: _LongParam.type,
                MenuName: action.curBtn.label,
                PageParam: JSON.stringify({Template: _LongParam.type}),
                LongParam: result.LongParam
              }
              Api.getSystemConfig(param).then(() => {})
            }
          }
        })
      })
      return 'true'
    }).then(response => {
      if (response === 'true') {
        notification.success({
          top: 92,
          message: '保存成功',
          duration: 2
        })
        if (this.state.closeVisible) {
          this.props.handleConfig('')
        } else {
          this.setState({
            menuloading: false,
            menucloseloading: false
          })
        }
      } else {
        this.setState({
          menuloading: false,
          menucloseloading: false
        })
      }
    })
  }
  cancelConfig = () => {
    const { menu } = this.props
    const { config, originMenu } = this.state
    let _this = this
    let isAdd = false
    if (
      (config.search[0] && config.search[0].origin) ||
      (config.action[0] && config.action[0].origin) ||
      (config.columns[0] && config.columns[0].origin) ||
      (config.tabs[0] && config.tabs[0].origin)
    ) {
      isAdd = true
    }
    if (isAdd) {
      confirm({
        content: '菜单尚未提交,确定放弃保存吗?',
        okText: this.state.dict['header.confirm'],
        cancelText: this.state.dict['header.cancel'],
        onOk() {
          _this.props.handleConfig('')
        },
        onCancel() {}
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
          LongParam: _config,
          PageParam: _pageParam,
          MenuName: res.menuName,
          MenuNo: res.menuNo,
          ParentID: res.parentId
        }
        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
          this.setState({
            closeVisible: true
          })
        } else {
          this.props.handleConfig('')
        }
      }, () => {
        this.setState({
          closeVisible: true
        })
      })
    }
  }
  queryField = (type) => {
    const {selectedTables, tableColumns, config} = this.state
    // 判断是否已选择表名
    if (selectedTables.length === 0) {
      notification.warning({
        top: 92,
        message: '请选择表名!',
        duration: 10
      })
      return
    }
    // 表字段集转为map数据
    let columns = new Map()
    tableColumns.forEach(table => {
      table.columns.forEach(column => {
        columns.set(column.field, column)
      })
    })
    if (type === 'search') {
      // 添加搜索条件,字段集中存在搜索条件字段,使用搜索条件对象替换字段集,设置数据类型
      config.search.forEach(item => {
        if (columns.has(item.field)) {
          let _datatype = columns.get(item.field).datatype
          columns.set(item.field, {...item, selected: true, datatype: _datatype})
        }
      })
    } else if (type === 'columns') {
      // 添加显示列,字段集中存在显示列字段,使用显示列对象替换字段集,设置数据类型
      config.columns.forEach(item => {
        if (columns.has(item.field)) {
          let _datatype = columns.get(item.field).datatype
          columns.set(item.field, {...item, selected: true, datatype: _datatype})
        }
      })
    }
    // 显示字段集弹窗
    this.setState({
      addType: type,
      tableVisible: true,
      fields: [...columns.values()]
    })
  }
  addFieldSubmit = () => {
    // 字段集为空,关闭弹窗
    if (!this.state.fields || this.state.fields.length === 0) {
      this.setState({
        tableVisible: false,
        addType: ''
      })
    }
    const {addType, config} = this.state
    const textmatch = { // 选择text时匹配规则
      text: 'like',
      number: 'like',
      datetime: 'like',
      date: 'like'
    }
    const selectmatch = { // 选择select时匹配规则
      text: '=',
      number: '=',
      datetime: '=',
      date: '='
    }
    const datematch = { // 选择dateRange时匹配规则
      text: 'between',
      number: 'between',
      datetime: 'between',
      date: 'between'
    }
    // 获取已选字段集合
    let cards = this.refs.searchcard.state.selectCards
    let columnsMap = new Map()
    cards.forEach(card => {
      columnsMap.set(card.field, card)
    })
    let items = []
    if (addType === 'search') {
      config.search.forEach(item => {
        if (columnsMap.has(item.field)) {
          let cell = columnsMap.get(item.field)
          if (cell.selected && cell.type === item.type) { // 数据未修改
            items.push(item)
          } else if (cell.selected) { // 数据类型修改
            if (cell.type === 'text') {
              item.match = textmatch[cell.datatype]
            } else if (cell.type === 'select') {
              item.match = selectmatch[cell.datatype]
            } else if (cell.type === 'daterange') {
              item.match = datematch[cell.datatype]
            } else {
              cell.type = 'text'
              item.match = textmatch[cell.datatype]
            }
            item.type = cell.type
            item.initval = ''
            items.push(item)
          }
          columnsMap.delete(item.field)
        } else if (!item.origin) {
          items.push(item)
        }
      })
      let _columns = [...columnsMap.values()]
      _columns.forEach(item => {
        if (item.selected) {
          let _match = ''
          if (item.type === 'text') {
            _match = textmatch[item.datatype]
          } else if (item.type === 'select') {
            _match = selectmatch[item.datatype]
          } else if (item.type === 'daterange') {
            _match = datematch[item.datatype]
          } else {
            item.type = 'text'
            _match = textmatch[item.datatype]
          }
          let newcard = {
            uuid: Utils.getuuid(),
            label: item.label,
            field: item.field,
            initval: '',
            type: item.type,
            resourceType: '0',
            setAll: 'false',
            options: [],
            dataSource: '',
            linkField: '',
            valueField: '',
            valueText: '',
            orderBy: '',
            orderType: 'asc',
            match: _match,
            display: 'dropdown'
          }
          items.push(newcard)
        }
      })
    } else {
      config.columns.forEach(item => {
        if (columnsMap.has(item.field)) {
          let cell = columnsMap.get(item.field)
          if (cell.selected) {
            items.push(item)
          }
          columnsMap.delete(item.field)
        } else if (!item.origin) {
          items.push(item)
        }
      })
      let _columns = [...columnsMap.values()]
      _columns.forEach(item => {
        if (item.selected) {
          let newcard = {
            uuid: Utils.getuuid(),
            Align: 'left',
            label: item.label,
            field: item.field,
            Hide: 'false',
            IsSort: item.type === 'picture' ? 'false' : 'true',
            type: item.type,
            Width: 120
          }
          items.push(newcard)
        }
      })
    }
    this.setState({
      [addType + 'loading']: true,
      config: {...config, [addType]: items}
    }, () => {
      notification.success({
        top: 92,
        message: '操作成功',
        duration: 2
      })
      this.setState({
        [addType + 'loading']: false
      })
    })
  }
  onTableChange = (value) => {
    const {tables, selectedTables, tableColumns} = this.state
    let _table = tables.filter(item => item.TbName === value)[0]
    let isSelected = !!selectedTables.filter(cell => cell.TbName === value)[0]
    if (!isSelected) {
      this.setState({
        selectedTables: [...selectedTables, _table]
      })
      Api.getSystemConfig({func: 'sPC_Get_FieldName', TBName: value}).then(res => {
        if (res.status) {
          let tabmsg = {
            tableName: _table.name,
            columns: res.FDName.map(item => {
              let _type = item.FieldType.toLowerCase()
              let _decimal = 0
              if (/^nvarchar/.test(_type)) {
                _type = 'text'
              } else if (/^int/.test(_type)) {
                _type = 'number'
              } else if (/^decimal/.test(_type)) {
                _decimal = _type.split(',')[1]
                _decimal = parseInt(_decimal)
                _type = 'number'
              } else if (/^datetime/.test(_type)) {
                _type = 'datetime'
              } else if (/^date/.test(_type)) {
                _type = 'date'
              } else {
                _type = 'text'
              }
              return {
                field: item.FieldName,
                label: item.FieldDec,
                type: _type,
                datatype: _type,
                decimal: _decimal
              }
            })
          }
          this.setState({
            tableColumns: [...tableColumns, tabmsg]
          })
        } else {
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
          })
        }
      })
    }
  }
  deleteTable = (table) => {
    const {selectedTables, tableColumns} = this.state
    this.setState({
      selectedTables: selectedTables.filter(item => item.TbName !== table.TbName),
      tableColumns: tableColumns.filter(item => item.tableName !== table.TbName)
    })
  }
  changeSetting = () => {
    this.setState({
      settingVisible: true
    })
  }
  settingSave = () => {
    const { menu } = this.props
    const {config} = this.state
    this.settingRef.handleConfirm().then(res => {
      if (
        res.interType === 'inner' &&
        !res.innerFunc &&
        res.dataresource.length > 50 &&
        config.setting.dataresource !== res.dataresource
      ) {
        let param = {
          func: 's_DataSrc_Save',
          LText: res.dataresource,
          MenuID: menu.MenuID
        }
        param.LText = Utils.formatOptions(param.LText)
        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
        Api.getLocalConfig(param)
      }
      this.setState({
        config: {...config, setting: res},
        settingVisible: false,
        columnsloading: true,
        tabloading: true
      }, () => {
        this.setState({
          columnsloading: false,
          tabloading: false
        })
      })
    })
  }
  /**
   * @description 设置可配置按钮
   */
  setSubConfig = (btn, type) => {
    const { menu } = this.props
    const { config, originMenu } = this.state
    let isAdd = false
    if (
      (config.search[0] && config.search[0].origin) ||
      (config.action[0] && config.action[0].origin) ||
      (config.columns[0] && config.columns[0].origin) ||
      (config.tabs[0] && config.tabs[0].origin)
    ) {
      isAdd = true
    }
    if (isAdd) {
      notification.warning({
        top: 92,
        message: '菜单尚未保存,请保存菜单配置!',
        duration: 10
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
          LongParam: _config,
          PageParam: _pageParam,
          MenuName: res.menuName,
          MenuNo: res.menuNo,
          ParentID: res.parentId
        }
        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
          notification.warning({
            top: 92,
            message: '菜单配置已修改,请保存!',
            duration: 10
          })
        } else {
          this.setState({
            loading: true
          })
          let uuid = ''
          let _type = type
          if (type === 'button' && btn.OpenType === 'popview') {
            _type = 'tab'
          }
          if (_type === 'button') {
            uuid = btn.uuid
          } else {
            uuid = btn.linkTab
          }
          Api.getSystemConfig({
            func: 'sPC_Get_LongParam',
            MenuID: uuid
          }).then(res => {
            if (res.status) {
              this.setState({
                loading: false
              })
              let _LongParam = ''
              if (res.LongParam) {
                _LongParam = window.decodeURIComponent(window.atob(res.LongParam))
                try {
                  _LongParam = JSON.parse(_LongParam)
                } catch (e) {
                  _LongParam = ''
                }
              }
              if (_type === 'tab' && !_LongParam) {
                _LongParam = {
                  ...btn,
                  uuid: btn.linkTab,
                  create: true
                }
              }
              this.props.handleSubConfig(btn, originMenu, _LongParam, _type)
            } else {
              this.setState({
                loading: false
              })
              notification.warning({
                top: 92,
                message: res.message,
                duration: 10
              })
            }
          })
        }
      }, () => {
        notification.warning({
          top: 92,
          message: '菜单基本信息已修改,请保存!',
          duration: 10
        })
      })
    }
  }
  onEnabledChange = (val, e) => {
    const { config } = this.state
    let tabinvalid = true
    if (config.tabgroups.length > 1) {
      config.tabgroups.forEach(group => {
        if (config[group].length === 0) {
          tabinvalid = false
        }
      })
    }
    if (config.setting.interType === 'inner' && !config.setting.innerFunc && !config.setting.dataresource) {
      notification.warning({
        top: 92,
        message: '菜单尚未设置数据源,不可启用!',
        duration: 10
      })
    } else if (!tabinvalid) {
      notification.warning({
        top: 92,
        message: '菜单标签页设置错误(多行标签内,行标签不可为空),不可启用!',
        duration: 10
      })
    } else {
      this.setState({
        config: {...config, enabled: !config.enabled}
      })
    }
  }
  onColumnNameChange = () => {
    const { showColumnName } = this.state
    this.setState({
      showColumnName: !showColumnName
    })
  }
  addTabGroup = () => {
    let _this = this
    let _config = JSON.parse(JSON.stringify(this.state.config))
    confirm({
      content: `确定新建标签组吗?`,
      okText: this.state.dict['header.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let newgroup = 'tabs' + Utils.getuuid()
        _config.tabgroups.push(newgroup)
        _config[newgroup] = []
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
        })
      },
      onCancel() {}
    })
  }
  delTabGroup = (groupId) => {
    let _this = this
    let _config = JSON.parse(JSON.stringify(this.state.config))
    confirm({
      content: `确定删除标签组吗?`,
      okText: this.state.dict['header.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        _config.tabgroups = _config.tabgroups.filter(group => group !== groupId)
        delete _config[groupId]
        _this.setState({
          config: _config,
          tabloading: true
        }, () => {
          _this.setState({
            tabloading: false
          })
        })
      },
      onCancel() {}
    })
  }
  render () {
    const configAction = this.state.config.action.filter(_action =>
      !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
    )
    let configTabs = []
    this.state.config.tabgroups.forEach(group => {
      configTabs.push(...this.state.config[group])
    })
    return (
      <div className="common-table-board">
        <DndProvider backend={HTML5Backend}>
          {/* 工具栏 */}
          <div className="tools">
            <Collapse accordion defaultActiveKey="0" bordered={false}>
              {/* 基本信息 */}
              <Panel header={this.state.dict['header.menu.basedata']} key="0" id="common-basedata">
                {/* 菜单信息 */}
                <MenuForm
                  dict={this.state.dict}
                  formlist={this.state.menuformlist}
                  wrappedComponentRef={(inst) => this.menuformRef = inst}
                />
                {/* 表名添加 */}
                <div className="ant-col ant-form-item-label">
                  <label>
                    <Tooltip placement="topLeft" title="此处可以添加配置相关的常用表,在添加搜索条件和显示列时,可通过工具栏中的添加按钮,批量添加表格相关字段。">
                      <Icon type="question-circle" />
                      {this.state.dict['header.menu.table.add']}
                    </Tooltip>
                  </label>
                </div>
                <Select
                  showSearch
                  className="tables"
                  style={{ width: '100%' }}
                  optionFilterProp="children"
                  value={this.state.dict['header.menu.table.placeholder']}
                  onChange={this.onTableChange}
                  showArrow={false}
                  getPopupContainer={() => document.getElementById('common-basedata')}
                  filterOption={(input, option) => {
                    return option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
                      option.props.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
                  }}
                >
                  {this.state.tables.map((table, index) => (
                    <Option key={index} title={table.TbName} value={table.TbName}>{table.Remark}</Option>
                  ))}
                </Select>
                {this.state.selectedTables.length > 0 && <List
                  size="small"
                  bordered
                  dataSource={this.state.selectedTables}
                  renderItem={(item, index) => <List.Item key={index} title={item.Remark + ' (' + item.TbName + ')'}>
                    {item.Remark + ' (' + item.TbName + ')'}
                    <Icon type="close" onClick={() => this.deleteTable(item)}/>
                    <div className="bottom-mask"></div>
                  </List.Item>}
                />}
              </Panel>
              {/* 搜索条件添加 */}
              <Panel header={this.state.dict['header.menu.search']} key="1">
                <div className="search-element">
                  {Source.searchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <Button type="primary" block onClick={() => this.queryField('search')}>{this.state.dict['header.menu.search.add']}</Button>
              </Panel>
              {/* 按钮添加 */}
              <Panel header={this.state.dict['header.menu.action']} key="2">
                <div className="search-element">
                  {Source.actionItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                {configAction.length > 0 ?
                  <p className="config-btn-title">
                    <Tooltip placement="topLeft" title="点击按钮,可完成或查看按钮配置信息。">
                      <Icon type="question-circle" />
                    </Tooltip>
                    {this.state.dict['header.menu.action.configurable']}
                  </p> : null
                }
                {configAction.map((item, index) => {
                  return (
                    <div key={index}>
                      <Button
                        icon={item.icon}
                        style={{marginBottom: '10px'}}
                        className={'config-button mk-btn mk-' + item.class}
                        onClick={() => this.setSubConfig(item, 'button')}
                      >{item.label}</Button>
                    </div>
                  )
                })}
              </Panel>
              {/* 添加显示列 */}
              <Panel header={this.state.dict['header.menu.column']} key="3">
                <div className="search-element">
                  {Source.columnItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <Button type="primary" block onClick={() => this.queryField('columns')}>{this.state.dict['header.menu.column.add']}</Button>
              </Panel>
              {/* 添加标签 */}
              <Panel header={this.state.dict['header.menu.tab']} key="4">
                <div className="search-element">
                  {Source.tabItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                {configTabs.length > 0 ?
                  <p className="config-btn-title">
                    <Tooltip placement="topLeft" title="点击按钮,可完成或查看标签配置信息。">
                      <Icon type="question-circle" />
                    </Tooltip>
                    {this.state.dict['header.menu.tab.configurable']}
                  </p> : null
                }
                {configTabs.map((item, index) => {
                  return (
                    <div key={index}>
                      <Button
                        className="config-button"
                        icon={item.icon}
                        style={{marginBottom: '10px'}}
                        onClick={() => this.setSubConfig(item, 'tab')}
                      >{item.label}</Button>
                    </div>
                  )
                })}
              </Panel>
            </Collapse>
          </div>
          <div className="setting">
            <Card title={this.state.dict['header.menu.page.configurable']} bordered={false} extra={
              <div>
                <Switch className="big" checkedChildren="启" unCheckedChildren="停" checked={this.state.config.enabled} onChange={this.onEnabledChange} />
                <Button type="primary" onClick={this.changeTemplate}>{this.state.dict['header.menu.template.change']}</Button>
                <Button type="primary" onClick={this.submitConfig} loading={this.state.menuloading}>{this.state.dict['header.save']}</Button>
                <Button onClick={this.cancelConfig}>{this.state.dict['header.return']}</Button>
              </div>
            } style={{ width: '100%' }}>
              <Icon type="setting" onClick={this.changeSetting} />
              <div className="search-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《搜索》中,选择对应搜索框拖至此处添加;或点击按钮《添加搜索条件》批量添加,选择批量添加时,需提前选择使用表。">
                  <Icon type="question-circle" />
                </Tooltip>
                {!this.state.searchloading ?
                  <DragElement
                    type="search"
                    list={this.state.config.search}
                    handleList={this.handleList}
                    handleMenu={this.handleSearch}
                    deleteMenu={this.deleteElement}
                    placeholder={this.state.dict['header.form.search.placeholder']}
                  /> : null
                }
              </div>
              <div className="action-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《按钮》中,选择对应类型的按钮拖至此处添加,如选择按钮类型为表单、新标签页等含有配置页面的按钮,可在左侧工具栏-按钮-可配置按钮处,点击按钮完成相关配置。注:当设置按钮显示位置为表格时,显示列会增加操作列。">
                  <Icon type="question-circle" />
                </Tooltip>
                {!this.state.actionloading ?
                  <DragElement
                    type="action"
                    list={this.state.config.action}
                    handleList={this.handleList}
                    handleMenu={this.handleAction}
                    copyElement={(val) => this.handleAction(val, 'copy')}
                    deleteMenu={this.deleteElement}
                    profileMenu={this.profileAction}
                    placeholder={this.state.dict['header.form.action.placeholder']}
                  /> : null
                }
              </div>
              {/* 显示列 */}
              <div className="column-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《显示列》中,选择对应类型的显示列拖至此处添加;或点击《添加显示列》按钮批量添加,选择批量添加时,需提前选择使用表。注:添加合并列时,需设置可选列。">
                  <Icon type="question-circle" />
                </Tooltip>
                <Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={this.state.showColumnName} onChange={this.onColumnNameChange} />
                {!this.state.columnsloading ?
                  <DragElement
                    type="columns"
                    list={this.state.config.columns}
                    setting={this.state.config.setting}
                    gridBtn={this.state.config.gridBtn}
                    handleList={this.handleList}
                    handleMenu={this.handleColumn}
                    deleteMenu={this.deleteElement}
                    handleGridBtn={this.handleGridBtn}
                    showfield={this.state.showColumnName}
                    placeholder={this.state.dict['header.form.column.placeholder']}
                  /> : null
                }
              </div>
              {/* 标签组 */}
              {!this.state.tabloading && this.state.config.tabgroups.map((groupId, index) => {
                return (
                  <div key={index} className="tab-list">
                    {index === 0 ? <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《标签页》中,选择对应类型的标签页拖至此处添加。">
                      <Icon type="question-circle" />
                    </Tooltip> : null}
                    {index === 0 ? <Icon type="plus" onClick={this.addTabGroup} /> : null}
                    {index !== 0 ? <Icon type="delete" onClick={() => {this.delTabGroup(groupId)}} /> : null}
                    <TabDragElement
                      type="tabs"
                      groupId={groupId}
                      list={this.state.config[groupId]}
                      handleList={this.handleList}
                      handleMenu={this.handleTab}
                      deleteMenu={this.deleteElement}
                      placeholder={this.state.dict['header.form.tab.placeholder']}
                    />
                  </div>)
              })}
            </Card>
          </div>
        </DndProvider>
        {/* 编辑搜索条件、按钮、显示列 */}
        <Modal
          title={this.state.modalTitle}
          visible={this.state.visible}
          width={700}
          onCancel={() => { this.setState({ visible: false }) }}
          footer={[
            this.state.formtemp === 'action' ?
            <Button key="delete" className="mk-btn mk-purple" onClick={this.creatFunc} loading={this.state.funcLoading}>{this.state.dict['header.menu.func.create']}</Button> : null,
            <Button key="cancel" onClick={() => { this.setState({ visible: false }) }}>{this.state.dict['header.cancel']}</Button>,
            <Button key="confirm" type="primary" onClick={this.handleSubmit}>{this.state.dict['header.confirm']}</Button>
          ]}
          destroyOnClose
        >
          {this.state.formtemp === 'search' ?
            <SearchForm
              dict={this.state.dict}
              formlist={this.state.formlist}
              card={this.state.card}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'action' ?
            <ActionForm
              dict={this.state.dict}
              card={this.state.card}
              tabs={this.state.tabviews}
              formlist={this.state.formlist}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'columns' && this.state.card.type !== 'colspan' ?
            <ColumnForm
              dict={this.state.dict}
              card={this.state.card}
              formlist={this.state.formlist}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'columns' && this.state.card.type === 'colspan' ?
            <ColspanForm
              dict={this.state.dict}
              card={this.state.card}
              columns={this.state.config.columns}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'gridbtn' ?
            <GridBtnForm
              dict={this.state.dict}
              card={this.state.config.gridBtn}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
          {this.state.formtemp === 'tabs' ?
            <TabForm
              type="tabs"
              tabs={this.state.tabviews}
              dict={this.state.dict}
              card={this.state.card}
              formlist={this.state.formlist}
              wrappedComponentRef={(inst) => this.formRef = inst}
            /> : null
          }
        </Modal>
        {/* 根据字段名添加显示列及搜索条件 */}
        <Modal
          wrapClassName="common-table-fields-modal"
          title={this.state.dict['header.edit']}
          visible={this.state.tableVisible}
          width={'65vw'}
          style={{minWidth: '900px', maxWidth: '1200px'}}
          cancelText={this.state.dict['header.close']}
          onOk={this.addFieldSubmit}
          onCancel={() => { // 取消添加
            this.setState({
              tableVisible: false,
              addType: ''
            })
          }}
          destroyOnClose
        >
          {this.state.addType && this.state.fields.length > 0 ?
            <EditCard data={this.state.fields} ref="searchcard" type={this.state.addType} dict={this.state.dict} /> : null
          }
          {(!this.state.fields || this.state.fields.length === 0) &&
            <Empty />
          }
        </Modal>
        {/* 按钮使用系统存储过程时,验证信息模态框 */}
        <Modal
          wrapClassName="common-table-fields-modal"
          title={'验证信息'}
          visible={this.state.profileVisible}
          width={'75vw'}
          style={{minWidth: '900px', maxWidth: '1200px'}}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ profileVisible: false }) }}
          destroyOnClose
        >
          <VerifyCard card={this.state.card} columns={this.state.config.columns} wrappedComponentRef={(inst) => this.verifyRef = inst} dict={this.state.dict} />
        </Modal>
        {/* 设置全局配置及列表数据源 */}
        <Modal
          title={this.state.dict['header.edit']}
          visible={this.state.settingVisible}
          width={700}
          // onOk={this.settingSave}
          onCancel={() => { // 取消修改
            this.setState({
              settingVisible: false
            })
          }}
          footer={[
            <Button key="delete" className="mk-btn mk-purple" onClick={this.tableCreatFunc} loading={this.state.funcLoading}>{this.state.dict['header.menu.func.create']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({ settingVisible: false }) }}>{this.state.dict['header.cancel']}</Button>,
            <Button key="confirm" type="primary" onClick={this.settingSave}>{this.state.dict['header.confirm']}</Button>
          ]}
          destroyOnClose
        >
          <SettingForm
            dict={this.state.dict}
            menu={this.props.menu}
            config={this.state.config}
            data={this.state.config.setting}
            columns={this.state.config.columns}
            usefulFields={this.props.permFuncField}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
        <Modal
          bodyStyle={{textAlign: 'center', color: '#000000', fontSize: '16px'}}
          closable={false}
          visible={this.state.closeVisible}
          onCancel={() => { this.setState({closeVisible: false}) }}
          footer={[
            <Button key="save" className="mk-btn mk-green" loading={this.state.menucloseloading} onClick={this.submitConfig}>{this.state.dict['header.save']}</Button>,
            <Button key="confirm" className="mk-btn mk-yellow" onClick={() => {this.props.handleConfig('')}}>{this.state.dict['header.notsave']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['header.cancel']}</Button>
          ]}
          destroyOnClose
        >
          {this.state.dict['header.menu.config.placeholder']}
        </Modal>
        {this.state.loading && <Spin size="large" />}
      </div>
    )
  }
}
const mapStateToProps = (state) => {
  return {
    permFuncField: state.permFuncField
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(ComTableConfig)
src/templates/modalviewconfig/index.scss
New file
@@ -0,0 +1,515 @@
.common-table-board {
  position: fixed;
  z-index: 1070;
  padding-top: 48px;
  top: 0px;
  left: 0px;
  right: 0px;
  bottom: 0px;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  .tools {
    flex: 1;
    background: #ffffff;
    border-right: 1px solid #d9d9d9;
    height: 100%;
    overflow-y: hidden;
    padding-bottom: 30px;
    .ant-collapse-item {
      position: relative;
      border: 0;
    }
    .ant-input-search {
      margin-top: 10px;
    }
    .ant-collapse-item.ant-collapse-item-active {
      border-bottom: 1px solid #d9d9d9;
    }
    .ant-collapse-header {
      padding: 11px 16px 10px 40px;
      border-bottom: 1px solid #d9d9d9;
      background: #1890ff;
      color: #ffffff;
    }
    .ant-collapse-content-box {
      .ant-form-item {
        margin-bottom: 10px;
        .ant-form-item-label {
          text-align: left;
          height: 25px;
          line-height: 25px;
        }
      }
    }
    .search-element {
      padding-top: 10px;
      li {
        padding: 0px 16px 10px;
        div {
          cursor: move;
        }
      }
    }
    .config-btn-title {
      margin-top: 20px;
      margin-bottom: 10px;
      color: #1890ff;
      border-bottom: 1px solid #e8e8e8;
    }
    .tables {
      .ant-select-selection-selected-value {
        opacity: 0.4!important;
      }
    }
    .ant-list {
      margin-top: 20px;
      .ant-list-item {
        display: -webkit-box;
        padding-right: 20px;
        position: relative;
        padding-left: 5px;
        overflow: hidden;
        text-overflow: ellipsis;
        -webkit-line-clamp: 2;
        -webkit-box-orient: vertical;
        min-height: 55px;
        width: 100%;
        .anticon {
          position: absolute;
          top: 0px;
          right: 0px;
          padding: 3px 3px 10px 10px;
          cursor: pointer;
        }
        .bottom-mask {
          position: absolute;
          width: 100%;
          height: 8px;
          bottom: 0;
          left: 0;
          background: #ffffff;
          border-radius: 8px;
        }
      }
    }
    .anticon-question-circle {
      color: #c49f47;
      margin-right: 3px;
    }
    .config-button {
      min-width: 65px;
    }
  }
  .tools:hover {
    overflow-y: auto;
  }
  .tools::-webkit-scrollbar {
    width: 7px;
  }
  .tools::-webkit-scrollbar-thumb {
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
    background: rgba(0, 0, 0, 0);
  }
  .tools::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0);
    background: rgba(0, 0, 0, 0);
  }
  .tools:hover::-webkit-scrollbar-thumb {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
  }
  .tools:hover::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
    border: 1px solid rgba(0, 0, 0, 0.07);
  }
  .setting {
    position: relative;
    width: calc(100vw - 235px);
    height: 100%;
    // overflow-y: hidden;
    background: #ffffff;
    .ant-switch.big {
      min-width: 60px;
      height: 28px;
      line-height: 28px;
      margin-top: -2px;
      .ant-switch-inner {
        font-size: 14px;
      }
    }
    .ant-switch.big::after {
      width: 24px;
      height: 24px;
    }
    .ant-card-head {
      min-height: 44px;
    }
    .ant-card-head-title {
      padding: 5px 0;
      color: #1890ff;
    }
    .ant-card-extra {
      padding: 5px 0;
      button {
        margin-left: 20px;
      }
    }
    .ant-card-body {
      position: relative;
      padding: 0 0 40px;
      .search-list {
        padding: 1px 24px 20px;
        min-height: 87px;
        border-bottom: 1px solid #d9d9d9;
        > .ant-row {
          min-height: 65px;
        }
        .ant-row .ant-col-6 {
          padding: 0 12px!important;
        }
        .ant-row.ant-form-item .ant-col {
          padding: 0;
        }
        .page-card {
          position: relative;
          background: #ffffff;
          border-radius: 2px;
          padding-top: 15px;
          .ant-form-item {
            cursor: move;
            display: flex;
            margin-bottom: 0px;
            .ant-form-item-label {
              width: 100px;
              height: 40px;
              label {
                width: 100%;
                cursor: move;
                overflow: hidden;
                display: inline-block;
                text-overflow: ellipsis;
                white-space: nowrap;
              }
            }
            .ant-form-item-control-wrapper {
              flex: 1 1;
              .ant-select {
                width: 100%;
                margin-top: 4px;
              }
              .ant-calendar-picker {
                margin-top: 4px;
              }
              .input-mask {
                position: absolute;
                top: 0;
                left: 0;
                right: 0;
                bottom: 0;
                opacity: 0;
                z-index: 2;
              }
              .data-range .ant-calendar-picker-input {
                padding: 4px 20px 4px 5px;
                font-size: 13px;
              }
            }
          }
          .edit {
            position: absolute;
            left: 0;
            top: 5px;
            color: #1890ff;
            cursor: pointer;
            display: none;
          }
          .edit.close {
            left: 20px;
            color: #ff4d4f;
          }
        }
        .page-card:hover {
          .edit {
            display: inline-block;
          }
        }
        .ant-calendar-picker {
          min-width: 100px!important;
          width: 100%;
        }
      }
      .action-list {
        padding: 0px 20px 15px;
        min-height: 82px;
        > .ant-row {
          min-height: 67px;
        }
        .page-card {
          display: inline-block;
          margin: 0px 0px 0px 0px;
          padding: 15px 10px 0 0;
          position: relative;
          div {
            cursor: move;
          }
          .edit {
            position: absolute;
            left: 0;
            top: 0px;
            color: #1890ff;
            cursor: pointer;
            display: none;
          }
          .edit.close {
            left: 40px;
            color: #ff4d4f;
          }
          .edit.copy {
            left: 20px;
            color: #26C281;
          }
          .edit.profile {
            left: 60px;
            color: purple;
          }
          button {
            cursor: move;
            min-width: 65px;
            .anticon-table {
              font-size: 10px;
              position: absolute;
              right: 1px;
              bottom: 0px;
            }
          }
        }
        .page-card:hover {
          .edit {
            display: inline-block;
          }
        }
      }
      .column-list {
        position: relative;
        padding: 0px 20px;
        .ant-switch {
          position: absolute;
          right: 20px;
          top: -10px;
        }
        > .ant-row {
          background: #fafafa;
          border-radius: 4px;
          min-height: 47px;
          border: 1px solid #e8e8e8;
          .column-box {
            display: flex;
          }
          .column-box:not(:first-child) {
            border-top: 1px solid #e8e8e8;
          }
          .page-card {
            position: relative;
            padding: 0px;
            min-height: 45px;
            > div {
              padding: 12px 0px 0px;
              cursor: move;
              height: 100%;
              .ant-table-column-sorters {
                padding: 0px 8px 12px;
              }
              .ant-table-column-fields {
                padding: 0px 8px 5px;
              }
            }
            .ant-table-column-sorter {
              position: relative;
              display: inline-block;
              width: 24px;
              font-size: 12px;
              color: #bfbfbf;
              .anticon-caret-up {
                position: relative;
                left: 10px;
                top: -3px;
              }
              .anticon-caret-down {
                position: relative;
                left: -2px;
                top: 3px;
              }
            }
            .edit {
              position: absolute;
              left: 0;
              top: 0px;
              color: #1890ff;
              cursor: pointer;
              display: none;
            }
            .edit.close {
              left: 20px;
              color: #ff4d4f;
            }
            .ant-checkbox-inner {
              margin-top: 14px;
              margin-left: calc(50% - 8px);
            }
          }
          .page-card:hover {
            .edit {
              display: inline-block;
            }
          }
          .page-card:not(:last-child) {
            border-right: 1px solid #e8e8e8;
          }
        }
      }
      .tab-list {
        position: relative;
        padding: 30px 20px 0px;
        .ant-switch {
          position: absolute;
          right: 20px;
          top: 20px;
        }
        > .ant-row {
          min-height: 47px;
          .page-card {
            position: relative;
            padding: 0px;
            > div {
              padding: 12px 0px 0px;
              cursor: move;
            }
          }
          .ant-tabs-tab {
            .edit {
              position: absolute;
              left: 0;
              top: 0px;
              color: #1890ff;
              cursor: pointer;
              display: none;
            }
            .edit.close {
              left: 20px;
              color: #ff4d4f;
            }
          }
          .ant-tabs-bar {
            min-height: 55px;
          }
          .ant-tabs-tab:hover {
            .edit {
              display: inline-block;
            }
          }
          .ant-tabs-content {
            .ant-tabs-tabpane img {
              width: 100%;
            }
          }
        }
        > .anticon-question-circle {
          position: absolute;
          left: 5px;
          top: 20px;
        }
        > .anticon-plus {
          position: absolute;
          font-size: 24px;
          right: 25px;
          top: 50px;
          z-index: 1;
          cursor: pointer;
        }
        > .anticon-delete {
          position: absolute;
          font-size: 24px;
          right: 25px;
          top: 50px;
          z-index: 1;
          cursor: pointer;
        }
        .ant-tabs-nav-container-scrolling {
          margin-right: 50px;
        }
      }
      > .anticon-setting {
        position: absolute;
        font-size: 18px;
        right: 5px;
        top: 10px;
        padding: 10px;
        z-index: 1;
      }
    }
    .anticon-question-circle {
      color: #c49f47;
      position: relative;
      left: -15px;
      top: 5px;
    }
  }
  .setting {
    overflow-y: scroll;
  }
  .setting::-webkit-scrollbar {
    width: 7px;
  }
  .setting::-webkit-scrollbar-thumb {
    border-radius: 5px;
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
    background: rgba(0, 0, 0, 0.13);
    display: none;
  }
  .setting::-webkit-scrollbar-track {
    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
    border-radius: 3px;
    border: 1px solid rgba(0, 0, 0, 0.07);
    background: rgba(0, 0, 0, 0);
  }
  .setting:hover::-webkit-scrollbar-thumb {
    display: block;
  }
  .ant-spin {
    position: absolute;
    margin-left: calc(50vw - 22px);
    margin-top: 30vh;
  }
}
.common-table-fields-modal {
  .ant-modal {
    top: 50px;
    padding-bottom: 5px;
    .ant-modal-body {
      max-height: calc(100vh - 190px);
      overflow-y: auto;
      .ant-empty {
        margin: 15vh 8px;
      }
    }
    .ant-modal-body::-webkit-scrollbar {
      width: 7px;
    }
    .ant-modal-body::-webkit-scrollbar-thumb {
      border-radius: 5px;
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
      background: rgba(0, 0, 0, 0.13);
    }
    .ant-modal-body::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
      border-radius: 3px;
      border: 1px solid rgba(0, 0, 0, 0.07);
      background: rgba(0, 0, 0, 0);
    }
  }
}
src/templates/modalviewconfig/settingform/index.jsx
New file
@@ -0,0 +1,312 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Select, Tooltip, Icon, notification } from 'antd'
import './index.scss'
const { TextArea } = Input
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object, // 字典项
    menu: PropTypes.object,
    data: PropTypes.object,
    config: PropTypes.object,
    columns: PropTypes.array,
    usefulFields: PropTypes.array
  }
  state = {
    interType: this.props.data.interType || 'inner',
    columns: this.props.columns.filter(item => item.field && item.type !== 'colspan'),
    currentTabs: null,
    selectTabs: []
  }
  UNSAFE_componentWillMount() {
    const { config, data } = this.props
    let _tabs = []
    let _select = []
    let _tabMap = new Map()
    config.tabgroups.forEach(groupname => {
      config[groupname].forEach(tab => {
        if (tab.origin) return
        _tabs.push(tab)
        _tabMap.set(tab.uuid, true)
      })
    })
    data.subtabs && data.subtabs.forEach(tabId => {
      if (_tabMap.has(tabId)) {
        _select.push(tabId)
      }
    })
    this.setState({
      currentTabs: _tabs,
      selectTabs: _select
    })
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.actionfixed = values.actionfixed === 'true'
          values.columnfixed = values.columnfixed === 'true'
          if (values.interType === 'inner' && !values.innerFunc && !values.dataresource) {
            notification.warning({
              top: 92,
              message: '请自定义函数或填写数据源!',
              duration: 10
            })
          } else {
            resolve(values)
          }
        } else {
          reject(err)
        }
      })
    })
  }
  onChange = (e) => {
    this.setState({
      interType: e.target.value
    })
  }
  selectChange = (val) => {
    // let _order = this.props.form.getFieldValue('order')
    this.props.form.setFieldsValue({
      order: `${val} desc`
    })
  }
  render() {
    const { data, dict, menu, usefulFields } = this.props
    const { getFieldDecorator } = this.props.form
    const { interType, columns, selectTabs } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    let primaryKey = data.primaryKey
    if (primaryKey) {
      let field = columns.filter(column => column.field === primaryKey)
      if (field.length !== 1) {
        primaryKey = ''
      }
    }
    if (!primaryKey && columns.length === 0) {
      primaryKey = 'ID'
    }
    let str = '^(' + usefulFields.join('|') + ')'
    let _patten = new RegExp(str + '[0-9a-zA-Z_]*$', 'g')
    return (
      <Form {...formItemLayout} className="ant-advanced-search-form commontable-setting-form" id="commontable-setting-form">
        <Row gutter={24}>
          <Col span={12}>
            <Form.Item label="表名">
              {getFieldDecorator('tableName', {
                initialValue: data.tableName,
                rules: [
                  {
                    required: true,
                    message: dict['form.required.input'] + '表名!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="表格属性">
              {getFieldDecorator('tableType', {
                initialValue: data.tableType
              })(
                <Select
                  getPopupContainer={() => document.getElementById('commontable-setting-form')}
                >
                  <Select.Option value="">不可选</Select.Option>
                  <Select.Option value="radio">单选</Select.Option>
                  <Select.Option value="checkbox">多选</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label={dict['header.form.intertype']}>
              {getFieldDecorator('interType', {
                initialValue: data.interType || 'inner'
              })(
                <Radio.Group onChange={this.onChange}>
                  <Radio value="inner">{dict['header.form.interface.inner']}</Radio>
                  <Radio value="outer">{dict['header.form.interface.outer']}</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          {interType === 'outer' ? <Col span={12}>
            <Form.Item label={dict['header.form.interface']}>
              {getFieldDecorator('interface', {
                initialValue: data.interface || '',
                rules: [
                  {
                    required: true,
                    message: dict['form.required.input'] + dict['header.form.interface'] + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col> : null}
          {interType === 'outer' ? <Col span={12}>
            <Form.Item label={dict['header.form.outerFunc']}>
              {getFieldDecorator('outerFunc', {
                initialValue: data.outerFunc || ''
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col> : null}
          {interType !== 'outer' ? <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" overlayClassName="middle" title={`可自定义数据处理函数,函数名称需以${usefulFields.join(', ')}等字符开始;未设置时会调用系统函数,使用系统函数需完善数据源。`}>
                <Icon type="question-circle" />
                {dict['header.form.innerFunc']}
              </Tooltip>
            }>
              {getFieldDecorator('innerFunc', {
                initialValue: data.innerFunc || '',
                rules: [
                  {
                    pattern: _patten,
                    message: '名称只允许包含数字、字母和下划线,且以指定字符开始。'
                  }, {
                    max: 50,
                    message: '内部函数名称不超过50个字符。'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col> : null}
          {interType !== 'outer' ? <Col span={24}>
            <Form.Item help={'数据ID:' + menu.MenuID} label={
              <Tooltip placement="topLeft" title="使用系统函数时,需填写数据源,自定义函数时,可忽略。">
                <Icon type="question-circle" />
                {'数据源'}
              </Tooltip>
            } className="textarea">
              {getFieldDecorator('dataresource', {
                initialValue: data.dataresource
              })(<TextArea rows={4} />)}
            </Form.Item>
          </Col> : null}
          <Col span={12}>
            <Form.Item label="固定按钮">
              {getFieldDecorator('actionfixed', {
                initialValue: data.actionfixed ? 'true' : 'false'
              })(
                <Select>
                  <Select.Option value="true">是</Select.Option>
                  <Select.Option value="false">否</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="固定列">
              {getFieldDecorator('columnfixed', {
                initialValue: data.columnfixed ? 'true' : 'false'
              })(
                <Select>
                  <Select.Option value="true">是</Select.Option>
                  <Select.Option value="false">否</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="主键">
              {getFieldDecorator('primaryKey', {
                initialValue: primaryKey
              })(
                <Select
                  getPopupContainer={() => document.getElementById('commontable-setting-form')}
                  onChange={this.selectChange}
                >
                  <Select.Option key='unset' value="">不设置</Select.Option>
                  {columns.length === 0 ?
                    <Select.Option key='id' value="ID">ID</Select.Option> : null
                  }
                  {columns.map((option, index) =>
                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.field}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="默认排序">
              {getFieldDecorator('order', {
                initialValue: data.order || (primaryKey ? primaryKey + ' desc' : ''),
                rules: [
                  {
                    required: true,
                    message: dict['form.required.input'] + '默认排序字段!'
                  }
                ]
              })(<Input placeholder="ID asc, UID desc" autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="初始化">
              {getFieldDecorator('onload', {
                initialValue: data.onload || 'true'
              })(
                <Select>
                  <Select.Option value="true">加载数据</Select.Option>
                  <Select.Option value="false">不加载数据</Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="主表可选取关联标签,标签关联后,主表数据切换时,下级标签会跟随主表主键值变化。">
                <Icon type="question-circle" />
                {'下级标签'}
              </Tooltip>
            }>
              {getFieldDecorator('subtabs', {
                initialValue: selectTabs
              })(
                <Select
                  mode="multiple"
                  style={{ width: '100%' }}
                  placeholder="Please select"
                >
                  {this.state.currentTabs.map((option, index) =>
                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(SettingForm)
src/templates/modalviewconfig/settingform/index.scss
New file
@@ -0,0 +1,14 @@
.ant-advanced-search-form.commontable-setting-form {
  .textarea {
    .ant-form-item-label {
      width: 16.3%;
    }
    .ant-form-item-control-wrapper {
      width: 83.33333333%;
    }
  }
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
}
src/templates/modalviewconfig/source.jsx
New file
@@ -0,0 +1,375 @@
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/comtable.js'
import enUS from '@/locales/en-US/comtable.js'
const CommonDict = (!localStorage.getItem('lang') || localStorage.getItem('lang') === 'zh-CN') ? zhCN : enUS
class CommonTableBaseData {
  baseConfig = {
    type: 'system',
    enabled: false,
    setting: {
      actionfixed: false,
      columnfixed: false,
      tableName: '',
      tableType: 'checkbox',
      primaryKey: '',
      order: '',
      dataresource: '',
      interType: 'inner',
      innerFunc: '',
      interface: '',
      outerFunc: '',
      onload: 'true',
      subtabs: []
    },
    tables: [],
    search: [
      {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'text',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'like',
        display: 'dropdown'
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'select',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'equal',
        display: 'dropdown'
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'label',
        field: '',
        initval: '',
        type: 'date',
        resourceType: '0',
        setAll: 'false',
        options: [],
        dataSource: '',
        linkField: '',
        valueField: '',
        valueText: '',
        orderBy: '',
        orderType: 'asc',
        match: 'greater',
        display: 'dropdown'
      }
    ],
    action: [
      {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'add',
        intertype: 'inner',
        innerFunc: '',
        interface: '',
        method: 'POST',
        outerFunc: '',
        sql: '',
        sqlType: '',
        callbackFunc: '',
        Ot: 'notRequired',
        position: 'toolbar',
        execSuccess: 'grid',
        execError: 'never',
        errorTime: 15,
        OpenType: 'pop',
        pageTemplate: '',
        url: '',
        icon: 'plus',
        class: 'green',
        verify: null
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'update',
        intertype: 'inner',
        innerFunc: '',
        interface: '',
        method: 'POST',
        outerFunc: '',
        sql: '',
        sqlType: '',
        callbackFunc: '',
        Ot: 'requiredSgl',
        position: 'grid',
        execSuccess: 'grid',
        execError: 'never',
        errorTime: 15,
        OpenType: 'pop',
        pageTemplate: '',
        url: '',
        icon: 'form',
        class: 'purple',
        verify: null
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        label: 'delete',
        intertype: 'inner',
        innerFunc: '',
        interface: '',
        method: 'POST',
        outerFunc: '',
        sql: '',
        sqlType: '',
        callbackFunc: '',
        Ot: 'required',
        position: 'toolbar',
        execSuccess: 'grid',
        execError: 'never',
        errorTime: 15,
        OpenType: 'prompt',
        pageTemplate: '',
        url: '',
        icon: 'delete',
        class: 'red',
        verify: null
      }
    ],
    columns: [
      {
        origin: true,
        uuid: Utils.getuuid(),
        Align: 'left',
        label: 'label',
        field: '',
        Hide: 'false',
        IsSort: 'true',
        type: 'text',
        Width: 120
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        Align: 'left',
        label: 'label',
        field: '',
        Hide: 'false',
        IsSort: 'true',
        type: 'text',
        Width: 120
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        Align: 'left',
        label: 'label',
        field: '',
        Hide: 'false',
        IsSort: 'true',
        type: 'text',
        Width: 120
      }, {
        origin: true,
        uuid: Utils.getuuid(),
        Align: 'left',
        label: 'label',
        field: '',
        Hide: 'false',
        IsSort: 'true',
        type: 'text',
        Width: 120
      }
    ],
    gridBtn: {
      display: false,
      Align: 'center',
      IsSort: 'false',
      uuid: Utils.getuuid(),
      label: CommonDict['header.form.column.action'],
      type: 'action',
      style: 'button',
      show: 'horizontal',
      Width: 120
    },
    tabs: [
      {
        origin: true,          // 是否为示例
        groupId: 'tabs',
        uuid: Utils.getuuid(),
        label: 'tab1',
        icon: '',
        type: 'SubTable',
        linkTab: '',
        subtabs: [],
        supMenu: ''
      },
      {
        origin: true,
        groupId: 'tabs',
        uuid: Utils.getuuid(),
        label: 'tab2',
        icon: '',
        type: 'SubTable',
        linkTab: '',
        subtabs: [],
        supMenu: ''
      }
    ],
    tabgroups: ['tabs']
  }
  searchItems = [
    {
      type: 'search',
      label: '文本框',
      subType: 'text',
      url: ''
    },
    {
      type: 'search',
      label: '下拉框',
      subType: 'select',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(天)',
      subType: 'date',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(周)',
      subType: 'dateweek',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(月)',
      subType: 'datemonth',
      url: ''
    },
    {
      type: 'search',
      label: '时间框(区间)',
      subType: 'daterange',
      url: ''
    }
  ]
  actionItems = [
    {
      type: 'action',
      label: CommonDict['header.form.popform'],
      subType: 'pop',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.prompt'],
      subType: 'prompt',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.exec'],
      subType: 'exec',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.excelIn'],
      subType: 'excelIn',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.excelOut'],
      subType: 'excelOut',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.popview'],
      subType: 'popview',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.tab'],
      subType: 'tab',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.blank'],
      subType: 'blank',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.newpage.inner'],
      subType: 'innerpage',
      url: ''
    },
    {
      type: 'action',
      label: CommonDict['header.form.newpage.outer'],
      subType: 'outerpage',
      url: ''
    }
  ]
  columnItems = [
    {
      type: 'columns',
      label: CommonDict['header.form.text'],
      subType: 'text',
      url: ''
    },
    {
      type: 'columns',
      label: CommonDict['header.form.number'],
      subType: 'number',
      url: ''
    },
    {
      type: 'columns',
      label: CommonDict['header.form.picture'],
      subType: 'picture',
      url: ''
    },
    {
      type: 'columns',
      label: CommonDict['header.form.colspan'],
      subType: 'colspan',
      url: ''
    }
  ]
  tabItems = [
    {
      type: 'tabs',
      label: CommonDict['header.menu.tab.subtable'],
      subType: 'SubTable',
    }
  ]
}
export default new CommonTableBaseData()
src/templates/modalviewconfig/tabdragelement/card.jsx
New file
@@ -0,0 +1,41 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon } from 'antd'
import './index.scss'
const Card = ({ id, type, card, moveCard, findCard, editCard, delCard, hasDrop }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: type, id, originalIndex },
    collect: monitor => ({
      isDragging: monitor.isDragging(),
    }),
  })
  const [, drop] = useDrop({
    accept: type,
    canDrop: () => true,
    drop: (item) => {
      if (!item.hasOwnProperty('originalIndex')) {
        hasDrop(card)
      }
    },
    hover({ id: draggedId }) {
      if (!draggedId) return
      if (draggedId !== id) {
        const { index: overIndex } = findCard(id)
        moveCard(draggedId, overIndex)
      }
    },
  })
  const opacity = isDragging ? 0 : 1
  return (
    <div className="page-card" style={{ opacity: opacity}}>
      <div ref={node => drag(drop(node))}>
        {card.icon ? <Icon type={card.icon} /> : null}
        {card.label}
      </div>
    </div>
  )
}
export default Card
src/templates/modalviewconfig/tabdragelement/index.jsx
New file
@@ -0,0 +1,114 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import update from 'immutability-helper'
import { Tabs, Icon } from 'antd'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const { TabPane } = Tabs
const Container = ({list, type, groupId, placeholder, handleList, handleMenu, deleteMenu }) => {
  let target = null
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
    if (!card) return
    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
    setCards(_cards)
    handleList(type, _cards)
  }
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
    return {
      card,
      index: cards.indexOf(card),
    }
  }
  const hasDrop = (item) => {
    target = item
  }
  const [, drop] = useDrop({
    accept: type,
    drop(item) {
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
      let newcard = {}
      newcard.uuid = Utils.getuuid()
      newcard.label = 'tab'
      newcard.icon = ''
      newcard.type = item.subType
      newcard.linkTab = Utils.getuuid()
      newcard.subtabs = []
      newcard.supMenu = ''
      newcard.groupId = groupId
      let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
      if (target) {
        targetId = target.uuid
      }
      const { index: overIndex } = findCard(`${targetId}`)
      let targetIndex = overIndex
      // if (!target) {
      targetIndex++
      // }
      // if (targetIndex < 0) {
      //   targetIndex = 0
      // }
      const _cards = update(cards, { $splice: [[targetIndex, 0, newcard]] })
      setCards(_cards)
      handleList(type, _cards, newcard)
      target = null
    }
  })
  const edit = (card) => {
    handleMenu(card)
  }
  const del = (card) => {
    deleteMenu({card: card, type: type})
  }
  return (
    <div ref={drop} className="ant-row maintable-tab-list">
      <Tabs defaultActiveKey="0">
        {cards.map((card, index) => (
          <TabPane tab={
            <div key={card.uuid}>
              <Card
                key={card.uuid}
                id={`${card.uuid}`}
                type={type}
                card={card}
                moveCard={moveCard}
                findCard={findCard}
                hasDrop={hasDrop}
              />
              <Icon className="edit" type="edit" onClick={() => edit(card)} />
              <Icon className="edit close" type="close" onClick={() => del(card)} />
            </div>
          } key={`${index}`}>
            《{card.label}》标签内容
          </TabPane>
        ))}
      </Tabs>
      {cards.length === 0 ?
        <div className="commontab-drawarea-placeholder">
          {placeholder}
        </div> : null
      }
    </div>
  )
}
export default Container
src/templates/modalviewconfig/tabdragelement/index.scss
New file
@@ -0,0 +1,21 @@
.common-source-item {
  display: block;
  box-shadow: 0px 0px 2px #bcbcbc;
  padding: 0.4rem 0.7rem;
  background-color: white;
  margin: 0px 0px 10px;
  cursor: move;
  border-radius: 4px;
}
.maintable-tab-list {
  .ant-tabs-content {
    text-align: center;
    color: #bcbcbc;
  }
  .commontab-drawarea-placeholder {
    position: absolute;
    top: 25px;
    left: calc(50% - 50px);
    color: #bcbcbc;
  }
}
src/templates/modalviewconfig/tabform/index.jsx
New file
@@ -0,0 +1,227 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Select, Icon, Tooltip } from 'antd'
import Utils from '@/utils/utils.js'
import './index.scss'
class MainTab extends Component {
  static propTpyes = {
    tabs: PropTypes.array,   // 类型
    type: PropTypes.string,  // 类型
    dict: PropTypes.object,  // 字典项
    formlist: PropTypes.any, // 表单
    card: PropTypes.object   // 标签页信息
  }
  state = {
    formlist: null // 表单
  }
  /**
   * @description 表单预处理
   */
  UNSAFE_componentWillMount () {
    const { formlist } = this.props
    let type = formlist.filter(cell => cell.key === 'type')[0].initVal
    let _initval = formlist.filter(cell => cell.key === 'linkTab')[0].initVal
    let _tabs = this.props.tabs.filter(tab => tab.type === type)
    let initTab = _tabs.filter(tab => tab.uuid === _initval)[0]
    this.setState({
      formlist: formlist.map(item => {
        if (item.key === 'linkTab') {
          item.options = [
            {
              value: '',
              text: '新建'
            },
            ..._tabs
          ]
          if (!initTab) {
            item.initVal = ''
          }
        }
        return item
      })
    })
  }
  /**
   * @description 标签页类型切换
   */
  openTypeChange = (key, value) => {
    const { formlist } = this.state
    if (key === 'type') {
      let _tabs = this.props.tabs.filter(tab => tab.type === value)
      this.setState({
        formlist: formlist.map(item => {
          if (item.key === 'linkTab') {
            item.options = [
              {
                value: '',
                text: '新建'
              },
              ..._tabs
            ]
            item.initVal = ''
            item.hidden = true
          }
          return item
        })
      }, () => {
        this.setState({
          formlist: this.state.formlist.map(item => {
            if (item.key === 'linkTab') {
              item.hidden = false
            }
            return item
          })
        })
      })
      // this.setState({
      //   formlist: this.state.formlist.map(form => {
      //     return form
      //   })
      // }, () => {
      //   this.setState({
      //     formlist: this.state.formlist.map(form => {
      //       return form
      //     })
      //   })
      // })
    }
  }
  getFields() {
    const { getFieldDecorator } = this.props.form
    const fields = []
    this.state.formlist.forEach((item, index) => {
      if (item.hidden) return
      if (item.type === 'text') { // 文本搜索
        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.input'] + item.label + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} />)}
            </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.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  onChange={(value) => {this.openTypeChange(item.key, value)}}
                >
                  {item.options.map((option, i) =>
                    <Select.Option id={'mk' + i} title={option.text} key={'mk' + i} value={option.value}>
                      {item.key === 'icon' && i !== 0 ? <Icon type={option.text} /> : option.text}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'mutilselect') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="标签可关联下级标签,下级标签可选范围:相邻的下侧标签组中未被同级或主表关联的标签。">
                <Icon type="question-circle" />
                {item.label}
              </Tooltip>
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <Select
                  mode="multiple"
                  style={{ width: '100%' }}
                  placeholder="Please select"
                >
                  {item.options.map((option, index) =>
                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          values.uuid = this.props.card.uuid
          values.groupId = this.props.card.groupId
          if (!values.linkTab) { // 没有关联标签(新建时),创建新标签Id
            values.linkTab = Utils.getuuid()
          }
          resolve({
            type: this.props.type,
            values
          })
        } else {
          reject(err)
        }
      })
    })
  }
  render() {
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="ant-advanced-search-form commontable-tab-form">
        <Row gutter={24}>{this.getFields()}</Row>
      </Form>
    )
  }
}
export default Form.create()(MainTab)
src/templates/modalviewconfig/tabform/index.scss
New file
@@ -0,0 +1,7 @@
.ant-advanced-search-form.commontable-tab-form {
  min-height: 180px;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
}
src/templates/tableshare/verifycard/billcodeform/index.jsx
@@ -1,30 +1,54 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Button, notification } from 'antd'
import { Form, Row, Col, Select, Button, InputNumber } from 'antd'
import './index.scss'
class UniqueForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,  // 字典项
    fields: PropTypes.array,  // 表单
    columns: PropTypes.array,  // 表单
    modular: PropTypes.array,  // 表单
    modularDetail: PropTypes.array,  // 表单
    orderChange: PropTypes.func,  // 表单
    orderCheck: PropTypes.func  // 表单
    orderChange: PropTypes.func  // 表单
  }
  state = {
    editItem: null,
    modularDetail: []
    modularDetail: [],
    funFields: [],
    billFields: [],
    type: '1'
  }
  UNSAFE_componentWillMount() {
    let _modularDetail = []
    let _billFields = []
    if (this.props.modular.length > 0) {
      this.setState({
        modularDetail: this.props.modularDetail.filter(item => item.BID === this.props.modular[0].ID)
      })
      _modularDetail = this.props.modularDetail.filter(item => item.BID === this.props.modular[0].ID)
    }
    let fieldMap = new Map()
    this.props.fields.forEach(_field => {
      if (_field.type === 'text' && !fieldMap.has(_field.field)) {
        _billFields.push(_field)
        fieldMap.set(_field.field, true)
      }
    })
    this.props.columns.forEach(_field => {
      if (_field.type === 'text' && !fieldMap.has(_field.field)) {
        _billFields.push(_field)
        fieldMap.set(_field.field, true)
      }
    })
    this.setState({
      modularDetail: _modularDetail,
      funFields: this.props.fields.filter(field => field.type === 'funcvar'),
      billFields: _billFields
    })
  }
  UNSAFE_componentWillReceiveProps (nextProps) {
@@ -34,34 +58,41 @@
      this.setState({
        modularDetail: _detail
      })
      this.props.form.setFieldsValue({
        ModularCode: nextProps.modular[0].ID,
        ModularDetailCode: _detail[0] ? _detail[0].ModularDetailCode : ''
      })
    }
  }
  edit = (record) => {
    let _detail = this.props.modularDetail.filter(item => item.BID === record.ModularCode)
    let _modularDetailCode = ''
    if (record.TypeCharOne === 'Y' || record.TypeCharOne === 'n') {
      let _detail = this.props.modularDetail.filter(item => item.BID === record.ModularCode)
      let _modularDetailCode = ''
    if (_detail.filter(item => item.ModularDetailCode === record.ModularDetailCode).length > 0) {
      _modularDetailCode = record.ModularDetailCode
      if (_detail.filter(item => item.ModularDetailCode === record.ModularDetailCode).length > 0) {
        _modularDetailCode = record.ModularDetailCode
      }
      this.setState({
        type: '2',
        editItem: record,
        modularDetail: _detail
      })
      this.props.form.setFieldsValue({
        field: record.field,
        TypeCharOne: record.TypeCharOne,
        ModularCode: record.ModularCode,
        ModularDetailCode: _modularDetailCode,
      })
    } else {
      this.setState({
        type: '1',
        editItem: record,
      })
      this.props.form.setFieldsValue({
        field: record.field,
        TypeCharOne: record.TypeCharOne,
        Type: record.Type,
        linkField: record.linkField,
      })
    }
    this.setState({
      editItem: record,
      modularDetail: _detail
    })
    this.props.form.setFieldsValue({
      field: record.field,
      TypeCharOne: record.TypeCharOne,
      ModularCode: record.ModularCode,
      ModularDetailCode: _modularDetailCode,
      errmsg: record.errmsg,
      errorCode: record.errorCode
    })
  }
  voucherChange = (value) => {
@@ -75,12 +106,28 @@
    })
  }
  TypeCharOneChange = (value) => {
    if (value === 'Y' || value === 'n') {
      this.setState({
        type: '2'
      })
    } else {
      this.setState({
        type: '1'
      })
    }
  }
  handleConfirm = () => {
    const { funFields, billFields } = this.state
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (!err) {
        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
        let _funField = funFields.filter(item => item.field === values.field)[0]
        values.fieldName = _funField ? _funField.label : ''
        // 一级菜单值为20190203125926873D6029A9C511041719420时TypeCharTwo=TableCode,其他用BillCode
        if (values.ModularCode === '20190203125926873D6029A9C511041719420') {
          values.TypeCharTwo = 'TableCode'
@@ -96,22 +143,17 @@
        }
        values.billType = billType[values.TypeCharOne]
        // 设置凭证类型中文名
        let ModularCode = this.props.modular.filter(item => item.ID === values.ModularCode)[0]
        values.ModularCodeName = ModularCode.NameNO
        if (values.TypeCharOne === 'Y' || values.TypeCharOne === 'n') {
          // 设置凭证类型中文名
          let ModularCode = this.props.modular.filter(item => item.ID === values.ModularCode)[0]
          values.ModularCodeName = ModularCode.NameNO
        // 设置流水号位数
        let _detail = this.state.modularDetail.filter(item => item.ModularDetailCode === values.ModularDetailCode)[0]
        values.Type = _detail.Type
        let _vaild = this.props.orderCheck(values)
        if (!_vaild) {
          notification.warning({
            top: 92,
            message: '字段值不可重复',
            duration: 10
          })
          return
          // 设置流水号位数
          let _detail = this.state.modularDetail.filter(item => item.ModularDetailCode === values.ModularDetailCode)[0]
          values.Type = _detail.Type
        } else {
          let _billField = billFields.filter(item => item.field === values.linkField)[0]
          values.linkFieldName = _billField ? _billField.label : ''
        }
        this.props.orderChange(values)
@@ -120,7 +162,6 @@
        })
        this.props.form.setFieldsValue({
          field: '',
          errmsg: ''
        })
      }
    })
@@ -128,6 +169,7 @@
  render() {
    const { getFieldDecorator } = this.props.form
    const { type } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -138,34 +180,35 @@
        sm: { span: 14 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
          <Col span={6}>
            <Form.Item label={'字段'}>
          <Col span={5}>
            <Form.Item label={'函数变量'}>
              {getFieldDecorator('field', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '字段!'
                  },
                  {
                    pattern: /^[0-9a-zA-Z_]*$/,
                    message: '名称只允许包含数字、字母和下划线,且以指定字符开始。'
                  },
                  {
                    max: 50,
                    message: '名称不超过50个字符。'
                    message: this.props.dict['form.required.input'] + '函数变量!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
              })(
                <Select>
                  {this.state.funFields.map(option =>
                    <Select.Option id={option.uuid} key={option.uuid} value={option.field}>
                      {option.label}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={6}>
          <Col span={5}>
            <Form.Item label={'类型'}>
              {getFieldDecorator('TypeCharOne', {
                initialValue: 'Y',
                initialValue: 'Lp',
                rules: [
                  {
                    required: true,
@@ -173,7 +216,7 @@
                  }
                ]
              })(
                <Select>
                <Select onChange={(value) => {this.TypeCharOneChange(value)}}>
                  <Select.Option value="Y"> 单号 </Select.Option>
                  <Select.Option value="n"> 编码 </Select.Option>
                  <Select.Option value="Lp"> 行号 </Select.Option>
@@ -182,7 +225,41 @@
              )}
            </Form.Item>
          </Col>
          <Col span={6}>
          {type === '1' ? <Col span={5}>
            <Form.Item label={'关联字段'}>
              {getFieldDecorator('linkField', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '关联字段!'
                  }
                ]
              })(
                <Select>
                  {this.state.billFields.map(option =>
                    <Select.Option id={option.uuid} key={option.uuid} value={option.field}>
                      {option.label}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col> : null}
          {type === '1' ? <Col span={5}>
            <Form.Item label={'位数'}>
              {getFieldDecorator('Type', {
                initialValue: 4,
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '位数!'
                  }
                ]
              })(<InputNumber min={1} max={10} precision={0} />)}
            </Form.Item>
          </Col> : null}
          {type === '2' ? <Col span={5}>
            <Form.Item label={'凭证类型'}>
              {getFieldDecorator('ModularCode', {
                initialValue: this.props.modular[0] ? this.props.modular[0].ID : '',
@@ -206,8 +283,8 @@
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={6}>
          </Col> : null}
          {type === '2' ? <Col span={5}>
            <Form.Item label={'凭证标识'}>
              {getFieldDecorator('ModularDetailCode', {
                initialValue: this.state.modularDetail[0] ? this.state.modularDetail[0].ModularDetailCode : '',
@@ -230,44 +307,9 @@
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'提示信息'}>
              {getFieldDecorator('errmsg', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '提示信息!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'报错编码'}>
              {getFieldDecorator('errorCode', {
                initialValue: 'E',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.select'] + '报错编码!'
                  }
                ]
              })(
                <Select>
                  <Select.Option value="E"> E </Select.Option>
                  <Select.Option value="N"> N </Select.Option>
                  <Select.Option value="F"> F </Select.Option>
                  <Select.Option value="NM"> NM </Select.Option>
                  <Select.Option value="S"> S </Select.Option>
                  <Select.Option value="-1"> -1 </Select.Option>
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={2}>
            <Button onClick={this.handleConfirm} type="primary" className="add-row">
          </Col> : null}
          <Col span={4} className="add">
            <Button onClick={this.handleConfirm} type="primary">
              确定
            </Button>
          </Col>
src/templates/tableshare/verifycard/customform/index.jsx
@@ -83,13 +83,13 @@
            </Button>
          </Col>
          <Col span={7}>
            <Form.Item label={'结果'}>
            <Form.Item label={'结果处理'}>
              {getFieldDecorator('result', {
                initialValue: 'true',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.select'] + '结果!'
                    message: this.props.dict['form.required.select'] + '结果处理!'
                  }
                ]
              })(
@@ -131,8 +131,6 @@
                  <Select.Option value="N"> N </Select.Option>
                  <Select.Option value="F"> F </Select.Option>
                  <Select.Option value="NM"> NM </Select.Option>
                  <Select.Option value="S"> S </Select.Option>
                  <Select.Option value="-1"> -1 </Select.Option>
                </Select>
              )}
            </Form.Item>
src/templates/tableshare/verifycard/index.jsx
@@ -30,22 +30,17 @@
      {
        title: '字段名',
        dataIndex: 'field',
        width: '50%'
        width: '55%'
      },
      {
        title: '提示信息',
        dataIndex: 'errmsg',
        width: '25%'
      },
      {
        title: 'ErrorCode',
        title: '报错编码',
        dataIndex: 'errorCode',
        width: '10%'
        width: '20%'
      },
      {
        title: '操作',
        align: 'center',
        width: '15%',
        width: '25%',
        dataIndex: 'operation',
        render: (text, record) =>
          (<div>
@@ -62,20 +57,20 @@
      {
        title: 'SQL',
        dataIndex: 'sql',
        width: '40%'
        width: '50%'
      },
      {
        title: '结果',
        title: '结果处理',
        dataIndex: 'resultName',
        width: '10%'
      },
      {
        title: '提示信息',
        dataIndex: 'errmsg',
        width: '25%'
        width: '15%'
      },
      {
        title: 'ErrorCode',
        title: '报错编码',
        dataIndex: 'errorCode',
        width: '10%'
      },
@@ -97,38 +92,35 @@
    ],
    orderColumns: [
      {
        title: '字段',
        dataIndex: 'field',
        width: '15%'
        title: '函数变量',
        dataIndex: 'fieldName',
        width: '15%',
        render: (text, record) => (`${record.fieldName || ''}(${record.field})`)
      },
      {
        title: '类型',
        dataIndex: 'billType',
        width: '10%',
        width: '15%',
      },
      {
        title: '凭证类型',
        dataIndex: 'ModularCodeName',
        width: '10%'
        width: '15%'
      },
      {
        title: '凭证标识',
        dataIndex: 'ModularDetailCode',
        width: '10%'
        width: '15%'
      },
      {
        title: '流水号位数',
        title: '关联字段',
        dataIndex: 'linkFieldName',
        width: '15%',
        render: (text, record) => (record.linkField ? `${record.linkFieldName || ''}(${record.linkField})` : '')
      },
      {
        title: '位数',
        dataIndex: 'Type',
        width: '10%'
      },
      {
        title: '提示信息',
        dataIndex: 'errmsg',
        width: '20%'
      },
      {
        title: 'ErrorCode',
        dataIndex: 'errorCode',
        width: '10%'
      },
      {
@@ -397,17 +389,6 @@
    })
  }
  orderCheck = (values) => {
    let verify = JSON.parse(JSON.stringify(this.state.verify))
    let valid = true
    verify.billcodes.forEach(item => {
      if (item.uuid !== values.uuid && item.field === values.field) {
        valid = false
      }
    })
    return valid
  }
  onOptionChange = (e, key) => {
    const { verify } = this.state
    let value = e.target.value
@@ -630,11 +611,12 @@
        </TabPane>
        <TabPane tab="单号生成" key="4">
          <BillcodeForm
            fields={fields}
            columns={this.props.columns}
            dict={this.props.dict}
            modular={orderModular}
            modularDetail={orderModularDetail}
            orderChange={this.orderChange}
            orderCheck={this.orderCheck}
            wrappedComponentRef={(inst) => this.orderForm = inst}
          />
          <Table
@@ -650,6 +632,7 @@
          <VoucherForm
            dict={this.props.dict}
            voucher={voucher}
            columns={this.props.columns}
            voucherobj={verify.voucher}
            voucherDetail={voucherDetail}
            voucherChange={this.voucherChange}
src/templates/tableshare/verifycard/uniqueform/index.jsx
@@ -1,37 +1,48 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Select, Button } from 'antd'
import { Form, Row, Col, Select, Button } from 'antd'
import './index.scss'
class UniqueForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,  // 字典项
    fields: PropTypes.array,  // 表单
    uniqueChange: PropTypes.func  // 表单
    dict: PropTypes.object,       // 字典项
    fields: PropTypes.array,      // 表单字段
    uniqueChange: PropTypes.func  // 修改函数
  }
  state = {
    editItem: null
    editItem: null // 编辑元素
  }
  edit = (record) => {
    this.setState({
      editItem: record
    })
    this.props.form.setFieldsValue({
      field: record.field.split(','),
      errmsg: record.errmsg,
      errorCode: record.errorCode
    })
  }
  handleConfirm = () => {
    const { fields } = this.props
    // 表单提交时检查输入值是否正确
    this.props.form.validateFieldsAndScroll((err, values) => {
      if (!err) {
        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
        values.fieldlabel = values.field.map(field => {
          let item = fields.filter(cell => cell.field === field)[0]
          let label = ''
          if (item) {
            label = item.label
          }
          return label
        })
        values.fieldlabel = values.fieldlabel.join(',')
        values.field = values.field.join(',')
        this.props.uniqueChange(values)
@@ -40,7 +51,6 @@
        })
        this.props.form.setFieldsValue({
          field: [],
          errmsg: ''
        })
      }
    })
@@ -59,6 +69,7 @@
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form" id="verifycard1">
        <Row gutter={24}>
@@ -84,19 +95,6 @@
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'提示信息'}>
              {getFieldDecorator('errmsg', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '提示信息!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'报错编码'}>
              {getFieldDecorator('errorCode', {
                initialValue: 'E',
@@ -112,8 +110,6 @@
                  <Select.Option value="N"> N </Select.Option>
                  <Select.Option value="F"> F </Select.Option>
                  <Select.Option value="NM"> NM </Select.Option>
                  <Select.Option value="S"> S </Select.Option>
                  <Select.Option value="-1"> -1 </Select.Option>
                </Select>
              )}
            </Form.Item>
src/templates/tableshare/verifycard/voucherform/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Switch } from 'antd'
import { Form, Row, Col, Select, Switch } from 'antd'
import './index.scss'
@@ -10,33 +10,33 @@
    dict: PropTypes.object,         // 字典项
    voucherobj: PropTypes.object,   // 凭证信息
    voucher: PropTypes.array,       // 表单
    columns: PropTypes.array,       // 表单
    voucherDetail: PropTypes.array, // 表单
    voucherChange: PropTypes.func  // 表单
    voucherChange: PropTypes.func   // 表单
  }
  state = {
    voucher: [],
    voucherDetail: []
    voucherDetail: [],
    columns: []
  }
  UNSAFE_componentWillMount() {
    let _voucher = []
    let _voucherDetail = []
    if (this.props.voucherobj.BVoucherType && this.props.voucher.length > 0) {
      let _voucher = this.props.voucher.filter(item => item.TypeCharOne === this.props.voucherobj.BVoucherType)
      let _voucherDetail = this.props.voucherDetail.filter(item => item.BID === this.props.voucherobj.VoucherType)
      this.setState({
        voucher: _voucher,
        voucherDetail: _voucherDetail
      })
      _voucher = this.props.voucher.filter(item => item.TypeCharOne === this.props.voucherobj.BVoucherType)
      _voucherDetail = this.props.voucherDetail.filter(item => item.BID === this.props.voucherobj.VoucherType)
    } else if (this.props.voucher.length > 0) {
      let _voucher = this.props.voucher.filter(item => item.TypeCharOne === 'MES')
      let _voucherDetail = this.props.voucherDetail.filter(item => _voucher[0] && item.BID === _voucher[0].ID)
      this.setState({
        voucher: _voucher,
        voucherDetail: _voucherDetail
      })
      _voucher = this.props.voucher.filter(item => item.TypeCharOne === 'MES')
      _voucherDetail = this.props.voucherDetail.filter(item => _voucher[0] && item.BID === _voucher[0].ID)
    }
    this.setState({
      voucher: _voucher,
      voucherDetail: _voucherDetail,
      columns: this.props.columns.filter(col => col.type === 'text')
    })
  }
  UNSAFE_componentWillReceiveProps (nextProps) {
@@ -137,6 +137,7 @@
  render() {
    const { getFieldDecorator } = this.props.form
    const { voucherobj } = this.props
    const { columns } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -147,6 +148,7 @@
        sm: { span: 14 }
      }
    }
    return (
      <Form {...formItemLayout} className="verify-form">
        <Row gutter={24}>
@@ -221,36 +223,26 @@
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'提示信息'}>
              {getFieldDecorator('errmsg', {
                initialValue: voucherobj.errmsg ||'',
            <Form.Item label={'关联字段'}>
              {getFieldDecorator('linkField', {
                initialValue: voucherobj.linkField || (columns[0] && columns[0].field) || '',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.input'] + '提示信息!'
                  }
                ]
              })(<Input placeholder="" onChange={this.contentChange} autoComplete="off" />)}
            </Form.Item>
          </Col>
          <Col span={6}>
            <Form.Item label={'报错编码'}>
              {getFieldDecorator('errorCode', {
                initialValue: voucherobj.errmsg || 'E',
                rules: [
                  {
                    required: true,
                    message: this.props.dict['form.required.select'] + '报错编码!'
                    message: this.props.dict['form.required.input'] + '关联字段!'
                  }
                ]
              })(
                <Select onChange={this.contentChange}>
                  <Select.Option value="E"> E </Select.Option>
                  <Select.Option value="N"> N </Select.Option>
                  <Select.Option value="F"> F </Select.Option>
                  <Select.Option value="NM"> NM </Select.Option>
                  <Select.Option value="S"> S </Select.Option>
                  <Select.Option value="-1"> -1 </Select.Option>
                <Select
                  showSearch
                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  onChange={this.contentChange}
                >
                  {columns.map((option, index) =>
                    <Select.Option id={index + option.uuid} key={index + option.uuid} value={option.field}>
                      {option.label}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
src/utils/utils.js
@@ -448,41 +448,139 @@
   * @return {String} type   执行类型
   * @return {String} table  表名
   */
  static getSysDefaultSql (btn, setting, formdata, primaryId) {
    // let verify = btn.verify
    let primaryKey = setting.primaryKey || 'id'
    let _sql = ''
  static getSysDefaultSql (btn, setting, formdata, primaryId, data) {
    let verify = btn.verify
    let _formFieldValue = {}
    // if (verify && verify.accountdate === 'true') {
    //   _sql += `exec s_FIBVoucherDateCheck @ErrorCode=@ErrorCode OUTPUT,@retmsg=@retmsg OUTPUT
    //     if @ErrorCode!=''
    //     GOTO aaa
    //     `
    // }
    // if (verify && verify.invalid === 'true') {
    //   _sql += `Declare @tbid nvarchar(50), @ErrorCode nvarchar(50),
    //     @retmsg nvarchar(4000)
    //     Select @tbid='', @ErrorCode='',@retmsg=''
    //     Select @tbid=${primaryKey} from ${btn.sql} where ${primaryKey} ='${primaryId}'
    //     If @tbid=''
    //     Begin
    //       Setect @ErrorCode='E',@retmsg='数据已失效'
    //       goto aaa
    //     end
    //     `
    // }
    if (formdata) { // 获取字段键值对
      formdata.forEach(form => {
        _formFieldValue[form.key] = form.value
      })
    }
    console.log(btn)
    console.log(formdata)
    console.log(primaryId)
    let primaryKey = setting.primaryKey || 'id' // 主键字段
    // 系统变量声明与设置初始值
    let _sql = `Declare @tbid nvarchar(50), @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50)
      Select @tbid='', @ErrorCode='',@retmsg='',@BVoucher='',@FIBVoucherDate='',@FiYear=''
      `
    if (verify && verify.accountdate === 'true') { // 启用账期验证
      _sql += `exec s_FIBVoucherDateCheck @ErrorCode=@ErrorCode OUTPUT,@retmsg=@retmsg OUTPUT
        if @ErrorCode!=''
          GOTO aaa
        `
    }
    if (btn.sqlType !== 'insert' && verify && verify.invalid === 'true') { // 失效验证,添加数据时不用
      _sql += `Select @tbid=${primaryKey} from ${btn.sql} where ${primaryKey} ='${primaryId}'
        If @tbid=''
        Begin
          Setect @ErrorCode='E',@retmsg='数据已失效'
          goto aaa
        end
        `
    }
    if (formdata && verify && verify.uniques.length > 0) { // 唯一性验证,必须存在表单(表单存在时,主键均为单值)
      let _primaryId = primaryId
      if (btn.sqlType === 'insert') {
        _primaryId = ''
      }
      verify.uniques.forEach(item => {
        let _fieldValue = [] // 表单键值对field=value
        let _value = []      // 表单值,用于错误提示
        item.field.split(',').forEach(_field => {
          _fieldValue.push(`${_field}='${_formFieldValue[_field]}'`)
          _value.push(_formFieldValue[_field])
        })
        _sql += `Select @tbid='', @ErrorCode='',@retmsg=''
          Select @tbid='X' from ${btn.sql} where ${_fieldValue.join(' and ')} and ${primaryKey} !='${_primaryId}'
          If @tbid!=''
          Begin
            Setect @ErrorCode='${item.errorCode}',@retmsg='${item.fieldlabel || ''}:${_value.join(' ')} 已存在'
            goto aaa
          end
          `
      })
    }
    if (verify && verify.customverifys.length > 0) { // 自定义验证
      verify.customverifys.forEach(item => {
        let _cuSql = item.sql
        if (data) {
          _formFieldValue = {...data, ..._formFieldValue}
        }
        let keys = Object.keys(_formFieldValue)
        keys = keys.sort((a, b) => {
          return b.length - a.length
        })
        keys.forEach(key => {
          _cuSql.replace('@' + key, _formFieldValue[key])
        })
        _sql += `Select @tbid='', @ErrorCode='',@retmsg=''
          Select top 1 @tbid='X' from (${_cuSql}) a
          If @tbid ${item.result === 'true' ? '=' : '!='}''
          Begin
            Setect @ErrorCode='${item.errorCode}',@retmsg='${item.errmsg}'
            goto aaa
          end
          `
      })
    }
    if (verify && verify.billcodes.length > 0) {
      verify.billcodes.forEach(item => {
        let _ModularDetailCode = ''
        if (item.TypeCharOne === 'Lp' || item.TypeCharOne === 'BN') {
          _ModularDetailCode = item.TypeCharOne + item.linkField
        } else {
          _ModularDetailCode = item.ModularDetailCode
        }
        _sql += `Declare @${item.field} nvarchar(50)
          select @BillCode='', @${item.field}=''
          exec s_get_BillCode
            @ModularDetailCode='${_ModularDetailCode}',
            @Type=${item.Type},
            @TypeCharOne='${item.TypeCharOne}',
            @TypeCharTwo ='${item.TypeCharTwo}',
            @BillCode =@BillCode output,
            @ErrorCode =@ErrorCode output,
            @retmsg=@retmsg output
          if @ErrorCode!=''
            goto aaa
          set @${item.field}=@BillCode
          `
      })
    }
    if (verify && verify.voucher && verify.voucher.enabled) { // 凭证-显示列中选取,必须选行
      let _voucher = verify.voucher
      _sql += `exec s_BVoucher_Create
          @Bill ='${data[_voucher.linkField]}',
          @BVoucherType ='${_voucher.BVoucherType}',
          @VoucherTypeOne ='${_voucher.VoucherTypeOne}',
          @VoucherTypeTwo ='${_voucher.VoucherTypeTwo}',
          @Type =${_voucher.Type},
          @BVoucher =@BVoucher OUTPUT ,
          @FIBVoucherDate =@FIBVoucherDate OUTPUT ,
          @FiYear =@FiYear OUTPUT ,
          @ErrorCode =@ErrorCode OUTPUT,
          @retmsg=@retmsg OUTPUT
        if @ErrorCode!=''
          GOTO aaa
        `
    }
    if (btn.OpenType === 'pop' && btn.sqlType === 'insert') {
      let keys = []
      let values = []
      formdata.forEach(item => {
        if (!item.readonly && item.type === 'number') {
        if (item.type === 'number') {
          keys.push(item.key)
          values.push(item.value)
        } else if (!item.readonly) {
        } else {
          keys.push(item.key)
          values.push('\'' + item.value + '\'')
        }
@@ -494,19 +592,23 @@
    } else if (btn.OpenType === 'pop' && btn.sqlType === 'update') {
      let _form = []
      formdata.forEach(item => {
        if (!item.readonly && item.type === 'number') {
        if (item.type === 'number') {
          _form.push(item.key + '=' + item.value)
        } else if (!item.readonly) {
        } else {
          _form.push(item.key + '=\'' + item.value + '\'')
        }
      })
      _form = _form.join(',')
      _sql += `update ${btn.sql} set ${_form},modifydate=getdate(),modifyuserid=@userid where ${primaryKey}=@${primaryKey}`
      _sql += `update ${btn.sql} set ${_form},modifydate=getdate(),modifyuserid=@userid,BVoucher=@BVoucher,FIBVoucherDate=@FIBVoucherDate,FiYear=@FiYear where ${primaryKey}=@${primaryKey}`
    } else if ((btn.OpenType === 'prompt' || btn.OpenType === 'exec') && btn.sqlType === 'LogicDelete') { // 逻辑删除
      _sql += `update ${btn.sql} set deleted=1,modifydate=getdate(),modifyuserid=@userid where ${primaryKey}=@${primaryKey}`
    } else if ((btn.OpenType === 'prompt' || btn.OpenType === 'exec') && btn.sqlType === 'delete') {
      _sql += `insert into snote (remark,createuserid) select '删除表:${btn.sql} 数据: ${primaryKey}='+@${primaryKey},@userid delete ${btn.sql} where ${primaryKey}=@${primaryKey}`
    }
    _sql += `
      aaa:
      select @ErrorCode= as ErrorCode,@retmsg as ErrorCode
      `
    console.log(_sql)
    return _sql
  }
@@ -652,7 +754,6 @@
   * @return {String}
   */
  static getfunc (param, btn, menu, config) {
    console.log(menu)
    let form = ''
    let formParam = ''
    let columns = config.columns