king
2020-04-29 9b6ce1a5778c6e1a813237e87588c0052aae1bbb
src/templates/comtableconfig/index.jsx
@@ -4,13 +4,14 @@
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 { Button, Card, Modal, Collapse, notification, Spin, Select, List, Icon, Empty, Switch, Tooltip, message } from 'antd'
import moment from 'moment'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/comtable.js'
import enUS from '@/locales/en-US/comtable.js'
import options from '@/store/options.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import { getSearchForm, getActionForm, getColumnForm } from '@/templates/zshare/formconfig'
import { queryTableSql } from '@/utils/option.js'
@@ -19,7 +20,6 @@
import TabForm from '@/templates/zshare/tabform'
import SearchForm from '@/templates/zshare/searchform'
import ColumnForm from '@/templates/zshare/columnform'
import PasteForm from '@/templates/zshare/pasteform'
import DragElement from '@/templates/zshare/dragelement'
import ColspanForm from '@/templates/zshare/colspanform'
import GridBtnForm from '@/templates/zshare/gridbtnform'
@@ -30,7 +30,7 @@
import VerifyCardPrint from '@/templates/zshare/verifycardprint'
import MenuForm from '@/templates/zshare/menuform'
import TabDragElement from '@/templates/zshare/tabdragelement'
import TransferForm from '@/components/transferform'
import EditComponent from '@/templates/zshare/editcomponent'
import SourceElement from '@/templates/zshare/dragelement/source'
import CreateFunc from '@/templates/zshare/createfunc'
import CreateInterface from '@/templates/zshare/createinterface'
@@ -76,10 +76,9 @@
    tabviews: [],            // 所有标签页
    profileVisible: false,   // 验证信息模态框
    optionLibs: null,        // 自定义下拉选项库
    thawBtnVisible: false,   // 解冻按钮弹窗
    thawbtnlist: null,       // 解冻按钮列表
    thawButtons: [],         // 已选择要解冻的按钮
    activeKey: '0'           // 默认展开基本信息
    activeKey: '0',          // 默认展开基本信息
    sqlVerifing: false       // sql验证
  }
  /**
@@ -123,6 +122,7 @@
    _config.tabgroups = _config.tabgroups || ['tabs']
    _config.setting.subtabs = _config.setting.subtabs || []
    _config.Template = 'CommonTable'
    _config.easyCode = _config.easyCode || ''
    
    let _oriActions = []
    if (_config.type === 'user') {
@@ -223,6 +223,14 @@
            MenuID: 'currenttab',
            text: this.state.dict['header.form.currenttab']
          }]
        },
        {
          type: 'text',
          key: 'easyCode',
          label: this.state.dict['header.form.easyCode'],
          initVal: _config.easyCode,
          required: false,
          readonly: false
        }
      ]
    })
@@ -255,7 +263,7 @@
        notification.warning({
          top: 92,
          message: res.message,
          duration: 10
          duration: 5
        })
      }
    })
@@ -317,7 +325,7 @@
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
            duration: 5
          })
        }
      })
@@ -344,7 +352,7 @@
        notification.warning({
          top: 92,
          message: res.message,
          duration: 10
          duration: 5
        })
      }
    })
@@ -393,7 +401,7 @@
        notification.warning({
          top: 92,
          message: res.message,
          duration: 10
          duration: 5
        })
      }
    })
@@ -434,12 +442,10 @@
   * @description 搜索条件编辑,获取搜索条件表单信息
   */
  handleSearch = (card) => {
    const { menu } = this.props
    this.setState({
      modaltype: 'search',
      card: card,
      formlist: getSearchForm(card, menu.roleList)
      formlist: getSearchForm(card, this.props.sysRoles)
    })
  }
@@ -449,8 +455,8 @@
  handleAction = (card, type) => {
    let ableField = this.props.permFuncField.join(', ')
    let functip = <div>
      <p style={{marginBottom: '5px'}}>{this.state.dict['header.modal.func.innerface'].replace('@ableField', ableField)}</p>
      <p>{this.state.dict['header.modal.func.outface']}</p>
      <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
      <p>{this.state.dict['model.tooltip.func.outface']}</p>
    </div>
    this.setState({
@@ -465,13 +471,78 @@
   */
  handleColumn = (card) => {
    const { menu } = this.props
    if (card.type !== 'colspan') {
      this.setState({
        modaltype: 'columns',
        card: card,
        formlist: getColumnForm(card, menu.roleList)
      let menulist = menu.fstMenuList.map(item => {
        return {
          value: item.MenuID,
          label: item.text,
          isLeaf: false
        }
      })
      if ((card.type === 'text' || card.type === 'number') && card.linkmenu && card.linkmenu.length > 0) {
        let _param = {
          func: 'sPC_Get_FunMenu',
          ParentID: card.linkmenu[0],
          systemType: options.systemType,
          debug: 'Y'
        }
        this.setState({
          loading: true
        })
        Api.getSystemConfig(_param).then(result => {
          if (result.status) {
            menulist = menulist.map(item => {
              if (item.value === card.linkmenu[0]) {
                item.children = result.data.map(item => {
                  let submenu = {
                    value: item.ParentID,
                    label: item.MenuNameP,
                    children: item.FunMenu.map(cell => {
                      return {
                        value: cell.MenuID,
                        label: cell.MenuName,
                        MenuID: cell.MenuID,
                        MenuName: cell.MenuName,
                        MenuNo: cell.MenuNo,
                        Ot: cell.Ot,
                        PageParam: cell.PageParam,
                        LinkUrl: cell.LinkUrl,
                        disabled: cell.MenuID === menu.MenuID
                      }
                    })
                  }
                  return submenu
                })
              }
              return item
            })
          } else {
            notification.warning({
              top: 92,
              message: result.message,
              duration: 5
            })
          }
          this.setState({
            loading: false,
            modaltype: 'columns',
            card: card,
            formlist: getColumnForm(card, this.props.sysRoles, menulist)
          })
        })
      } else {
        this.setState({
          modaltype: 'columns',
          card: card,
          formlist: getColumnForm(card, this.props.sysRoles, menulist)
        })
      }
    } else {
      this.setState({
        modaltype: 'colspan',
@@ -589,9 +660,33 @@
          type: 'mutilselect',
          key: 'equalTab',
          label: this.state.dict['header.form.equalTab'],
          tooltip: '如果子标签中含有刷新同级标签的按钮,在此处添加需要刷新的标签。',
          initVal: equalTab,
          required: false,
          options: equalTabs
        },
        {
          type: 'text',
          key: 'foreignKey',
          label: '外键',
          tooltip: '外键旨在标签页中执行默认函数(添加)时,替换BID字段',
          initVal: card.foreignKey || '',
          required: false
        },
        {
          type: 'radio',
          key: 'searchPass',
          label: '主表搜索',
          initVal: card.searchPass || 'false',
          tooltip: '使用主表搜索条件时,主表的搜索条件会传入子表中。',
          required: false,
          options: [{
            value: 'true',
            text: '使用'
          }, {
            value: 'false',
            text: '不使用'
          }]
        }
      ]
    })
@@ -632,20 +727,83 @@
          })
        }
        let fieldrepet = false // 字段重复
        let labelrepet = false // 提示文字重复
        let _search = config.search.map(item => {
          if (item.uuid !== res.uuid && res.field && item.field) {
            if (item.field === res.field) {
              fieldrepet = true
            } else if (item.label === res.label) {
              labelrepet = true
            }
          }
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        if (fieldrepet) {
          notification.warning({
            top: 92,
            message: '字段已存在!',
            duration: 5
          })
          return
        } else if (labelrepet) {
          notification.warning({
            top: 92,
            message: '名称已存在!',
            duration: 5
          })
          return
        }
        _search = _search.filter(item => !item.origin)
        this.setState({
          config: {...config, search: _search},
          optionLibs: optionLibs,
          modaltype: ''
        })
        if ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && res.resourceType === '1' && /\s/.test(res.dataSource)) {
          this.setState({
            sqlVerifing: true
          })
          let param = {
            func: 's_debug_sql',
            LText: res.dataSource
          }
          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)
          if (window.GLOB.mainSystemApi && res.database === 'sso') {
            param.rduri = window.GLOB.mainSystemApi
          }
          Api.getLocalConfig(param).then(result => {
            if (result.status) {
              this.setState({
                sqlVerifing: false,
                config: {...config, search: _search},
                optionLibs: optionLibs,
                modaltype: ''
              })
            } else {
              this.setState({sqlVerifing: false})
              Modal.error({
                title: result.message
              })
            }
          })
        } else {
          this.setState({
            config: {...config, search: _search},
            optionLibs: optionLibs,
            modaltype: ''
          })
        }
      })
    } else if (modaltype === 'actionEdit' || modaltype === 'actionCopy') {
      this.actionFormRef.handleConfirm().then(res => {
@@ -707,7 +865,7 @@
                  notification.warning({
                    top: 92,
                    message: response.message,
                    duration: 10
                    duration: 5
                  })
                } else {
                  this.setState({
@@ -782,7 +940,7 @@
                  notification.warning({
                    top: 92,
                    message: response.message,
                    duration: 10
                    duration: 5
                  })
                } else {
                  this.setState({
@@ -821,13 +979,41 @@
      })
    } else if (modaltype === 'columns' || modaltype === 'colspan') {
      this.columnFormRef.handleConfirm().then(res => {
        let fieldrepet = false // 字段重复
        let labelrepet = false // 提示文字重复
        let _columns = config.columns.map(item => {
          if (item.uuid !== res.uuid && res.field && item.field) {
            if (item.field === res.field) {
              fieldrepet = true
            } else if (item.label === res.label) {
              labelrepet = true
            }
          }
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        if (fieldrepet) {
          notification.warning({
            top: 92,
            message: '字段已存在!',
            duration: 5
          })
          return
        } else if (labelrepet) {
          notification.warning({
            top: 92,
            message: '名称已存在!',
            duration: 5
          })
          return
        }
        _columns = _columns.filter(item => !item.origin)
        this.setState({
@@ -867,7 +1053,7 @@
  editModalCancel = () => {
    const { config, card, modaltype } = this.state
    if (card.focus) {
    if (card && card.focus) {
      let _config = null
      if (modaltype === 'search') {
        let _search = config.search.filter(item => item.uuid !== card.uuid)
@@ -915,7 +1101,7 @@
        notification.warning({
          top: 92,
          message: '请填写内部函数!',
          duration: 10
          duration: 5
        })
        return
      }
@@ -961,7 +1147,7 @@
              notification.warning({
                top: 92,
                message: '弹窗(表单)按钮,请先配置表单信息!',
                duration: 10
                duration: 5
              })
              resolve(false)
            }
@@ -979,7 +1165,7 @@
            notification.warning({
              top: 92,
              message: '请完善导入Excel验证信息!',
              duration: 10
              duration: 5
            })
            resolve(false)
          }
@@ -1058,23 +1244,9 @@
        notification.warning({
          top: 92,
          message: '接口类型为-内部,且存在内部函数时,才可以创建存储过程!',
          duration: 10
          duration: 5
        })
        return
      }
      if (/[^\s]+\s+[^\s]+/ig.test(setting.dataresource) && 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)
      }
      let _config = {...config, setting: setting}
@@ -1092,7 +1264,28 @@
  }
  /**
   * @description 创建表格接口
   * @description 创建按钮接口(写入)
   */
  btnCreatInterface = () => {
    const { menu } = this.props
    const { config } = this.state
    this.menuformRef.handleConfirm().then(res => {
      this.actionFormRef.handleConfirm().then(result => {
        let _menu = {
          type: 'main',
          MenuID: menu.MenuID,
          menuName: res.menuName,
          menuNo: res.menuNo
        }
        this.refs.btnCreatInterface.triggerInInterface(result, config, _menu)
      })
    })
  }
  /**
   * @description 创建表格接口(读出)
   */
  tableCreatInterface = () => {
    const { menu } = this.props
@@ -1100,25 +1293,11 @@
    this.menuformRef.handleConfirm().then(res => {
      this.settingRef.handleConfirm().then(setting => {
        if (/[^\s]+\s+[^\s]+/ig.test(setting.dataresource) && 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)
        }
        if (setting.interType !== 'inner' || setting.innerFunc) {
          notification.warning({
            top: 92,
            message: '接口类型为-内部,且不存在内部函数时,才可以创建接口!',
            duration: 10
            duration: 5
          })
          return
        }
@@ -1131,13 +1310,7 @@
          menuNo: res.menuNo
        }
  
        this.refs.tableCreatInterface.exec(_menu, _config).then(result => {
          if (result === 'success') {
            this.setState({
              config: _config
            })
          }
        })
        this.refs.tableCreatInterface.triggerOutInterface(_menu, _config)
      })
    })
  }
@@ -1148,7 +1321,7 @@
    confirm({
      content: `确定删除<<${element.card.label}>>吗?`,
      okText: this.state.dict['header.confirm'],
      okText: this.state.dict['model.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let _config = null
@@ -1206,73 +1379,31 @@
  }
  /**
   * @description 按钮双击触发子配置
   */
  btnDoubleClick = (element) => {
    if (!element.origin && (element.OpenType === 'pop' || element.OpenType === 'popview' || element.OpenType === 'blank' || element.OpenType === 'tab')) {
      this.setSubConfig(element, 'button')
    } else {
      notification.warning({
        top: 92,
        message: '此按钮无子配置项!',
        duration: 5
      })
    }
  }
  /**
   * @description 验证信息保存
   */
  verifySubmit = () => {
    const { card } = this.state
    let config = JSON.parse(JSON.stringify(this.state.config))
    if (card.OpenType === 'excelIn') {
      this.verifyRef.handleConfirm().then(res => {
        config.action = config.action.map(item => {
          if (item.uuid === card.uuid) {
            item.verify = res
          }
          return item
        })
        this.setState({
          profileVisible: false,
          config: config,
          card: '',
        })
      })
    } else if (card.execMode) {
      this.verifyRef.handleConfirm().then(res => {
        config.action = config.action.map(item => {
          if (item.uuid === card.uuid) {
            item.verify = res
          }
          return item
        })
        this.setState({
          profileVisible: false,
          config: config,
          card: '',
        })
      })
    } else {
      let _verify = this.verifyRef.state.verify
      if (card.OpenType !== 'excelOut' && _verify.default === 'false' && _verify.scripts.length === 0) {
        notification.warning({
          top: 92,
          message: '不执行默认sql时,必须设置自定义脚本!',
          duration: 10
        })
        return
      } else if (card.OpenType === 'excelOut') {
        let _cols = _verify.columns.map(col => col.Column)
        let _vcols = Array.from(new Set(_cols))
        if (_cols.length > _vcols.length) {
          notification.warning({
            top: 92,
            message: 'Excel列字段名,不可重复!',
            duration: 10
          })
          return
        }
      }
    this.verifyRef.handleConfirm().then(res => {
      config.action = config.action.map(item => {
        if (item.uuid === card.uuid) {
          item.verify = _verify
          item.verify = res
        }
  
        return item
@@ -1281,9 +1412,9 @@
      this.setState({
        profileVisible: false,
        config: config,
        card: '',
        card: ''
      })
    }
    })
  }
  /**
@@ -1342,6 +1473,36 @@
        }
      }
      let btnNames = config.action.map(item => item.label)
      btnNames = Array.from(new Set(btnNames))
      if (btnNames.length < config.action.length) {
        notification.warning({
          top: 92,
          message: '按钮名称不可相同!',
          duration: 5
        })
        return
      }
      let tabNames = []
      let tablength = 0
      config.tabgroups.forEach(group => {
        config[group].forEach(tab => {
          tabNames.push(tab.label)
        })
        tablength += config[group].length
      })
      tabNames = Array.from(new Set(tabNames))
      if (tabNames.length < tablength) {
        notification.warning({
          top: 92,
          message: '标签名称不可相同!',
          duration: 5
        })
        return
      }
      if (config.type === 'user') { // 使用已有菜单时,默认添加关联标签id
        config.action = config.action.map(item => {
          if (item.OpenType === 'popview' && !item.linkTab) {
@@ -1362,7 +1523,7 @@
      }
      let _LongParam = ''
      let _config = {...config, tables: this.state.selectedTables}
      let _config = {...config, tables: this.state.selectedTables, easyCode: res.easyCode}
      let _pageParam = {...menu.PageParam, OpenType: res.opentype}
      // 未设置数据源或标签不合法时,启用状态为false
@@ -1506,7 +1667,7 @@
          notification.warning({
            top: 92,
            message: '编译错误',
            duration: 10
            duration: 5
          })
          this.setState({
            menucloseloading: false,
@@ -1581,6 +1742,7 @@
          ParentID: res.parentId,
          MenuID: menu.MenuID,
          MenuNo: res.menuNo,
          EasyCode: res.easyCode,
          Template: menu.PageParam.Template || '',
          MenuName: res.menuName,
          PageParam: JSON.stringify(_pageParam),
@@ -1647,7 +1809,7 @@
                notification.warning({
                  top: 92,
                  message: error.message,
                  duration: 10
                  duration: 5
                })
                resolve(false)
              } else {
@@ -1691,7 +1853,7 @@
            notification.warning({
              top: 92,
              message: msg,
              duration: 10
              duration: 5
            })
            return false
          } else {
@@ -1745,7 +1907,7 @@
              notification.warning({
                top: 92,
                message: response.message,
                duration: 10
                duration: 5
              })
            }
          })
@@ -1755,7 +1917,7 @@
      notification.warning({
        top: 92,
        message: this.state.dict['header.menu.basemsg'],
        duration: 10
        duration: 5
      })
    })
  }
@@ -1807,7 +1969,7 @@
            notification.warning({
              top: 92,
              message: error.message,
              duration: 10
              duration: 5
            })
            resolve(false)
          } else {
@@ -1939,7 +2101,7 @@
    if (config.isAdd) {
      confirm({
        content: '菜单尚未提交,确定放弃保存吗?',
        okText: this.state.dict['header.confirm'],
        okText: this.state.dict['model.confirm'],
        cancelText: this.state.dict['header.cancel'],
        onOk() {
          _this.props.handleView()
@@ -1948,7 +2110,7 @@
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _config = {...config, tables: this.state.selectedTables, easyCode: res.easyCode}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
@@ -1985,7 +2147,7 @@
      notification.warning({
        top: 92,
        message: '请选择表名!',
        duration: 10
        duration: 5
      })
      return
    }
@@ -2038,25 +2200,6 @@
      })
    }
    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
@@ -2064,7 +2207,7 @@
      notification.warning({
        top: 92,
        message: '请选择添加字段',
        duration: 10
        duration: 5
      })
      return
    }
@@ -2084,12 +2227,12 @@
          if (cell.type !== item.type) { // 数据类型修改
            if (cell.type === 'select') {
              item.match = selectmatch[cell.datatype]
              item.match = '='
            } else if (cell.type === 'daterange') {
              item.match = datematch[cell.datatype]
              item.match = 'between'
            } else {
              cell.type = 'text'
              item.match = textmatch[cell.datatype]
              item.match = 'like'
            }
            
            item.type = cell.type
@@ -2106,11 +2249,11 @@
      _columns.forEach(item => {
        let _match = ''
        if (item.type === 'select') {
          _match = selectmatch[item.datatype]
          _match = '='
        } else if (item.type === 'daterange') {
          _match = datematch[item.datatype]
          _match = 'between'
        } else {
          _match = textmatch[item.datatype]
          _match = 'like'
        }
        let newcard = {
@@ -2231,7 +2374,7 @@
          notification.warning({
            top: 92,
            message: res.message,
            duration: 10
            duration: 5
          })
        }
      })
@@ -2286,10 +2429,71 @@
        Api.getLocalConfig(param)
      }
      this.setState({
        config: {...config, setting: res},
        settingVisible: false,
      })
      if (res.interType === 'inner' && !res.innerFunc && /\s/.test(res.dataresource)) {
        this.setState({
          sqlVerifing: true
        })
        let _dataresource = res.dataresource
        if (res.queryType === 'statistics') {
          let fieldmap = new Map()
          let options = config.search.map(item => {
            let _field = item.key
            let _val = ''
            if (fieldmap.has(_field)) {
              _field = _field + '1'
            }
            fieldmap.set(item.key, true)
            if (/date/.test(item.type)) {
              _val = '1900-01-01'
            }
            return {
              reg: new RegExp('@' + _field + '@', 'ig'),
              value: _val
            }
          })
          options.forEach(item => {
            _dataresource = _dataresource.replace(item.reg, `'${item.value}'`)
          })
        }
        let param = {
          func: 's_debug_sql',
          LText: _dataresource
        }
        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).then(result => {
          if (result.status) {
            this.setState({
              sqlVerifing: false,
              config: {...config, setting: res},
              settingVisible: false
            })
          } else {
            this.setState({sqlVerifing: false})
            Modal.error({
              title: result.message
            })
          }
        })
      } else {
        this.setState({
          config: {...config, setting: res},
          settingVisible: false
        })
      }
    })
  }
@@ -2304,11 +2508,11 @@
      notification.warning({
        top: 92,
        message: this.state.dict['header.menu.config.notsave'],
        duration: 10
        duration: 5
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables}
        let _config = {...config, tables: this.state.selectedTables, easyCode: res.easyCode}
        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
        let _originMenu = {
          ...originMenu,
@@ -2324,7 +2528,7 @@
          notification.warning({
            top: 92,
            message: this.state.dict['header.menu.config.update'],
            duration: 10
            duration: 5
          })
          return
        }
@@ -2405,7 +2609,7 @@
            notification.warning({
              top: 92,
              message: res.message,
              duration: 10
              duration: 5
            })
          }
        })
@@ -2413,7 +2617,7 @@
        notification.warning({
          top: 92,
          message: this.state.dict['header.menu.config.update'],
          duration: 10
          duration: 5
        })
      })
    }
@@ -2438,19 +2642,19 @@
      notification.warning({
        top: 92,
        message: '菜单尚未设置数据源,不可启用!',
        duration: 10
        duration: 5
      })
    } else if (!config.setting.primaryKey) {
      notification.warning({
        top: 92,
        message: '菜单尚未设置主键,不可启用!',
        duration: 10
        duration: 5
      })
    } else if (!tabinvalid) {
      notification.warning({
        top: 92,
        message: '菜单标签页设置错误(多行标签内,行标签不可为空),不可启用!',
        duration: 10
        duration: 5
      })
    } else {
      this.setState({
@@ -2502,7 +2706,7 @@
    confirm({
      content: `确定新建标签组吗?`,
      okText: this.state.dict['header.confirm'],
      okText: this.state.dict['model.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
        let newgroup = 'tabs' + Utils.getuuid()
@@ -2527,7 +2731,7 @@
    confirm({
      content: `确定删除标签组吗?`,
      okText: this.state.dict['header.confirm'],
      okText: this.state.dict['model.confirm'],
      cancelText: this.state.dict['header.cancel'],
      onOk() {
@@ -2540,86 +2744,6 @@
      },
      onCancel() {}
    })
  }
  /**
   * @description 解冻按钮
   */
  handleThaw = () => {
    const { menu } = this.props
    this.setState({
      thawBtnVisible: true
    })
    Api.getSystemConfig({
      func: 'sPC_Get_FrozenMenu',
      ParentID: menu.MenuID,
      TYPE: 40
    }).then(res => {
      if (res.status) {
        let _list = []
        res.data.forEach(menu => {
          let _conf = ''
          if (menu.ParentParam) {
            try {
              _conf = JSON.parse(window.decodeURIComponent(window.atob(menu.ParentParam)))
            } catch (e) {
              console.warn('Parse Failure')
              _conf = ''
            }
          }
          if (_conf) {
            _list.push({
              key: menu.MenuID,
              title: menu.MenuName,
              btnParam: _conf
            })
          }
        })
        this.setState({
          thawbtnlist: _list
        })
      } else {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 10
        })
      }
    })
  }
  /**
   * @description 解冻按钮提交
   */
  thawBtnSubmit = () => {
    const { thawButtons, config, thawbtnlist } = this.state
    // 三级菜单解除冻结
    if (this.refs.trawmenu.state.targetKeys.length === 0) {
      notification.warning({
        top: 92,
        message: this.state.dict['form.required.select'] + this.state.dict['header.form.thawbutton'],
        duration: 10
      })
    } else {
      thawbtnlist.forEach(item => {
        if (this.refs.trawmenu.state.targetKeys.includes(item.key)) {
          config.action.push(item.btnParam)
        }
      })
      this.setState({
        thawButtons: [...thawButtons, ...this.refs.trawmenu.state.targetKeys],
        config: config,
        thawBtnVisible: false
      })
    }
  }
  handleGroup = (index, type) => {
@@ -2642,6 +2766,27 @@
    })
  }
  copycolumn = () => {
    const { config } = this.state
    let oInput = document.createElement('input')
    let val = {
      copyType: 'columns',
      columns: config.columns
    }
    oInput.value = window.btoa(window.encodeURIComponent(JSON.stringify(val)))
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
    oInput.className = 'oInput'
    oInput.style.display = 'none'
    message.success('复制成功。')
    document.body.removeChild(oInput)
  }
  /**
   * @description 选择不保存时,如有复制按钮,则删除
   */
@@ -2656,38 +2801,43 @@
    this.props.handleView()
  }
  pasteSubmit = () => {
    this.pasteFormRef.handleConfirm().then(res => {
      if (res.copyType !== 'action') {
        notification.warning({
          top: 92,
          message: '配置信息格式错误!',
          duration: 10
        })
        return
      }
  updateConfig = (res) => {
    if (res.type === 'thaw') {
      this.setState({
        modaltype: ''
      }, () => {
        this.handleAction(res, 'copy')
        thawButtons: res.thawButtons,
        config: res.config
      })
    })
    } else if (res.type === 'paste') {
      if (res.copyType === 'action') {
        this.handleAction(res.content, 'copy')
      } else if (res.copyType === 'columns') {
        this.setState({
          config: res.config
        })
      }
    }
  }
  render () {
    const { modaltype, activeKey } = this.state
    const configAction = this.state.config.action.filter(_action =>
    const { modaltype, activeKey, config } = this.state
    const configAction = 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])
    config.tabgroups.forEach(group => {
      configTabs.push(...config[group])
    })
    let hasbtncrtinter = false
    if (modaltype === 'actionEdit' && config.setting.interType === 'inner' && !config.setting.innerFunc && config.setting.dataresource) {
      hasbtncrtinter = true
    }
    return (
      <div className="common-table-board">
        {/* <div className="ant-modal-mask"></div> */}
        <DndProvider backend={HTML5Backend}>
          {/* 工具栏 */}
          <div className="tools">
@@ -2763,9 +2913,6 @@
                      {this.state.dict['header.menu.action.configurable']}
                    </p> : null
                  }
                  <div className="thawbutton" title={this.state.dict['header.form.thawbutton']} onClick={this.handleThaw}>
                    <Icon type="unlock" />
                  </div>
                </div>
                {configAction.map((item, index) => {
                  return (
@@ -2827,6 +2974,7 @@
              </div>
            } bordered={false} extra={
              <div>
                <EditComponent dict={this.state.dict} type="maintable" config={this.state.config} MenuID={this.props.menu.MenuID} thawButtons={this.state.thawButtons} refresh={this.updateConfig}/>
                <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>
@@ -2840,7 +2988,7 @@
                </Tooltip>
                <DragElement
                  type="search"
                  list={this.state.config.search}
                  list={config.search}
                  handleList={this.handleList}
                  handleMenu={this.handleSearch}
                  deleteMenu={this.deleteElement}
@@ -2851,18 +2999,16 @@
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《按钮》中,选择对应类型的按钮拖至此处添加,如选择按钮类型为表单、新标签页等含有配置页面的按钮,可在左侧工具栏-按钮-可配置按钮处,点击按钮完成相关配置。注:当设置按钮显示位置为表格时,显示列会增加操作列。">
                  <Icon type="question-circle" />
                </Tooltip>
                <div className="copybutton" title={this.state.dict['header.form.paste']} onClick={() => {this.setState({modaltype: 'paste'})}}>
                  <Icon type="snippets" />
                </div>
                <DragElement
                  type="action"
                  list={this.state.config.action}
                  setting={this.state.config.setting}
                  list={config.action}
                  setting={config.setting}
                  handleList={this.handleList}
                  handleMenu={this.handleAction}
                  copyElement={(val) => this.handleAction(val, 'copy')}
                  deleteMenu={this.deleteElement}
                  profileMenu={this.profileAction}
                  doubleClickCard={this.btnDoubleClick}
                  placeholder={this.state.dict['header.form.action.placeholder']}
                />
              </div>
@@ -2871,12 +3017,13 @@
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《显示列》中,选择对应类型的显示列拖至此处添加;或点击《添加显示列》按钮批量添加,选择批量添加时,需提前选择使用表。注:添加合并列时,需设置可选列。">
                  <Icon type="question-circle" />
                </Tooltip>
                {config.columns && config.columns.length > 0 ? <Icon className="column-copy" title="copy" type="copy" onClick={this.copycolumn} /> : null}
                <Switch checkedChildren="开" unCheckedChildren="关" defaultChecked={this.state.showColumnName} onChange={this.onColumnNameChange} />
                <DragElement
                  type="columns"
                  list={this.state.config.columns}
                  setting={this.state.config.setting}
                  gridBtn={this.state.config.gridBtn}
                  list={config.columns}
                  setting={config.setting}
                  gridBtn={config.gridBtn}
                  handleList={this.handleList}
                  handleMenu={this.handleColumn}
                  deleteMenu={this.deleteElement}
@@ -2886,13 +3033,13 @@
                />
              </div>
              {/* 标签组 */}
              {this.state.config.tabgroups.map((groupId, index) => {
              {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 !== (this.state.config.tabgroups.length - 1) ?
                    {index !== (config.tabgroups.length - 1) ?
                      <Icon type="arrow-down" onClick={() => {this.handleGroup(index, 'down')}} /> : null
                    }
                    {index !== 0 ? <Icon type="arrow-up" onClick={() => {this.handleGroup(index, 'up')}} /> : null}
@@ -2901,10 +3048,11 @@
                    <TabDragElement
                      type="tabs"
                      groupId={groupId}
                      list={this.state.config[groupId]}
                      list={config[groupId]}
                      handleList={this.handleList}
                      handleMenu={this.handleTab}
                      deleteMenu={this.deleteElement}
                      doubleClickCard={(tab) => this.setSubConfig(tab, 'tab')}
                      placeholder={this.state.dict['header.form.tab.placeholder']}
                    />
                  </div>)
@@ -2916,9 +3064,10 @@
        <Modal
          title={this.state.dict['header.modal.search.edit']}
          visible={modaltype === 'search'}
          width={700}
          width={750}
          maskClosable={false}
          onOk={this.handleSubmit}
          confirmLoading={this.state.sqlVerifing}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
@@ -2939,9 +3088,10 @@
          maskClosable={false}
          onCancel={this.editModalCancel}
          footer={[
            hasbtncrtinter ? <CreateInterface key="interface" dict={this.state.dict} ref="btnCreatInterface" trigger={this.btnCreatInterface}/> : null,
            modaltype === 'actionEdit' ? <CreateFunc key="create" dict={this.state.dict} ref="btnCreatFunc" trigger={this.creatFunc}/> : null,
            <Button key="cancel" onClick={this.editModalCancel}>{this.state.dict['header.cancel']}</Button>,
            <Button key="confirm" type="primary" onClick={this.handleSubmit}>{this.state.dict['header.confirm']}</Button>
            <Button key="confirm" type="primary" onClick={this.handleSubmit}>{this.state.dict['model.confirm']}</Button>
          ]}
          destroyOnClose
        >
@@ -2951,7 +3101,7 @@
            tabs={this.state.tabviews}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
            setting={this.state.config.setting}
            setting={config.setting}
            wrappedComponentRef={(inst) => this.actionFormRef = inst}
          />
        </Modal>
@@ -2959,7 +3109,7 @@
        <Modal
          title={this.state.dict['header.modal.column.edit']}
          visible={modaltype === 'columns'}
          width={700}
          width={750}
          maskClosable={false}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
@@ -2968,6 +3118,7 @@
          <ColumnForm
            dict={this.state.dict}
            card={this.state.card}
            MenuID={this.props.menu.MenuID}
            inputSubmit={this.handleSubmit}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.columnFormRef = inst}
@@ -2987,7 +3138,7 @@
            dict={this.state.dict}
            card={this.state.card}
            inputSubmit={this.handleSubmit}
            columns={this.state.config.columns}
            columns={config.columns}
            wrappedComponentRef={(inst) => this.columnFormRef = inst}
          />
        </Modal>
@@ -2995,7 +3146,7 @@
        <Modal
          title={this.state.dict['header.modal.gridbtn.edit']}
          visible={modaltype === 'gridbtn'}
          width={700}
          width={750}
          maskClosable={false}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
@@ -3004,7 +3155,7 @@
          <GridBtnForm
            dict={this.state.dict}
            inputSubmit={this.handleSubmit}
            card={this.state.config.gridBtn}
            card={config.gridBtn}
            wrappedComponentRef={(inst) => this.gridBtnFormRef = inst}
          />
        </Modal>
@@ -3012,7 +3163,7 @@
        <Modal
          title={this.state.dict['header.modal.tabs.edit']}
          visible={modaltype === 'tabs'}
          width={700}
          width={750}
          maskClosable={false}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
@@ -3031,7 +3182,7 @@
        {/* 根据字段名添加显示列及搜索条件 */}
        <Modal
          wrapClassName="common-table-fields-modal"
          title={this.state.dict['header.edit']}
          title={this.state.dict['model.edit']}
          visible={this.state.tableVisible}
          width={'65vw'}
          maskClosable={false}
@@ -3061,6 +3212,7 @@
          visible={this.state.profileVisible}
          width={'75vw'}
          style={{minWidth: '900px', maxWidth: '1200px'}}
          okText={this.state.dict['header.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ profileVisible: false }) }}
          destroyOnClose
@@ -3069,7 +3221,7 @@
            <VerifyCard
              card={this.state.card}
              dict={this.state.dict}
              columns={this.state.config.columns}
              columns={config.columns}
              wrappedComponentRef={(inst) => this.verifyRef = inst}
            /> : null
          }
@@ -3077,7 +3229,7 @@
            <VerifyCardPrint
              card={this.state.card}
              dict={this.state.dict}
              columns={this.state.config.columns}
              columns={config.columns}
              wrappedComponentRef={(inst) => this.verifyRef = inst}
            /> : null
          }
@@ -3085,7 +3237,7 @@
            <VerifyCardExcelIn
              card={this.state.card}
              dict={this.state.dict}
              columns={this.state.config.columns}
              columns={config.columns}
              wrappedComponentRef={(inst) => this.verifyRef = inst}
            /> : null
          }
@@ -3099,9 +3251,9 @@
        </Modal>
        {/* 设置全局配置及列表数据源 */}
        <Modal
          title={this.state.dict['header.edit']}
          title={this.state.dict['model.edit']}
          visible={this.state.settingVisible}
          width={700}
          width={750}
          maskClosable={false}
          onCancel={() => { // 取消修改
            this.setState({
@@ -3112,7 +3264,7 @@
            <CreateInterface key="interface" dict={this.state.dict} ref="tableCreatInterface" trigger={this.tableCreatInterface}/>,
            <CreateFunc key="create" dict={this.state.dict} ref="tableCreatFunc" trigger={this.tableCreatFunc}/>,
            <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>
            <Button key="confirm" type="primary" loading={this.state.sqlVerifing} onClick={this.settingSave}>{this.state.dict['model.confirm']}</Button>
          ]}
          destroyOnClose
        >
@@ -3120,8 +3272,8 @@
            dict={this.state.dict}
            menu={this.props.menu}
            inputSubmit={this.settingSave}
            data={this.state.config.setting}
            columns={this.state.config.columns}
            data={config.setting}
            columns={config.columns}
            usefulFields={this.props.permFuncField}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
@@ -3141,34 +3293,6 @@
        >
          {this.state.dict['header.menu.config.placeholder']}
        </Modal>
        {/* 解冻按钮模态框 */}
        <Modal
          title={this.state.dict['header.form.thawbutton']}
          okText={this.state.dict['header.confirm']}
          cancelText={this.state.dict['header.cancel']}
          visible={this.state.thawBtnVisible}
          onOk={this.thawBtnSubmit}
          onCancel={() => {this.setState({thawBtnVisible: false, thawbtnlist: null})}}
          destroyOnClose
        >
          {!this.state.thawbtnlist && <Spin style={{marginLeft: 'calc(50% - 22px)', marginTop: '70px', marginBottom: '70px'}} size="large" />}
          {this.state.thawbtnlist && <TransferForm ref="trawmenu" menulist={this.state.thawbtnlist}/>}
        </Modal>
        {/* 按钮配置信息粘贴复制 */}
        <Modal
          title={this.state.dict['header.form.paste']}
          visible={modaltype === 'paste'}
          width={600}
          maskClosable={false}
          onOk={this.pasteSubmit}
          onCancel={() => {this.setState({modaltype: ''})}}
          destroyOnClose
        >
          <PasteForm
            dict={this.state.dict}
            wrappedComponentRef={(inst) => this.pasteFormRef = inst}
          />
        </Modal>
        {this.state.loading && <Spin size="large" />}
      </div>
    )
@@ -3177,6 +3301,7 @@
const mapStateToProps = (state) => {
  return {
    sysRoles: state.sysRoles,
    permFuncField: state.permFuncField
  }
}