king
2021-03-05 e36eb1999794bd71e76482b92a0b0b20f49d0032
src/templates/subtableconfig/index.jsx
@@ -4,33 +4,35 @@
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, Icon, Switch, Tooltip, Col } from 'antd'
import moment from 'moment'
import Api from '@/api'
import zhCN from '@/locales/zh-CN/comtable.js'
import enUS from '@/locales/en-US/comtable.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import Utils from '@/utils/utils.js'
import { getSearchForm, getActionForm, getColumnForm } from '@/templates/tableshare/formconfig'
import { updateSubTable } from '@/utils/utils-update.js'
import ActionForm from './actionform'
import SettingForm from './settingform'
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 asyncComponent from '@/utils/asyncComponent'
import SearchComponent from '@/templates/sharecomponent/searchcomponent'
import ActionComponent from '@/templates/sharecomponent/actioncomponent'
import ColumnComponent from '@/templates/sharecomponent/columncomponent'
import MenuForm from './menuform'
import SourceElement from '@/templates/zshare/dragsource'
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
const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/settingcomponent'))
const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
const ChartGroupComponent = asyncComponent(() => import('@/templates/sharecomponent/chartgroupcomponent'))
const ChartComponent = asyncComponent(() => import('@/templates/sharecomponent/chartcomponent'))
const CardComponent = asyncComponent(() => import('@/templates/sharecomponent/cardcomponent'))
class SubTableConfig extends Component {
  static propTpyes = {
@@ -38,39 +40,29 @@
    editTab: PropTypes.any,
    tabConfig: PropTypes.any,
    editSubTab: PropTypes.any,
    btnTab: PropTypes.any,
    btnTabConfig: PropTypes.any,
    config: PropTypes.any,
    handleView: PropTypes.func
  }
  state = {
    dict: CommonDict,        // 字典
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 字典
    config: null,            // 页面配置
    visible: false,          // 搜索条件、按钮、显示列,模态框显示控制
    modalTitle: '',          // 模态框的标题
    tableVisible: false,     // 数据表字段模态框
    addType: '',             // 添加类型-搜索条件或显示列
    tableColumns: [],        // 表格显示列
    fields: null,            // 搜索条件及显示列,可选字段
    menuformlist: null,      // 基本信息表单字段
    formlist: null,          // 搜索条件、按钮、显示列表单字段
    formtemp: '',            // 表单类型,显示列、按钮、搜索条件
    modaltype: '',           // 模态框类型,控制模态框显示
    card: null,              // 编辑元素
    menuloading: false,      // 菜单保存中
    menucloseloading: false, // 菜单关闭时,选择保存
    loading: false,          // 加载中,页面spin
    settingVisible: false,   // 全局配置模态框
    closeVisible: false,     // 关闭模态框
    tables: [],              // 可用表名
    selectedTables: [],      // 已选表名
    originConfig: null,      // 原配置
    originActions: null,     // 原始按钮信息,使用已有用户模板
    delActions: [],          // 删除按钮列表
    copyActions: [],         // 复制按钮组
    funcLoading: false,      // 存储过程创建中
    showColumnName: false,   // 显示列字段名控制
    tabviews: [],            // 所有标签页
    profileVisible: false    // 验证信息模态框
    thawButtons: [],         // 已选择要解冻的按钮
    activeKey: '0',          // 默认展开基本信息
    chartview: null,         // 当前视图
    openEdition: ''          // 编辑版本标记,防止多人操作
  }
  /**
@@ -84,12 +76,12 @@
    let _config = null
    if (!config) {
      _config = JSON.parse(JSON.stringify(Source.baseConfig))
      _config = fromJS(Source.baseConfig).toJS()
      _config.uuid = editSubTab ? editSubTab.linkTab : editTab.linkTab
      _config.tabName = editSubTab ? editSubTab.label : editTab.label
      _config.isAdd = true
    } else {
      _config = JSON.parse(JSON.stringify(config))
      _config = fromJS(config).toJS()
    }
    
    let _oriActions = []
@@ -97,9 +89,14 @@
    if (_config.type === 'user') {
      _config.action = _config.action.map(item => {
        let uuid = Utils.getuuid()
        if (item.linkTab) {
          item.linkTab = ''
        }
        if (item.OpenType === 'pop') { // 含有子配置项的按钮
          _oriActions.push({
            prebtn: JSON.parse(JSON.stringify(item)),
            prebtn: fromJS(item).toJS(),
            curuuid: uuid,
            Template: 'Modal'
          })
@@ -110,37 +107,18 @@
      })
    }
    let _activeKey =  editSubTab ? editSubTab.activeKey : editTab.activeKey
    // 版本兼容
    _config = updateSubTable(_config)
    this.setState({
      openEdition: editSubTab ? (editSubTab.open_edition || '') : (editTab.open_edition || ''),
      chartview: _config.charts[0].uuid,
      originActions: _oriActions,
      config: _config,
      originConfig: _config,
      selectedTables: _config.tables || [],
      menuformlist: [
        {
          type: 'text',
          key: 'tabName',
          label: this.state.dict['header.menu.tabName'],
          initVal: _config.tabName,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'tabNo',
          label: this.state.dict['header.menu.menuNo'],
          initVal: _config.tabNo,
          required: true,
          readonly: false
        },
        {
          type: 'text',
          key: 'Remark',
          label: this.state.dict['header.menu.Remark'],
          initVal: _config.Remark,
          required: false,
          readonly: false
        }
      ]
      activeKey: _activeKey || '0',
      originConfig: fromJS(_config).toJS(),
    })
  }
@@ -150,110 +128,54 @@
   * 2、根据配置信息中已使用表获取相关字段信息
   */
  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'
    }
    this.reloadTab(false)
  }
    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 => {
  /**
   * @description 加载或刷新标签信息
   */
  reloadTab = (type) => {
    this.setState({
      loading: type,
      tabviews: []
    })
    Api.getSystemConfig({func: 'sPC_Get_UserTemp', TypeCharTwo: 'tab'}).then(res => {
      if (res.status) {
        this.setState({
          tables: res.data
        let _tabviews = []
        res.UserTemp.forEach(temp => {
          let item = {
            uuid: temp.MenuID,
            value: temp.MenuID,
            text: temp.MenuName,
            type: temp.Template,
            MenuNo: temp.MenuNo
          }
          if (this.props.config && temp.MenuID === this.props.config.uuid) return
          _tabviews.push(item)
        })
        this.setState({
          loading: false,
          tabviews: _tabviews
        })
        if (type) {
          notification.success({
            top: 92,
            message: '刷新成功。',
            duration: 2
          })
        }
      } else {
        this.setState({
          loading: false
        })
        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
            }
          })
          duration: 5
        })
      }
    })
@@ -270,15 +192,20 @@
  // 页面返回
  handleViewBack = () => {
    const {menu, editTab, tabConfig, editSubTab} = this.props
    const {menu, editTab, tabConfig, editSubTab, btnTab, btnTabConfig} = this.props
    let _tabview = menu ? menu.LongParam.Template : ''
    let _subconfig = null
    if (editSubTab) {
      _subconfig = tabConfig
      if (editTab.hasOwnProperty('OpenType')) {
        _tabview = editTab.tabType
        _tabview = editTab.tabType || 'SubTable'
      } else {
        _tabview = editTab.type
      }
    } else if (!editSubTab && btnTab) {
      _tabview = btnTab.tabTemplate
      _subconfig = btnTabConfig
    }
    let param = {
@@ -287,983 +214,279 @@
      tabConfig: null,
      editSubTab: null,
      subTabConfig: null,
      btnTab: btnTab,
      btnTabConfig: btnTabConfig,
      editAction: null,
      subConfig: tabConfig,
      subConfig: _subconfig,
      tabview: _tabview
    }
    this.state.copyActions.forEach(item => {
      let _param = {
        func: 'sPC_MainMenu_Del',
        MenuID: item
      }
      Api.getSystemConfig(_param)
    })
    this.props.handleView(param)
  }
  /**
   * @description 元素添加或拖动时顺序变化
   */
  handleList = (type, list, card) => {
    const { config } = this.state
    if (list.length > config[type].length) {
      list = list.filter(item => !item.origin)
      if (type === 'search') {
        this.handleSearch(card)
      } else if (type === 'action') {
        this.handleAction(card)
      } else if (type === 'columns') {
        this.handleColumn(card)
      }
    }
    this.setState({config: {...config, [type]: list}})
  }
  /**
   * @description 搜索条件编辑,获取搜索条件表单信息
   */
  handleSearch = (card) => {
    this.setState({
      modaltype: 'search',
      card: card,
      formlist: getSearchForm(card)
    })
  }
  /**
   * @description 按钮编辑,获取按钮表单信息
   */
  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>
    </div>
    this.setState({
      modaltype: type === 'copy' ? 'actionCopy' : 'actionEdit',
      card: card,
      formlist: getActionForm(card, functip, this.state.config, this.props.permFuncField)
    })
  }
  /**
   * @description 显示列与合并列编辑,获取表单信息
   */
  handleColumn = (card) => {
    if (card.type !== 'colspan') {
      this.setState({
        modaltype: 'columns',
        card: card,
        formlist: getColumnForm(card)
      })
    } else {
      this.setState({
        modaltype: 'colspan',
        card: card
      })
    }
  }
  /**
   * @description 操作列编辑
   */
  handleGridBtn = () => {
    this.setState({
      modaltype: 'gridbtn'
    })
  }
  /**
   * @description 搜索、按钮、显示列修改后提交保存
   * 1、搜索条件保存,当类型为下拉框且存在数据源时,将查询条件拼接为sql,并用base64转码
   * 2、按钮包括正常编辑和复制,复制时,按钮列末尾添加
   * 3、添加或编辑列,保存时,如按钮位置设置为表格,则修改操作列显示状态
   */
  handleSubmit = () => {
    const { card, config, modaltype } = this.state
    if (modaltype === 'search') {
      this.searchFormRef.handleConfirm().then(res => {
        let _search = config.search.map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        _search = _search.filter(item => !item.origin)
        this.setState({
          config: {...config, search: _search},
          modaltype: ''
        })
      })
    } else if (modaltype === 'actionEdit' || modaltype === 'actionCopy') {
      this.actionFormRef.handleConfirm().then(res => {
        let _action = config.action.map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        _action = _action.filter(item => !item.origin)
        if (modaltype === 'actionCopy') {
          _action.push(res)
        }
        // 复制按钮前后皆为表单时,复制表单配置信息,id存于复制列表
        if (res.OpenType === 'pop' && card.originCard && card.originCard.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: config.uuid,
                MenuID: res.uuid,
                MenuNo: config.tabNo,
                Template: 'Modal',
                MenuName: res.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
                  })
                } else {
                  this.setState({
                    copyActions: [...this.state.copyActions, res.uuid]
                  })
                }
              })
            }
          })
        }
        // 判断是否存在操作列
        let _hasGridbtn = _action.filter(act => act.position === 'grid').length > 0
        let _gridBtn = config.gridBtn
        if (_gridBtn) {
          _gridBtn.display = _hasGridbtn
        } else {
          _gridBtn = {
            display: _hasGridbtn,
            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, action: _action, gridBtn: _gridBtn},
          modaltype: ''
        })
      })
    } else if (modaltype === 'columns' || modaltype === 'colspan') {
      this.columnFormRef.handleConfirm().then(res => {
        let _columns = config.columns.map(item => {
          if (item.uuid === res.uuid) {
            return res
          } else {
            return item
          }
        })
        _columns = _columns.filter(item => !item.origin)
        this.setState({
          config: {...config, columns: _columns},
          modaltype: ''
        })
      })
    } else if (modaltype === 'gridbtn') {
      this.gridBtnFormRef.handleConfirm().then(res => {
        this.setState({
          config: {...config, gridBtn: res},
          modaltype: ''
        })
      })
    }
  }
  /**
   * @description 取消保存,如果元素为新添元素,则从序列中删除
   */
  editModalCancel = () => {
    const { config, card, modaltype } = this.state
    if (card.focus) {
      let _config = null
      if (modaltype === 'search') {
        let _search = config.search.filter(item => item.uuid !== card.uuid)
        _config = {...config, search: _search}
      } else if (modaltype === 'actionEdit') {
        let _action = config.action.filter(item => item.uuid !== card.uuid)
        _config = {...config, action: _action}
      } else if (modaltype === 'columns' || modaltype === 'colspan') {
        let _columns = config.columns.filter(item => item.uuid !== card.uuid)
        _config = {...config, columns: _columns}
      } else {
        _config = config
      }
      this.setState({
        card: null,
        config: _config,
        modaltype: ''
      })
    } else {
      this.setState({
        card: null,
        modaltype: ''
      })
    }
  }
  /**
   * @description 创建按钮存储过程
   */
  creatFunc = () => {
    let _config = JSON.parse(JSON.stringify(this.state.config))
    this.actionFormRef.handleConfirm().then(res => {
      let btn = res         // 按钮信息
      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: _config.tabNo
              }
              newLText = Utils.formatOptions(Utils.getfunc(_param, btn, {MenuID: _config.uuid, MenuName: _config.tabName}, _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: _config.tabNo
          }
          newLText = Utils.formatOptions(Utils.getfunc(_param, btn, {MenuID: _config.uuid, MenuName: _config.tabName}, _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
        }
        _config.action = _config.action.map(item => {
          if (item.uuid === btn.uuid) {
            return btn
          } else {
            return item
          }
        })
        _config.action = _config.action.filter(item => !item.origin)
        // 判断是否存在操作列
        let _hasGridbtn = _config.action.filter(act => act.position === 'grid').length > 0
        if (_config.gridBtn) {
          _config.gridBtn.display = _hasGridbtn
        } else {
          _config.gridBtn = {
            display: _hasGridbtn,
            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,
          funcLoading: false
        })
      })
    })
  }
  /**
   * @description 创建表格存储过程
   */
  tableCreatFunc = () => {
    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: config.uuid
        }
        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, {MenuID: config.uuid, MenuName: config.tabName, MenuNo: config.tabNo}, 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))
        _config[element.type] = _config[element.type].filter(item => {
          if (item.uuid === element.card.uuid) {
            return false
          } else {
            return true
          }
        })
        // 删除按钮元素
        let _delActions = _this.state.delActions
        if (element.type === 'action') {
          _delActions.push(element.card.uuid)
        }
        _this.setState({
          config: _config,
          delActions: _delActions
        })
      },
      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: ''
    })
  }
  /**
   * @description 标签页保存
   */
  submitConfig = () => {
    const { delActions, originConfig } = this.state
    let config = JSON.parse(JSON.stringify(this.state.config))
    const { delActions, thawButtons, openEdition } = this.state
    let _config = fromJS(this.state.config).toJS()
    let copyreg = /\d{4}-\d{2}-\d{2}\s\d{2}:\d{2}:\d{2}$/ig
    this.menuformRef.handleConfirm().then(res => {
    // 基本信息验证
    if (!_config.tabName || !_config.tabNo) {
      notification.warning({
        top: 92,
        message: this.state.dict['model.menu.basemsg'],
        duration: 5
      })
      this.setState({activeKey: '0'})
      return
    }
      if (originConfig.isAdd) {
        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 (copyreg.test(_config.tabNo) || copyreg.test(_config.tabName)) {
      notification.warning({
        top: 92,
        message: '此标签为复制标签,请修改标签名称和标签参数,不可以时间格式 YYYY-MM-DD HH:mm:ss 结尾!',
        duration: 5
      })
      return
    }
    if (_config.isAdd) {
      if (_config.search[0] && _config.search[0].origin) {
        _config.search = _config.search.filter(item => !item.origin)
      }
      let _LongParam = ''
      let _config = {...config, tables: this.state.selectedTables, ...res}
      // 保存时删除配置类型,system 、user
      delete _config.type
      delete _config.isAdd
      try {
        _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
      } catch (e) {
        notification.warning({
          top: 92,
          message: '编译错误',
          duration: 10
        })
        return
      if (_config.action[0] && _config.action[0].origin) {
        _config.action = _config.action.filter(item => !item.origin)
      }
      let btnParam = {
        func: 'sPC_Button_AddUpt',
        Type: 40,
        ParentID: _config.uuid,
        MenuNo: res.tabNo,
        Template: 'SubTable',
        PageParam: '',
        LongParam: '',
        LText: config.action.map((item, index) => {
          return `select '${item.uuid}' as menuid, '${item.label}' as menuname, '${(index + 1) * 10}' as Sort`
        })
      if (_config.columns[0] && _config.columns[0].origin) {
        _config.columns = _config.columns.filter(item => !item.origin)
      }
    }
      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 param = {
        func: 'sPC_Tab_AddUpt',
        MenuID: _config.uuid,
        MenuNo: res.tabNo,
        Template: 'SubTable',
        MenuName: res.tabName,
        Remark: res.Remark,
        Sort: 0,
        PageParam: JSON.stringify({Template: 'SubTable'}),
        LongParam: _LongParam
      }
      if (this.state.closeVisible) { // 显示关闭对话框时,模态框中保存按钮,显示保存中状态
        this.setState({
          menucloseloading: true
        })
      } else {
        this.setState({
          menuloading: true
        })
      }
      // 有按钮或标签删除时,先进行删除操作
      // 删除成功后,保存页面配置
      new Promise(resolve => {
        if (delActions.length > 0) {
          let deffers = delActions.map(item => {
            let _param = {
              func: 'sPC_MainMenu_Del',
              MenuID: item
            }
            return new Promise(resolve => {
              Api.getSystemConfig(_param).then(response => {
                resolve(response)
              })
            })
          })
          Promise.all(deffers).then(result => {
            let error = null
            result.forEach(response => {
              if (!response.status) {
                error = response
              }
            })
    // 未设置数据源或主键时,启用状态为false
    let result = this.verifyconfig(_config)
  
            if (error) {
              this.setState({
                menuloading: false,
                menucloseloading: false
              })
              notification.warning({
                top: 92,
                message: error.message,
                duration: 10
              })
              resolve(false)
            } else {
              this.setState({
                delActions: []
              })
              resolve(true)
    if (result !== true) {
      _config.enabled = false
    }
    if (this.state.closeVisible) { // 显示关闭对话框时,模态框中保存按钮,显示保存中状态
      this.setState({
        menucloseloading: true
      })
    } else {
      this.setState({
        menuloading: true
      })
    }
    // 保存时删除配置类型,system 、user
    delete _config.type
    delete _config.isAdd
    let _LongParam = ''
    try {
      _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
    } catch (e) {
      notification.warning({
        top: 92,
        message: '编译错误',
        duration: 5
      })
      this.setState({
        menucloseloading: false,
        menuloading: false
      })
      return
    }
    let btnParam = {
      func: 'sPC_Button_AddUpt',
      Type: 40,
      ParentID: _config.uuid,
      MenuNo: _config.tabNo,
      Template: 'SubTable',
      PageParam: '',
      LongParam: '',
      LText: []
    }
    let btntabs = []
    _config.action.forEach((item, index) => {
      if (item.OpenType === 'popview') {
        btntabs.push(`select '${item.uuid}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${(index + 1) * 10}' as Sort`)
      }
      btnParam.LText.push(`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')
    btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
    let tabParam = { // 添加标签按钮tab页
      func: 'sPC_sMenusTab_AddUpt',
      MenuID: _config.uuid,
      LText: btntabs.join(' union all ')
    }
    tabParam.LText = Utils.formatOptions(tabParam.LText)
    tabParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
    let param = {
      func: 'sPC_Tab_AddUpt',
      MenuID: _config.uuid,
      MenuNo: _config.tabNo,
      Template: 'SubTable',
      MenuName: _config.tabName,
      Remark: _config.Remark,
      Sort: 0,
      PageParam: JSON.stringify({Template: 'SubTable'}),
      LongParam: _LongParam
    }
    if (openEdition) {
      param.open_edition = openEdition
    }
    // 有按钮或标签删除时,先进行删除操作
    // 删除成功后,保存页面配置
    new Promise(resolve => {
      if (delActions.length > 0) {
        let deffers = delActions.map(item => {
          let _param = {
            func: 'sPC_MainMenu_Del',
            MenuID: item.card.uuid
          }
          let _ParentParam = null
          try {
            _ParentParam = window.btoa(window.encodeURIComponent(JSON.stringify(item.card)))
          } catch (e) {
            console.warn('Stringify Failure')
            _ParentParam = null
          }
          if (_ParentParam) { // 删除按钮时,保存按钮配置信息,用于恢复按钮
            _param.ParentParam = _ParentParam
          }
          return new Promise(resolve => {
            Api.getSystemConfig(_param).then(response => {
              resolve(response)
            })
          })
        })
        Promise.all(deffers).then(result => {
          let error = null
          result.forEach(response => {
            if (!response.status) {
              error = response
            }
          })
        } else if (delActions.length === 0) {
          resolve(true)
        }
      }).then(resp => {
        if (resp === false) return
        Api.getSystemConfig(param).then(response => {
          if (response.status) {
            this.setState({
              config: _config,
              originConfig: _config
            }, () => {
              this.setState({
                menuloading: false,
                menucloseloading: false
              })
              this.submitAction(btnParam)
            })
          } else {
          if (error) {
            this.setState({
              menuloading: false,
              menucloseloading: false
            })
            notification.warning({
              top: 92,
              message: response.message,
              duration: 10
              message: error.message,
              duration: 5
            })
            resolve(false)
          } else {
            this.setState({
              delActions: []
            })
            resolve(true)
          }
        })
      })
    }, () => {
      notification.warning({
        top: 92,
        message: this.state.dict['header.menu.basemsg'],
        duration: 10
      } else if (delActions.length === 0) {
        resolve(true)
      }
    }).then(resp => {
      if (resp === false) return
      if (thawButtons.length > 0) {
        let defers = thawButtons.map(item => {
          return new Promise((resolve) => {
            Api.getSystemConfig({
              func: 'sPC_MainMenu_ReDel',
              MenuID: item
            }).then(res => {
              if (res.status) {
                resolve('')
              } else {
                resolve(res.message)
              }
            })
          })
        })
        return Promise.all(defers)
      } else {
        return true
      }
    }).then(res => {
      if (res === true || res === false) return res
      let msg = res.filter(Boolean)[0]
      if (msg) {
        notification.warning({
          top: 92,
          message: msg,
          duration: 5
        })
        return false
      } else {
        this.setState({
          thawButtons: []
        })
        return true
      }
    }).then(resp => {
      if (resp === false) return
      Api.getSystemConfig(param).then(response => {
        if (response.status) {
          this.setState({
            openEdition: response.open_edition || '',
            config: _config,
            originConfig: fromJS(_config).toJS()
          }, () => {
            this.setState({
              menuloading: false,
              menucloseloading: false
            })
            this.submitAction(btnParam, tabParam)
          })
        } else {
          this.setState({
            menuloading: false,
            menucloseloading: false
          })
          notification.warning({
            top: 92,
            message: response.message,
            duration: 5
          })
        }
      })
    })
  }
@@ -1271,24 +494,57 @@
  /**
   * @description 保存或修改菜单按钮
   */
  submitAction = (param) => {
  submitAction = (btnParam, tabParam) => {
    const { config } = this.state
    new Promise(resolve => {
      if (param.LText) {
        Api.getSystemConfig(param).then(res => {
          if (res.status) {
            resolve(true)
          } else {
      let deffers = []
      if (tabParam.LText) {
        let defer = new Promise(resolve => {
          Api.getSystemConfig(tabParam).then(result => {
            resolve(result)
          })
        })
        deffers.push(defer)
      }
      if (btnParam.LText) {
        let defer = new Promise(resolve => {
          Api.getSystemConfig(btnParam).then(result => {
            if (result.status) {
              this.setState({ // 保存成功后清空复制列表
                copyActions: []
              })
            }
            resolve(result)
          })
        })
        deffers.push(defer)
      }
      if (deffers.length === 0) {
        resolve(true)
      } else {
        Promise.all(deffers).then(result => {
          let error = false
          result.forEach(res => {
            if (!res.status) {
              error = res
            }
          })
          if (error) {
            notification.warning({
              top: 92,
              message: res.message,
              duration: 10
              message: error.message,
              duration: 5
            })
            resolve(false)
          } else {
            resolve(true)
          }
        })
      } else {
        resolve(true)
      }
    }).then(response => {
      if (response === false) return response
@@ -1298,6 +554,7 @@
        let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 查看初始化按钮是否存在
        if (!curBtn) return
        if (curBtn.OpenType !== item.prebtn.OpenType) return
        if (curBtn.OpenType === 'funcbutton' && curBtn.execMode !== 'pop') return
        oriActions.push({
          prebtn: item.prebtn,
@@ -1316,10 +573,10 @@
            let _LongParam = ''
  
            if (result.LongParam) {
              _LongParam = window.decodeURIComponent(window.atob(result.LongParam))
              try {
                _LongParam = JSON.parse(_LongParam)
                _LongParam = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
              } catch (e) {
                console.warn('Parse Failure')
                _LongParam = ''
              }
            }
@@ -1373,505 +630,317 @@
    if (originConfig.isAdd) {
      confirm({
        content: '菜单尚未提交,确定放弃保存吗?',
        okText: this.state.dict['header.confirm'],
        cancelText: this.state.dict['header.cancel'],
        onOk() {
          _this.handleViewBack()
        },
        onCancel() {}
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables, ...res}
        if (!is(fromJS(originConfig), fromJS(_config))) {
          this.setState({
            closeVisible: true
          })
        } else {
          this.handleViewBack()
        }
      }, () => {
      if (!is(fromJS(originConfig), fromJS(config))) {
        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({
      config: {...config, [addType]: items}
    })
    notification.success({
      top: 92,
      message: '操作成功',
      duration: 2
    })
  }
  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 { 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: config.uuid
        }
        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)
      } else {
        this.handleViewBack()
      }
      this.setState({
        config: {...config, setting: res},
        settingVisible: false,
      })
    })
    }
  }
  /**
   * @description 设置可配置按钮
   */
  setSubConfig = (btn) => {
    const {menu, editTab, tabConfig, editSubTab} = this.props
    const { config, originConfig } = this.state
    const {menu, editTab, tabConfig, editSubTab, btnTab, btnTabConfig} = this.props
    const { config, originConfig, activeKey, openEdition } = this.state
    if (originConfig.isAdd) {
      notification.warning({
        top: 92,
        message: '菜单尚未保存,请保存菜单配置!',
        duration: 10
        duration: 5
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tables: this.state.selectedTables, ...res}
        if (!is(fromJS(originConfig), fromJS(_config))) {
          notification.warning({
            top: 92,
            message: '菜单配置已修改,请保存!',
            duration: 10
          })
        } else {
          this.setState({
            loading: true
          })
          // 子菜单信息验证通过后,跳转子按钮配置页面
          let _view = ''
          let _subtab = editSubTab
          if (btn.OpenType === 'pop') {
            _view = 'Modal'             // 表单页面
          } else if (btn.OpenType === 'popview') {
            _view = btn.tabType        // 新弹窗标签模板
            _subtab = btn
          }
          let param = {
            editMenu: menu,
            editTab: editTab,
            tabConfig: editSubTab ? tabConfig : originConfig,
            editSubTab: _subtab,
            subTabConfig: editSubTab ? originConfig : null,
            editAction: btn,
            subConfig: '',
            tabview: _view
          }
          Api.getSystemConfig({
            func: 'sPC_Get_LongParam',
            MenuID: btn.OpenType === 'popview' ? btn.linkTab : btn.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 (_LongParam && param.tabview === 'Modal' && _LongParam.type === 'Modal') {
                param.subConfig = _LongParam
              } else if (_LongParam && param.tabview === 'SubTable' && _LongParam.Template === 'SubTable') {
                param.subConfig = _LongParam
              }
              this.props.handleView(param)
            } else {
              this.setState({
                loading: false
              })
              notification.warning({
                top: 92,
                message: res.message,
                duration: 10
              })
            }
          })
        }
      }, () => {
      if (!is(fromJS(originConfig), fromJS(config))) {
        notification.warning({
          top: 92,
          message: '菜单基本信息已修改,请保存!',
          duration: 10
          message: '菜单配置已修改,请保存!',
          duration: 5
        })
      })
      } else {
        // 子菜单信息验证通过后,跳转子按钮配置页面
        let _view = ''
        let _subtab = editSubTab
        if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
          _view = 'Modal'      // 表单页面
        } else if (btn.OpenType === 'popview') {
          _view = 'SubTable'   // 新弹窗标签模板 tabType 属性已去除
          _subtab = btn
          if (editSubTab) {
            notification.warning({
              top: 92,
              message: '弹窗(标签)中不支持此按钮打开方式!',
              duration: 5
            })
            return
          }
        }
        if (editSubTab) {
          editSubTab.activeKey = activeKey
          editSubTab.open_edition = openEdition  // 更新版本号
        } else {
          editTab.activeKey = activeKey
          editTab.open_edition = openEdition     // 更新版本号
        }
        let param = {
          editMenu: menu,
          editTab: editTab,
          tabConfig: editSubTab ? tabConfig : originConfig,
          editSubTab: _subtab,
          subTabConfig: editSubTab ? originConfig : null,
          btnTab: btnTab,
          btnTabConfig: btnTabConfig,
          editAction: btn,
          subConfig: '',
          tabview: _view
        }
        this.setState({
          loading: true
        })
        Api.getSystemConfig({
          func: 'sPC_Get_LongParam',
          MenuID: btn.OpenType === 'popview' ? btn.linkTab : btn.uuid
        }).then(res => {
          if (res.status) {
            this.setState({
              loading: false
            })
            let _LongParam = ''
            if (res.LongParam) {
              try {
                _LongParam = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
              } catch (e) {
                console.warn('Parse Failure')
                _LongParam = ''
              }
            }
            if (_LongParam && param.tabview === 'Modal' && _LongParam.type === 'Modal') {
              param.subConfig = _LongParam
            } else if (_LongParam && param.tabview === 'SubTable' && _LongParam.Template === 'SubTable') {
              param.subConfig = _LongParam
            }
            if (param.editAction) {
              param.editAction.open_edition = res.open_edition || ''
            } else if (param.editSubTab) {
              param.editSubTab.open_edition = res.open_edition || ''
            }
            this.props.handleView(param)
          } else {
            this.setState({
              loading: false
            })
            notification.warning({
              top: 92,
              message: res.message,
              duration: 5
            })
          }
        })
      }
    }
  }
  /**
   * @description 切换标签是否启用
   */
  onEnabledChange = () => {
    const { config } = this.state
    if (config.setting.interType === 'inner' && !config.setting.innerFunc && !config.setting.dataresource) {
    let _enabled = !config.enabled
    let result = this.verifyconfig(config)
    if (_enabled && result !== true) {
      notification.warning({
        top: 92,
        message: '菜单尚未设置数据源,不可启用!',
        duration: 10
        message: result,
        duration: 5
      })
      return
    }
    this.setState({
      config: {...config, enabled: _enabled}
    })
  }
  /**
   * @description 校验配置信息的合法性
   */
  verifyconfig = (config) => {
    let hasKey = false
    let chartcols = []
    config.columns.forEach(col => {
      if (col.field) {
        chartcols.push(col.field)
      }
      if (config.setting.primaryKey === col.field) {
        hasKey = true
      }
    })
    let chartError = ''
    config.charts && config.charts.forEach((chart, index) => {
      if (chartError) return
      if (chart.Hide === 'true') return
      if (!['line', 'bar', 'pie'].includes(chart.chartType)) return
      if (!chart.Xaxis) {
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段尚未设置,不可启用!`
      } else if (['line', 'bar'].includes(chart.chartType) && chart.datatype !== 'statistics' && (!chart.Yaxis || chart.Yaxis.length === 0)) { // query 查询数据
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段尚未设置,不可启用!`
      } else if (['line', 'bar'].includes(chart.chartType) && chart.datatype === 'statistics' && (!chart.InfoType || !chart.InfoValue)) { // statistics 统计数据
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段尚未设置,不可启用!`
      } else if (chart.chartType === 'pie' && !chart.Yaxis) {
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段尚未设置,不可启用!`
      } else if (!chartcols.includes(chart.Xaxis)) {
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段在显示列中不存在,不可启用!`
      } else if (chart.chartType === 'pie' && !chartcols.includes(chart.Yaxis)) {
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段在显示列中不存在,不可启用!`
      } else if (['line', 'bar'].includes(chart.chartType) && chart.datatype === 'statistics' && (!chartcols.includes(chart.InfoType) || !chartcols.includes(chart.InfoValue))) { // statistics 统计数据
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段在显示列中不存在,不可启用!`
      } else if (['line', 'bar'].includes(chart.chartType) && chart.datatype !== 'statistics' && chart.Yaxis.filter(yaxis => !chartcols.includes(yaxis)).length > 0) {
        chartError = `图表${chart.title ? '《' + chart.title + '》' : index + 1}坐标轴字段在显示列中不存在,不可启用!`
      }
    })
    config.action && config.action.forEach((btn) => {
      if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
        let hascheck = false
        btn.verify.scripts.forEach(item => {
          if (item.status === 'false') return
          if (/\$check@|@check\$/ig.test(item.sql)) {
            hascheck = true
          }
        })
        if (hascheck) {
          notification.warning({
            top: 92,
            message: `可选择多行的按钮《${btn.label}》中 $check@ 或 @check$ 将不会生效!`,
            duration: 5
          })
        }
      }
    })
    if ((config.setting.interType === 'system' || config.setting.requestMode === 'system') && config.setting.default === 'false' && config.setting.scripts && config.setting.scripts.filter(item => item.status !== 'false').length === 0) {
      return '数据源中不执行默认sql,且未添加自定义脚本,不可启用!'
    } else if (config.setting.interType === 'custom' && config.setting.procMode !== 'inner' && config.setting.preScripts && config.setting.preScripts.filter(item => item.status !== 'false').length === 0) {
      return '数据源未设置前置脚本,不可启用!'
    } else if (config.setting.interType === 'custom' && config.setting.callbackType === 'script' && config.setting.cbScripts && config.setting.cbScripts.filter(item => item.status !== 'false').length === 0) {
      return '数据源未设置回调脚本,不可启用!'
    } else if (!config.setting.primaryKey) {
      return '尚未设置主键,不可启用!'
    }  else if (config.columns.length === 0) {
      return '尚未设置显示列,不可启用!'
    } else if (!hasKey) {
      return '显示列中不存在主键字段,不可启用!'
    } else if (chartError) {
      return chartError
    } else {
      this.setState({
        config: {...config, enabled: !config.enabled}
      })
      return true
    }
  }
  onColumnNameChange = () => {
    const { showColumnName } = this.state
  /**
   * @description 编辑功能完成更新,包括解冻按钮、粘贴、替换等
   */
  updateConfig = (res) => {
    if (res.type === 'thaw') {
      this.setState({
        thawButtons: res.thawButtons,
        config: res.config
      })
    } else if (res.type === 'paste') {
      this.setState({config: res.config})
    }
  }
  /**
   * @description 更新搜索条件配置信息
   */
  updatesearch = (config) => {
    this.setState({
      showColumnName: !showColumnName
      config: config
    })
  }
  /**
   * @description 更新按钮配置信息
   */
  updateaction = (config, copyId, delcard) => {
    const { copyActions, delActions } = this.state
    this.setState({
      config: config,
      copyActions: copyId ? [...copyActions, copyId] : copyActions,
      delActions: delcard ? [...delActions, delcard] : delActions
    })
  }
  /**
   * @description 更新显示列配置信息
   */
  updateconfig = (config) => {
    this.setState({
      config: config
    })
  }
  /**
   * @description 更新图表组配置信息
   */
  updatechartgroup = (config, _chartview) => {
    this.setState({
      config: config,
      chartview: _chartview
    })
  }
  render () {
    const { modaltype } = this.state
    const configAction = this.state.config.action.filter(_action =>
      !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
    )
    const { activeKey, config, chartview } = this.state
    const confActions = config.action.filter(_action => !_action.origin && (['pop', 'popview'].includes(_action.OpenType) || (_action.OpenType === 'funcbutton' && _action.execMode === 'pop')))
    return (
      <div className="common-table-board">
      <div className="model-subtable-board">
        <DndProvider backend={HTML5Backend}>
          {/* 工具栏 */}
          <div className="tools">
            <Collapse accordion defaultActiveKey="0" bordered={false}>
            <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
              {/* 基本信息 */}
              <Panel header={'标签基本信息'} key="0" id="common-basedata">
              <Panel forceRender={true} header={'标签基本信息'} key="0" id="subtable-basedata">
                {/* 菜单信息 */}
                <MenuForm
                  dict={this.state.dict}
                  formlist={this.state.menuformlist}
                  wrappedComponentRef={(inst) => this.menuformRef = inst}
                  config={config}
                  updatemenu={this.updateconfig}
                />
                {/* 表名添加 */}
                <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>}
                />}
                <TableComponent
                  config={config}
                  containerId="subtable-basedata"
                  updatetable={this.updateconfig}
                />
              </Panel>
              {/* 搜索条件添加 */}
              <Panel header={this.state.dict['header.menu.search']} key="1">
@@ -1880,7 +949,11 @@
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <Button type="primary" block onClick={() => this.queryField('search')}>{this.state.dict['header.menu.search.add']}</Button>
                <FieldsComponent
                  config={config}
                  type="search"
                  updatefield={this.updateconfig}
                />
              </Panel>
              {/* 按钮添加 */}
              <Panel header={this.state.dict['header.menu.action']} key="2">
@@ -1889,15 +962,17 @@
                    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) => {
                <div className="config-btn">
                  {confActions.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
                  }
                </div>
                {confActions.map((item, index) => {
                  return (
                    <div key={index}>
                      <Button
@@ -1917,225 +992,102 @@
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <Button type="primary" block onClick={() => this.queryField('columns')}>{this.state.dict['header.menu.column.add']}</Button>
                <FieldsComponent
                  config={config}
                  type="columns"
                  updatefield={this.updateconfig}
                />
              </Panel>
            </Collapse>
          </div>
          <div className="setting">
            <Card title={'标签(子表)页面配置'} bordered={false} extra={
            <Card title={
              <div>
                <Switch className="big" checkedChildren="启" unCheckedChildren="停" checked={this.state.config.enabled} onChange={this.onEnabledChange} />
                <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>
                标签(子表)页面配置
                <Icon type="redo" style={{marginLeft: '10px'}} title="刷新标签列表" onClick={() => this.reloadTab(true)} />
              </div>
            } bordered={false} extra={
              <div>
                <EditComponent dict={this.state.dict} options={['search', 'action', 'columns']} config={config} MenuID={config.uuid} thawButtons={this.state.thawButtons} refresh={this.updateConfig}/>
                <Switch className="big" checkedChildren="启" unCheckedChildren="停" checked={config.enabled} onChange={this.onEnabledChange} />
                <Button type="primary" onClick={this.submitConfig} loading={this.state.menuloading}>{this.state.dict['model.save']}</Button>
                <Button onClick={this.cancelConfig}>{this.state.dict['model.back']}</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>
                <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']}
                />
              </div>
              <div className="action-list">
                <Tooltip placement="bottomLeft" overlayClassName="middle" title="在左侧工具栏《按钮》中,选择对应类型的按钮拖至此处添加,如选择按钮类型为表单、新标签页等含有配置页面的按钮,可在左侧工具栏-按钮-可配置按钮处,点击按钮完成相关配置。注:当设置按钮显示位置为表格时,显示列会增加操作列。">
                  <Icon type="question-circle" />
                </Tooltip>
                <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']}
                />
              </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} />
                <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']}
                />
              <SettingComponent
                config={config}
                mainsearch={!this.props.editSubTab && this.props.editTab.mainsearch ? this.props.editTab.mainsearch : ''}
                MenuID={config.uuid}
                updatesetting={this.updateconfig}
              />
              <SearchComponent
                config={config}
                updatesearch={this.updatesearch}
              />
              <div className="chart-view" style={{position: 'relative'}}>
                {/* 视图组 权限 会员等级20+ */}
                {this.props.memberLevel >= 20 ? <ChartGroupComponent
                  config={config}
                  updatechartgroup={this.updatechartgroup}
                /> : null}
                {config.charts.map(item => {
                  if (!config.expand && chartview !== item.uuid) return ''
                  if (item.chartType === 'table') {
                    return (
                      <Col span={item.width || 24} key={item.uuid}>
                        {config.charts.length > 1 ? <p className="chart-title">{item.title}</p> : null}
                        <ActionComponent
                          type="subtable"
                          menu={{MenuID: config.uuid, MenuName: config.tabName, MenuNo: config.tabNo, fstMenuList: this.props.menu.fstMenuList}}
                          config={config}
                          tabs={this.state.tabviews}
                          setSubConfig={this.setSubConfig}
                          updateaction={this.updateaction}
                        />
                        <ColumnComponent
                          config={config}
                          menu={this.props.menu}
                          updatecolumn={this.updateconfig}
                        />
                      </Col>
                    )
                  } else if (item.chartType === 'card') {
                    return (
                      <Col span={item.width} key={item.uuid}>
                        <CardComponent
                          card={item}
                          config={config}
                          plotchange={this.updateconfig}
                        />
                      </Col>
                    )
                  } else {
                    return (
                      <Col span={item.width} key={item.uuid}>
                        <ChartComponent
                          plot={item}
                          config={config}
                          plotchange={this.updateconfig}
                        />
                      </Col>
                    )
                  }
                })}
              </div>
            </Card>
          </div>
        </DndProvider>
        {/* 编辑搜索条件 */}
        <Modal
          title={this.state.dict['header.modal.search.edit']}
          visible={modaltype === 'search'}
          width={700}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <SearchForm
            dict={this.state.dict}
            card={this.state.card}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.searchFormRef = inst}
          />
        </Modal>
        {/* 编辑按钮:复制、编辑 */}
        <Modal
          title={modaltype === 'actionEdit' ? this.state.dict['header.modal.action.edit'] : this.state.dict['header.modal.action.copy']}
          visible={modaltype === 'actionEdit' || modaltype === 'actionCopy'}
          width={700}
          onCancel={this.editModalCancel}
          footer={[
            modaltype === 'actionEdit' ? <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.editModalCancel}>{this.state.dict['header.cancel']}</Button>,
            <Button key="confirm" type="primary" onClick={this.handleSubmit}>{this.state.dict['header.confirm']}</Button>
          ]}
          destroyOnClose
        >
          <ActionForm
            dict={this.state.dict}
            card={this.state.card}
            tabs={this.state.tabviews}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.actionFormRef = inst}
          />
        </Modal>
        {/* 显示列编辑 */}
        <Modal
          title={this.state.dict['header.modal.column.edit']}
          visible={modaltype === 'columns'}
          width={700}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <ColumnForm
            dict={this.state.dict}
            card={this.state.card}
            formlist={this.state.formlist}
            wrappedComponentRef={(inst) => this.columnFormRef = inst}
          />
        </Modal>
        {/* 合并列编辑 */}
        <Modal
          title={this.state.dict['header.modal.colspan.edit']}
          visible={modaltype === 'colspan'}
          width={700}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <ColspanForm
            dict={this.state.dict}
            card={this.state.card}
            columns={this.state.config.columns}
            wrappedComponentRef={(inst) => this.columnFormRef = inst}
          />
        </Modal>
        {/* 操作列编辑 */}
        <Modal
          title={this.state.dict['header.modal.gridbtn.edit']}
          visible={modaltype === 'gridbtn'}
          width={700}
          onOk={this.handleSubmit}
          onCancel={this.editModalCancel}
          destroyOnClose
        >
          <GridBtnForm
            dict={this.state.dict}
            card={this.state.config.gridBtn}
            wrappedComponentRef={(inst) => this.gridBtnFormRef = inst}
          />
        </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 floor="subtable" 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}
            tabId={this.state.config.uuid}
            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}
          maskClosable={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.handleViewBack}>{this.state.dict['header.notsave']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['header.cancel']}</Button>
            <Button key="save" className="mk-btn mk-green" loading={this.state.menucloseloading} onClick={this.submitConfig}>{this.state.dict['model.save']}</Button>,
            <Button key="confirm" className="mk-btn mk-yellow" onClick={this.handleViewBack}>{this.state.dict['model.notsave']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['model.cancel']}</Button>
          ]}
          destroyOnClose
        >
@@ -2149,7 +1101,7 @@
const mapStateToProps = (state) => {
  return {
    permFuncField: state.permFuncField
    memberLevel: state.memberLevel
  }
}