king
2023-06-15 a29d9d644a2a30e9ef4afcc6d728c20c218dc359
src/templates/subtableconfig/index.jsx
@@ -1,33 +1,32 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {connect} from 'react-redux'
import { is, fromJS } from 'immutable'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import { Button, Card, Modal, Collapse, notification, Spin, Icon, Switch, Tooltip, Col } from 'antd'
import moment from 'moment'
import { Button, Card, Modal, Collapse, notification, Spin, Switch, Tooltip, Col } from 'antd'
import { QuestionCircleOutlined, RedoOutlined } from '@ant-design/icons'
// import moment from 'moment'
import Api from '@/api'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import Utils from '@/utils/utils.js'
import { getSubMenuForm } from '@/templates/zshare/formconfig'
import { updateSubTable } from '@/utils/utils-update.js'
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 '@/templates/zshare/menuform'
import EditComponent from '@/templates/zshare/editcomponent'
import MenuForm from './menuform'
import SourceElement from '@/templates/zshare/dragsource'
import Source from './source'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = (!localStorage.getItem('lang') || localStorage.getItem('lang') === 'zh-CN') ? zhCN : enUS
const Versions = asyncComponent(() => import('@/menu/versions'))
// const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
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'))
@@ -38,7 +37,6 @@
class SubTableConfig extends Component {
  static propTpyes = {
    menu: PropTypes.any,
    optionLibs: PropTypes.any,
    editTab: PropTypes.any,
    tabConfig: PropTypes.any,
    editSubTab: PropTypes.any,
@@ -49,13 +47,8 @@
  }
  state = {
    dict: CommonDict,        // 字典
    config: null,            // 页面配置
    visible: false,          // 搜索条件、按钮、显示列,模态框显示控制
    tableFields: [],         // 已选表字段集
    fields: null,            // 搜索条件及显示列,可选字段
    menuformlist: null,      // 基本信息表单字段
    formlist: null,          // 搜索条件、按钮、显示列表单字段
    menuloading: false,      // 菜单保存中
    menucloseloading: false, // 菜单关闭时,选择保存
    loading: false,          // 加载中,页面spin
@@ -65,12 +58,9 @@
    delActions: [],          // 删除按钮列表
    copyActions: [],         // 复制按钮组
    tabviews: [],            // 所有标签页
    optionLibs: null,        // 自定义下拉选项库
    thawButtons: [],         // 已选择要解冻的按钮
    activeKey: '0',          // 默认展开基本信息
    chartview: null,         // 当前视图
    pasteContent: null,      // 粘贴内容
    openEdition: ''          // 编辑版本标记,防止多人操作
    openEdition: '',         // 编辑版本标记,防止多人操作
  }
  /**
@@ -79,33 +69,17 @@
   * 2、设置操作类型、原始菜单信息(每次保存后重置)、已使用表及基本信息表单
   */
  UNSAFE_componentWillMount () {
    const { config, editTab, editSubTab, optionLibs } = this.props
    const { config, editTab, editSubTab } = this.props
    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.search.forEach(item => {
        if (
          (item.type === 'select' || item.type === 'multiselect' || item.type === 'link') &&
          item.resourceType === '0' &&
          item.options && item.options.length > 0
        ) {
          optionLibs.set(_config.uuid + item.uuid, {
            uuid: _config.uuid + item.uuid,
            label: item.label,
            parname: _config.tabName,
            type: 'search',
            options: item.options
          })
        }
      })
      _config = fromJS(config).toJS()
    }
    
    let _oriActions = []
@@ -113,9 +87,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'
          })
@@ -128,66 +107,16 @@
    let _activeKey =  editSubTab ? editSubTab.activeKey : editTab.activeKey
    if (!_config.version || _config.version < '1.0') {
      // 配置默认值,兼容
      _config.version = '1.0'
      // 兼容图表
      if (!_config.charts) {
        _config.expand = false
        _config.charts = [{
          uuid: Utils.getuuid(),
          label: '',
          title: '',
          chartType: 'table',
          icon: 'table',
          Hide: 'false',
          blacklist: []
        }]
      } else {
        _config.charts.forEach(card => {
          if (card.chartType === 'card') {
            card.details = card.details.map(_cell => {
              if (!_cell.fontSize) {
                _cell.fontSize = 14
              }
              if (!_cell.width) {
                _cell.width = 100
              } else if (_cell.width === 'helf') {
                _cell.width = 50
              } else if (_cell.width === 'third') {
                _cell.width = 33
              }
              if (_cell.bold === 'true') {
                _cell.fontWeight = 'normal'
              }
              if (!_cell.height) {
                _cell.height = 1
              }
              return _cell
            })
            if (card.widthType === 'ratio' && card.avatar && card.avatar.widthType !== 'ratio') {
              card.avatar.widthType = 'ratio'
              card.avatar.width = 32
            }
          }
        })
      }
    }
    // 版本兼容
    _config = updateSubTable(_config)
    this.setState({
      openEdition: editSubTab ? (editSubTab.open_edition || '') : (editTab.open_edition || ''),
      chartview: _config.charts[0].uuid,
      chartview: _config.charts ? _config.charts[0].uuid : '',
      originActions: _oriActions,
      optionLibs: optionLibs,
      config: _config,
      activeKey: _activeKey || '0',
      originConfig: _config,
      menuformlist: getSubMenuForm(_config)
      originConfig: fromJS(_config).toJS(),
    })
  }
@@ -198,6 +127,54 @@
   */
  componentDidMount () {
    this.reloadTab(false)
    document.onkeydown = (event) => {
      let e = event || window.event
      let keyCode = e.keyCode || e.which || e.charCode
      let preKey = ''
      if (e.ctrlKey) {
        preKey = 'ctrl'
      }
      if (e.shiftKey) {
        preKey = 'shift'
      } else if (e.altKey) {
        preKey = 'alt'
      }
      if (!preKey || !keyCode) return
      let _shortcut = `${preKey}+${keyCode}`
      if (_shortcut === 'ctrl+83') {
        let modals = document.querySelectorAll('.mk-pop-modal')
        let msg = null
        for (let i = 0; i < modals.length; i++) {
          if (msg) {
            break
          }
          let node = modals[i].querySelector('.mk-com-name')
          if (node) {
            msg = node.innerText
          }
        }
        if (msg) {
          notification.warning({
            top: 92,
            message: '请保存' + msg,
            duration: 5
          })
          return false
        }
        let node = document.getElementById('save-config')
        if (node && node.click) {
          node.click()
        }
        return false
      }
    }
  }
  /**
@@ -257,6 +234,7 @@
    this.setState = () => {
      return
    }
    document.onkeydown = () => {}
  }
  // 页面返回
@@ -268,7 +246,7 @@
    if (editSubTab) {
      _subconfig = tabConfig
      if (editTab.hasOwnProperty('OpenType')) {
        _tabview = editTab.tabType
        _tabview = editTab.tabType || 'SubTable'
      } else {
        _tabview = editTab.type
      }
@@ -279,7 +257,6 @@
    let param = {
      editMenu: menu,
      optionLibs: this.state.optionLibs,
      editTab: editSubTab ? editTab : null,
      tabConfig: null,
      editSubTab: null,
@@ -306,391 +283,179 @@
   * @description 标签页保存
   */
  submitConfig = () => {
    const { delActions, thawButtons, originConfig, openEdition } = this.state
    let config = JSON.parse(JSON.stringify(this.state.config))
    const { delActions, 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 (copyreg.test(res.MenuNo) || copyreg.test(res.MenuName)) {
        notification.warning({
          top: 92,
          message: '请修改标签名称和标签参数,不可以时间格式 YYYY-MM-DD HH:mm:ss 结尾!',
          duration: 5
        })
        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)
        }
      }
      let _LongParam = ''
      let _config = {...config, tabName: res.MenuName, tabNo: res.MenuNo, Remark: res.Remark}
      // 未设置数据源或主键时,启用状态为false
      let result = this.verifyconfig(_config)
      if (result !== true) {
        _config.enabled = false
      }
      _config.funcs = []
      _config.funcs.push({
        type: 'view',
        subtype: 'view',
        uuid: _config.uuid,
        intertype: _config.setting.interType || 'inner',
        interface: _config.setting.interface || '',
        tableName: _config.setting.tableName || '',
        innerFunc: _config.setting.innerFunc || '',
        outerFunc: _config.setting.outerFunc || ''
      })
      _config.action.forEach(item => {
        let tablename = item.OpenType === 'excelIn' ? (item.sheet || '') : (item.sql || '')
        if (item.OpenType === 'excelOut' && item.intertype === 'inner' && !item.innerFunc) {
          tablename = _config.setting.tableName || ''
        }
        if (item.OpenType === 'popview') {
          _config.funcs.push({
            type: 'tab',
            subtype: 'btn',
            uuid: item.uuid,
            label: item.label,
            linkTab: item.linkTab
          })
        } else {
          _config.funcs.push({
            type: 'button',
            subtype: 'btn',
            uuid: item.uuid,
            label: item.label,
            tableName: tablename,
            intertype: item.intertype,
            interface: item.interface || '',
            innerFunc: item.innerFunc || '',
            outerFunc: item.outerFunc || '',
            callbackFunc: item.callbackFunc || ''
          })
        }
      })
      if (this.state.closeVisible) { // 显示关闭对话框时,模态框中保存按钮,显示保存中状态
        this.setState({
          menucloseloading: true
        })
      } else {
        this.setState({
          menuloading: true
        })
      }
      new Promise(resolve => {
        let deffers = []
        _config.funcs.forEach(item => {
          if (item.type === 'tab') {
            let deffer = new Promise(resolve => {
              Api.getSystemConfig({
                func: 'sPC_Get_LongParam',
                MenuID: item.linkTab
              }).then(result => {
                if (result.status && result.LongParam) {
                  let _LongParam = ''
                  if (result.LongParam) {
                    try {
                      _LongParam = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
                    } catch (e) {
                      console.warn('Parse Failure')
                      _LongParam = ''
                    }
                  }
                  if (_LongParam) {
                    item.menuNo = _LongParam.tabNo
                    item.subfuncs = _LongParam.funcs || []
                  }
                }
                resolve()
              })
            })
            deffers.push(deffer)
          }
        })
        if (deffers.length === 0) {
          resolve()
        } else {
          Promise.all(deffers).then(() => {
            resolve()
          })
        }
      }).then(() => {
        // 保存时删除配置类型,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: 5
          })
          this.setState({
            menucloseloading: false,
            menuloading: false
          })
          return
        }
        let btnParam = {
          func: 'sPC_Button_AddUpt',
          Type: 40,
          ParentID: _config.uuid,
          MenuNo: res.MenuNo,
          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') + '.000'
        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') + '.000'
        tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
        let param = {
          func: 'sPC_Tab_AddUpt',
          MenuID: _config.uuid,
          MenuNo: res.MenuNo,
          Template: 'SubTable',
          MenuName: res.MenuName,
          Remark: res.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
                }
              })
              if (error) {
                this.setState({
                  menuloading: false,
                  menucloseloading: false
                })
                notification.warning({
                  top: 92,
                  message: error.message,
                  duration: 5
                })
                resolve(false)
              } else {
                this.setState({
                  delActions: []
                })
                resolve(true)
              }
            })
          } 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: _config
              }, () => {
                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
              })
            }
          })
        })
      })
    }, () => {
    // 基本信息验证
    if (!_config.tabName || !_config.tabNo) {
      notification.warning({
        top: 92,
        message: this.state.dict['header.menu.basemsg'],
        message: '请完善菜单基本信息!',
        duration: 5
      })
    })
  }
      this.setState({activeKey: '0'})
      return
    }
  /**
   * @description 保存或修改菜单按钮
   */
  submitAction = (btnParam, tabParam) => {
    const { config } = this.state
    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)
      }
      if (_config.action[0] && _config.action[0].origin) {
        _config.action = _config.action.filter(item => !item.origin)
      }
      if (_config.columns[0] && _config.columns[0].origin) {
        _config.columns = _config.columns.filter(item => !item.origin)
      }
    }
    if (_config.setting.doubleClick && _config.action.findIndex((item) => item.uuid === _config.setting.doubleClick) === -1) {
      _config.setting.doubleClick = ''
    }
    // 未设置数据源或主键时,启用状态为false
    let result = this.verifyconfig(_config)
    if (result !== true) {
      _config.enabled = false
    }
    if (this.state.closeVisible) { // 显示关闭对话框时,模态框中保存按钮,显示保存中状态
      this.setState({
        menucloseloading: true
      })
    } else {
      this.setState({
        menuloading: true
      })
    }
    let _LongParam = ''
    // 保存时删除配置类型,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: 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.hidden === 'true') return
    //   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 => {
      let deffers = []
      if (delActions.length > 0) {
        let deffers = delActions.map(item => {
          let _param = {
            func: 'sPC_MainMenu_Del',
            MenuID: item.card.uuid
          }
      if (tabParam.LText) {
        let defer = new Promise(resolve => {
          Api.getSystemConfig(tabParam).then(result => {
            resolve(result)
          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)
            })
          })
        })
        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
          let error = null
          result.forEach(response => {
            if (!response.status) {
              error = response
            }
          })
          if (error) {
            this.setState({
              menuloading: false,
              menucloseloading: false
            })
            notification.warning({
              top: 92,
              message: error.message,
@@ -698,82 +463,125 @@
            })
            resolve(false)
          } else {
            this.setState({
              delActions: []
            })
            resolve(true)
          }
        })
      } else if (delActions.length === 0) {
        resolve(true)
      }
    }).then(response => {
      if (response === false) return response
    }).then(resp => {
      if (resp === false) return
      let oriActions = []
      this.state.originActions.forEach(item => {
        let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 查看初始化按钮是否存在
        if (!curBtn) return
        if (curBtn.OpenType !== item.prebtn.OpenType) return
      return true
    }).then(res => {
      if (res === true || res === false) return res
        oriActions.push({
          prebtn: item.prebtn,
          curBtn: curBtn
        })
      })
      if (oriActions.length === 0) return 'true'
      oriActions.forEach(action => {
        Api.getSystemConfig({
          func: 'sPC_Get_LongParam',
          MenuID: action.prebtn ? action.prebtn.uuid : ''
        }).then(result => {
          if (result.status && result.LongParam) {
            let _LongParam = ''
            if (result.LongParam) {
              try {
                _LongParam = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
              } catch (e) {
                console.warn('Parse Failure')
                _LongParam = ''
              }
            }
            if (_LongParam) {
              let param = {
                func: 'sPC_ButtonParam_AddUpt',
                ParentID: config.uuid,
                MenuID: action.curBtn.uuid,
                MenuNo: config.tabNo,
                Template: _LongParam.type,
                MenuName: action.curBtn.label,
                PageParam: JSON.stringify({Template: _LongParam.type}),
                LongParam: result.LongParam
              }
              Api.getSystemConfig(param).then(() => {})
            }
          }
        })
      })
      return 'true'
    }).then(response => {
      if (response === 'true') {
        notification.success({
      let msg = res.filter(Boolean)[0]
      if (msg) {
        notification.warning({
          top: 92,
          message: '保存成功',
          duration: 2
          message: msg,
          duration: 5
        })
        if (this.state.closeVisible) {
          this.handleViewBack()
        return false
      } else {
        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
            })
            notification.success({
              top: 92,
              message: '保存成功',
              duration: 2
            })
            if (this.state.closeVisible) {
              this.handleViewBack()
            }
          })
          this.submitAction()
        } else {
          this.setState({
            menuloading: false,
            menucloseloading: false
          })
          notification.warning({
            top: 92,
            message: response.message,
            duration: 5
          })
        }
      } else {
        this.setState({
          menuloading: false,
          menucloseloading: false
        })
      }
      })
    })
  }
  /**
   * @description 保存或修改菜单按钮
   */
  submitAction = () => {
    const { config } = this.state
    let oriActions = []
    this.state.originActions.forEach(item => {
      let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 查看初始化按钮是否存在
      if (!curBtn) return
      if (curBtn.OpenType !== item.prebtn.OpenType) return
      if (curBtn.OpenType === 'funcbutton' && curBtn.execMode !== 'pop') return
      oriActions.push({
        prebtn: item.prebtn,
        curBtn: curBtn
      })
    })
    if (oriActions.length === 0) return
    oriActions.forEach(action => {
      Api.getSystemConfig({
        func: 'sPC_Get_LongParam',
        MenuID: action.prebtn ? action.prebtn.uuid : ''
      }).then(result => {
        if (result.status && result.LongParam) {
          let _LongParam = ''
          if (result.LongParam) {
            try {
              _LongParam = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
            } catch (e) {
              console.warn('Parse Failure')
              _LongParam = ''
            }
          }
          if (_LongParam) {
            let param = {
              func: 'sPC_ButtonParam_AddUpt',
              ParentID: config.uuid,
              MenuID: action.curBtn.uuid,
              MenuNo: config.tabNo,
              Template: _LongParam.type,
              MenuName: action.curBtn.label,
              PageParam: JSON.stringify({Template: _LongParam.type}),
              LongParam: result.LongParam
            }
            Api.getSystemConfig(param).then(() => {})
          }
        }
      })
    })
  }
@@ -785,29 +593,19 @@
    if (originConfig.isAdd) {
      confirm({
        content: '菜单尚未提交,确定放弃保存吗?',
        okText: this.state.dict['model.confirm'],
        cancelText: this.state.dict['header.cancel'],
        onOk() {
          _this.handleViewBack()
        },
        onCancel() {}
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tabName: res.MenuName, tabNo: res.MenuNo, Remark: res.Remark}
        if (!is(fromJS(originConfig), fromJS(_config))) {
          this.setState({
            closeVisible: true
          })
        } else {
          this.handleViewBack()
        }
      }, () => {
      if (!is(fromJS(originConfig), fromJS(config))) {
        this.setState({
          closeVisible: true
        })
      })
      } else {
        this.handleViewBack()
      }
    }
  }
@@ -825,103 +623,101 @@
        duration: 5
      })
    } else {
      this.menuformRef.handleConfirm().then(res => {
        let _config = {...config, tabName: res.MenuName, tabNo: res.MenuNo, Remark: res.Remark}
        if (!is(fromJS(originConfig), fromJS(_config))) {
          notification.warning({
            top: 92,
            message: '菜单配置已修改,请保存!',
            duration: 5
          })
        } 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
          }
          if (editSubTab) {
            editSubTab.activeKey = activeKey
            editSubTab.open_edition = openEdition  // 更新版本号
          } else {
            editTab.activeKey = activeKey
            editTab.open_edition = openEdition     // 更新版本号
          }
          let param = {
            editMenu: menu,
            optionLibs: this.state.optionLibs,
            editTab: editTab,
            tabConfig: editSubTab ? tabConfig : originConfig,
            editSubTab: _subtab,
            subTabConfig: editSubTab ? originConfig : null,
            btnTab: btnTab,
            btnTabConfig: btnTabConfig,
            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) {
                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
              })
            }
          })
        }
      }, () => {
      if (!is(fromJS(originConfig), fromJS(config))) {
        notification.warning({
          top: 92,
          message: '菜单基本信息已修改,请保存!',
          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
            })
          }
        })
      }
    }
  }
@@ -952,62 +748,77 @@
   * @description 校验配置信息的合法性
   */
  verifyconfig = (config) => {
    let charterr = ''
    config.charts.forEach(chart => {
      if (!charterr && ['line', 'bar', 'pie'].includes(chart.chartType) && !chart.Xaxis) {
        charterr = '图表' + (chart.title ? '《' + chart.title + '》' : '') + '坐标轴未设置,不可启用!'
      }
    })
    let hasKey = false
    let chartcols = []
    config.columns.forEach(col => {
      if (col.field) {
        chartcols.push(col.field)
      }
      if (config.setting.primaryKey === col.field) {
        hasKey = true
      }
    })
    if (config.setting.interType === 'inner' && !config.setting.innerFunc && config.setting.default !== 'false' && !config.setting.dataresource) {
      return '菜单尚未设置数据源,不可启用!'
    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}坐标轴字段在显示列中不存在,不可启用!`
      }
    })
    if (config.setting.interType === '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 '菜单尚未设置主键,不可启用!'
      return '尚未设置主键,不可启用!'
    }  else if (config.columns.length === 0) {
      return '尚未设置显示列,不可启用!'
    } else if (!hasKey) {
      return '显示列中不存在主键字段,不可启用!'
    } else if (charterr) {
      return charterr
    } else if (chartError) {
      return chartError
    } else {
      return true
    }
  }
  /**
   * @description 编辑功能完成更新,包括解冻按钮、粘贴、替换等
   * @description 编辑功能完成更新,包括解冻按钮等
   */
  updateConfig = (res) => {
    if (res.type === 'thaw') {
      this.setState({
        thawButtons: res.thawButtons,
        config: res.config
      })
    } else if (res.type === 'paste') {
      this.setState({
        pasteContent: res.content
      }, () => {
        this.setState({
          pasteContent: null
        })
      })
    }
    this.setState({
      config: res.config
    })
  }
  /**
   * @description 更新搜索条件配置信息
   */
  updatesearch = (config, options) => {
    const { optionLibs } = this.state
  updatesearch = (config) => {
    this.setState({
      config: config,
      optionLibs: options || optionLibs
      config: config
    })
  }
@@ -1042,70 +853,100 @@
      chartview: _chartview
    })
  }
  
  /**
   * @description 更新常用表信息,快捷添加后更新配置信息
   */
  updatetable = (config, fields) => {
    const { tableFields } = this.state
  refreshConfig = () => {
    const { config } = this.props
    let param = {
      func: 'sPC_Get_LongParam',
      MenuID: config.uuid
    }
    this.setState({
      config: config,
      tableFields: fields ? fields : tableFields
    })
  }
    Api.getSystemConfig(param).then(res => {
      if (res.status) {
        let _config = ''
        if (res.LongParam) {
          try {
            _config = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
          } catch (e) {
            console.warn('Parse Failure')
            _config = ''
          }
        }
  /**
   * @description 批量添加,更新配置信息
   */
  updatefield = (config) => {
    this.setState({
      config: config
        if (!_config) {
          notification.warning({
            top: 92,
            message: '未获取到配置信息!',
            duration: 5
          })
          return
        }
        // 版本兼容
        _config = updateSubTable(_config)
        this.setState({
          config: null
        }, () => {
          this.setState({
            chartview: _config.charts ? _config.charts[0].uuid : '',
            config: _config,
            openEdition: res.open_edition || '',
            activeKey: '0',
            originActions: [],
            originConfig: fromJS(_config).toJS()
          })
        })
      } else {
        notification.warning({
          top: 92,
          message: res.message,
          duration: 5
        })
      }
    })
  }
  render () {
    const { activeKey, config, chartview } = this.state
    const { activeKey, config, chartview, openEdition } = this.state
    const confActions = config.action.filter(_action => !_action.origin && ['pop', 'popview', 'blank', 'tab'].includes(_action.OpenType))
    if (!config) return null
    const confActions = config.action.filter(_action => !_action.origin && (['pop', 'popview'].includes(_action.OpenType) || (_action.OpenType === 'funcbutton' && _action.execMode === 'pop')))
    return (
      <div className="model-subtable-board">
        <DndProvider backend={HTML5Backend}>
          {/* 工具栏 */}
          <div className="tools">
            <Collapse accordion defaultActiveKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
            <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
              {/* 基本信息 */}
              <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}
                />
                {/* 表名添加 */}
                <TableComponent
                  config={config}
                  containerId="subtable-basedata"
                  updatetable={this.updatetable}
                  updatetable={this.updateconfig}
                />
              </Panel>
              {/* 搜索条件添加 */}
              <Panel header={this.state.dict['header.menu.search']} key="1">
              <Panel header="搜索" key="1">
                <div className="search-element">
                  {Source.searchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <FieldsComponent
                  config={config}
                  type="search"
                  tableFields={this.state.tableFields}
                  updatefield={this.updatefield}
                />
                <FieldsComponent config={config} type="search" />
              </Panel>
              {/* 按钮添加 */}
              <Panel header={this.state.dict['header.menu.action']} key="2">
              <Panel header="按钮" key="2">
                <div className="search-element">
                  {Source.actionItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
@@ -1115,9 +956,9 @@
                  {confActions.length > 0 ?
                    <p className="config-btn-title">
                      <Tooltip placement="topLeft" title="点击按钮,可完成或查看按钮配置信息。">
                        <Icon type="question-circle" />
                        <QuestionCircleOutlined className="mk-form-tip" />
                      </Tooltip>
                      {this.state.dict['header.menu.action.configurable']}
                      按钮配置
                    </p> : null
                  }
                </div>
@@ -1135,18 +976,13 @@
                })}
              </Panel>
              {/* 添加显示列 */}
              <Panel header={this.state.dict['header.menu.column']} key="3">
              <Panel header="显示列" key="3">
                <div className="search-element">
                  {Source.columnItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <FieldsComponent
                  config={config}
                  type="columns"
                  tableFields={this.state.tableFields}
                  updatefield={this.updatefield}
                />
                <FieldsComponent config={config} type="columns" />
              </Panel>
            </Collapse>
          </div>
@@ -1154,40 +990,34 @@
            <Card title={
              <div>
                标签(子表)页面配置 
                <Icon type="redo" style={{marginLeft: '10px'}} title="刷新标签列表" onClick={() => this.reloadTab(true)} />
                <RedoOutlined style={{marginLeft: '10px'}} title="刷新标签列表" onClick={() => this.reloadTab(true)} />
              </div>
            } bordered={false} extra={
              <div>
                <EditComponent dict={this.state.dict} type="subtable" config={this.state.config} 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.submitConfig} loading={this.state.menuloading}>{this.state.dict['header.save']}</Button>
                <Button onClick={this.cancelConfig}>{this.state.dict['header.return']}</Button>
                <Versions MenuId={config.uuid} open_edition={openEdition} updateConfig={this.refreshConfig}/>
                {/* <ReplaceField type="table" config={config} updateConfig={this.updateconfig}/> */}
                <EditComponent type="table" options={['search', 'form', 'action', 'columns']} config={config} refresh={this.updateConfig}/>
                <Switch className="big" checkedChildren="启" unCheckedChildren="停" checked={config.enabled} onChange={this.onEnabledChange} />
                <Button type="primary" id="save-config" onClick={this.submitConfig} loading={this.state.menuloading}>保存</Button>
                <Button onClick={this.cancelConfig}>返回</Button>
              </div>
            } style={{ width: '100%' }}>
              <SettingComponent
                type="subtable"
                config={config}
                mainsearch={!this.props.editSubTab && this.props.editTab.mainsearch ? this.props.editTab.mainsearch : ''}
                MenuID={config.uuid}
                menuformRef={this.menuformRef}
                permFuncField={this.props.permFuncField}
                updatesetting={this.updateconfig}
              />
              <SearchComponent
                menu={{MenuID: config.uuid, MenuName: config.tabName}}
                config={config}
                pasteContent={this.state.pasteContent}
                sysRoles={this.props.sysRoles}
                optionLibs={this.state.optionLibs}
                updatesearch={this.updatesearch}
              />
              <div className="chart-view" style={{position: 'relative'}}>
                {/* 视图组 权限 会员等级20+ */}
                {this.props.memberLevel >= 20 ? <ChartGroupComponent
              {config.charts ? <div className="chart-view" style={{position: 'relative'}}>
                {/* 视图组 已弃用 */}
                <ChartGroupComponent
                  config={config}
                  sysRoles={this.props.sysRoles}
                  updatechartgroup={this.updatechartgroup}
                /> : null}
                />
                {config.charts.map(item => {
                  if (!config.expand && chartview !== item.uuid) return ''
@@ -1197,20 +1027,15 @@
                        {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}}
                          menu={{MenuID: config.uuid, MenuName: config.tabName, MenuNo: config.tabNo, fstMenuList: this.props.menu.fstMenuList}}
                          config={config}
                          tabs={this.state.tabviews}
                          menuformRef={this.menuformRef}
                          pasteContent={this.state.pasteContent}
                          usefulFields={this.props.permFuncField}
                          setSubConfig={this.setSubConfig}
                          updateaction={this.updateaction}
                        />
                        <ColumnComponent
                          config={config}
                          menu={this.props.menu}
                          sysRoles={this.props.sysRoles}
                          pasteContent={this.state.pasteContent}
                          updatecolumn={this.updateconfig}
                        />
                      </Col>
@@ -1237,7 +1062,21 @@
                    )
                  }
                })}
              </div>
              </div> : <>
                <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}
                />
              </>}
            </Card>
          </div>
        </DndProvider>
@@ -1248,13 +1087,13 @@
          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}>保存</Button>,
            <Button key="confirm" className="mk-btn mk-yellow" onClick={this.handleViewBack}>不保存</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>取消</Button>
          ]}
          destroyOnClose
        >
          {this.state.dict['header.menu.config.placeholder']}
          配置已修改,是否保存配置信息?
        </Modal>
        {this.state.loading && <Spin size="large" />}
      </div>
@@ -1262,16 +1101,4 @@
  }
}
const mapStateToProps = (state) => {
  return {
    sysRoles: state.sysRoles,
    permFuncField: state.permFuncField,
    memberLevel: state.memberLevel
  }
}
const mapDispatchToProps = () => {
  return {}
}
export default connect(mapStateToProps, mapDispatchToProps)(SubTableConfig)
export default SubTableConfig