From 84e806945ef8982508a79e0b62a7bcb0881b108c Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期一, 30 十二月 2019 10:09:26 +0800
Subject: [PATCH] 2019-12-30

---
 src/templates/comtableconfig/source.jsx               |   43 +
 src/tabviews/subtabtable/index.scss                   |   36 +
 src/components/sidemenu/editthdmenu/index.jsx         |   10 
 src/templates/comtableconfig/index.jsx                |  282 +++++++++----
 src/templates/comtableconfig/tabform/index.scss       |    4 
 src/templates/comtableconfig/settingform/index.jsx    |   68 ++
 src/templates/subtableconfig/index.scss               |   10 
 src/templates/tableshare/transferform/index.scss      |   21 +
 src/tabviews/commontable/index.scss                   |    3 
 src/templates/comtableconfig/index.scss               |   42 +
 src/templates/comtableconfig/tabdragelement/index.jsx |    7 
 src/tabviews/subtable/index.jsx                       |    4 
 src/tabviews/subtabtable/index.jsx                    |  539 +++++++++++++++++++++++++
 src/tabviews/commontable/index.jsx                    |   54 +-
 src/components/sidemenu/editthdmenu/index.scss        |   14 
 src/templates/tableshare/transferform/index.jsx       |   47 ++
 src/templates/comtableconfig/tabform/index.jsx        |   28 +
 src/assets/css/main.scss                              |   18 
 18 files changed, 1,063 insertions(+), 167 deletions(-)

diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss
index 4955d5a..2d5cbe3 100644
--- a/src/assets/css/main.scss
+++ b/src/assets/css/main.scss
@@ -46,11 +46,13 @@
 html::-webkit-scrollbar-thumb {
   box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
   background: rgba(0, 0, 0, 0.13);
+  border-radius: 5px;
 }
 html::-webkit-scrollbar-track {
   box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
   border: 1px solid rgba(0, 0, 0, 0.07);
   background: rgba(0, 0, 0, 0);
+  border-radius: 3px;
 }
 
 input[type="button"], input[type="submit"], input[type="search"], input[type="reset"] {
@@ -129,6 +131,22 @@
   border: 1px solid rgba(0, 0, 0, 0.07);
   background: rgba(0, 0, 0, 0);
 }
+// 閲嶇疆妯℃�佹婊氬姩鏉�
+.ant-modal-wrap::-webkit-scrollbar {
+  width: 7px;
+}
+.ant-modal-wrap::-webkit-scrollbar-thumb {
+  border-radius: 5px;
+  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+  background: rgba(0, 0, 0, 0.13);
+}
+.ant-modal-wrap::-webkit-scrollbar-track {
+  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+  border-radius: 3px;
+  border: 1px solid rgba(0, 0, 0, 0.07);
+  background: rgba(0, 0, 0, 0);
+}
+
 .ant-modal-mask {
   z-index: 1080!important;
 }
diff --git a/src/components/sidemenu/editthdmenu/index.jsx b/src/components/sidemenu/editthdmenu/index.jsx
index b40bfd3..f5616ee 100644
--- a/src/components/sidemenu/editthdmenu/index.jsx
+++ b/src/components/sidemenu/editthdmenu/index.jsx
@@ -138,6 +138,8 @@
             editMenu: _menu,
             loading: false,
             tabview: _Template.length > 0 ? _menu.PageParam.Template : 'template'
+          }, () => {
+            document.getElementById('root').style.overflowY = 'hidden'
           })
 
           // 妯℃澘涓嶅瓨鍦ㄦ椂閿欒鎻愮ず
@@ -174,6 +176,7 @@
         return
       }
       this.setState({tabview: 'template', type: 'add'})
+      document.getElementById('root').style.overflowY = 'hidden'
     } else if (type === 'thaw') {
       if (previewList && !is(fromJS(previewList), fromJS(this.state.subMenulist))) {
         notification.warning({
@@ -426,11 +429,16 @@
 
   exittabview = () => {
     this.setState({tabview: ''})
+    document.getElementById('root').style.overflowY = 'unset'
   }
 
   handleConfig = (type) => {
     this.setState({tabview: type})
-    document.getElementById('root').style.overflowY = 'unset'
+    if (type) {
+      document.getElementById('root').style.overflowY = 'hidden'
+    } else {
+      document.getElementById('root').style.overflowY = 'unset'
+    }
   }
 
   tabHandleConfig = () => {
diff --git a/src/components/sidemenu/editthdmenu/index.scss b/src/components/sidemenu/editthdmenu/index.scss
index 7304371..eb9340b 100644
--- a/src/components/sidemenu/editthdmenu/index.scss
+++ b/src/components/sidemenu/editthdmenu/index.scss
@@ -162,6 +162,20 @@
         cursor: zoom-in;
       }
     }
+    .workplace::-webkit-scrollbar {
+      width: 7px;
+    }
+    .workplace::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+      background: rgba(0, 0, 0, 0.13);
+    }
+    .workplace::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
   }
   > .loading-thdmenu {
     position: fixed;
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index d91f0bd..c52b108 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { BackTop, notification, Spin, Tabs, Icon, Card, Switch} from 'antd'
+import { BackTop, notification, Spin, Tabs, Icon, Switch} from 'antd'
 import moment from 'moment'
 import Api from '@/api'
 import MainTable from './mainTable'
@@ -9,11 +9,13 @@
 import MainSearch from '@/tabviews/tableshare/topSearch'
 import SubTable from '@/tabviews/subtable'
 import NotFount from '@/components/404'
+// import asyncComponent from '@/utils/asyncLoadComponent'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
+// const SubTabTable = asyncComponent(() => import('@/tabviews/subtabtable'))
 const { TabPane } = Tabs
 
 export default class NormalTable extends Component {
@@ -32,7 +34,6 @@
     searchlist: null,
     actions: null,
     columns: null,
-    tabviews: null,
     arr_field: '',
     setting: null,
     data: null,
@@ -144,7 +145,6 @@
         searchlist: config.search,
         actions: _actions,
         columns: _columns,
-        tabviews: config.tabs,
         isLinkMain: _tab.length > 0,
         arr_field: _arrField.join(','),
         search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
@@ -552,7 +552,7 @@
   }
 
   render() {
-    const { setting, searchlist, actions, columns, loadingview, viewlost, tabviews, setsingle, pickup, isLinkMain } = this.state
+    const { setting, searchlist, actions, columns, loadingview, viewlost, setsingle, pickup, isLinkMain, config } = this.state
 
     return (
       <div className="commontable" id={'commontable' + this.props.MenuID}>
@@ -598,34 +598,28 @@
             />
           </div> : null
         }
-        {setting && setting.onload !== 'false' && setting.tabshow !== 'vertical' && tabviews && tabviews.length > 0 && 
-          <Tabs defaultActiveKey="0" tabPosition="top">
-            {tabviews.map((_tab, index) => {
-              return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
-                <TabPane tab={
-                  <span>
-                    {_tab.icon ? <Icon type={_tab.icon} /> : null}
-                    {_tab.label}
-                  </span>
-                } key={`${index}`}>
-                  {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
-                </TabPane> : null
-            })}
-          </Tabs>
-        }
-        {setting && setting.onload !== 'false' && setting.tabshow === 'vertical' && tabviews && tabviews.length > 0 && 
-          tabviews.map((_tab, index) => {
-            return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
-              <Card bordered={false} title={
-                <span>
-                  {_tab.icon ? <Icon type={_tab.icon} /> : null}
-                  {_tab.label}
-                </span>
-              } key={`${index}`}>
-                {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
-              </Card> : null
+        {setting && setting.onload !== 'false' &&
+          config.tabgroups.map(group => {
+            if (config[group].length === 0) return null
+
+            return (
+              <Tabs defaultActiveKey="0" tabPosition="top" key={group}>
+                {config[group].map((_tab, index) => {
+                  return !_tab.supMenu || (_tab.supMenu && this.state.BIDs[_tab.supMenu]) ?
+                    <TabPane tab={
+                      <span>
+                        {_tab.icon ? <Icon type={_tab.icon} /> : null}
+                        {_tab.label}
+                      </span>
+                    } key={`${index}`}>
+                      {_tab.type === 'SubTable' ? <SubTable SupMenuID={this.props.MenuID} MenuID={_tab.linkTab} Tab={_tab} BID={this.state.BIDs[_tab.supMenu] || ''} /> : null}
+                    </TabPane> : null
+                })}
+              </Tabs>
+            )
           })
         }
+        {/* {<SubTabTable SupMenuID={this.props.SupMenuID} MenuID={this.props.MenuID} Tab={this.props.Tab} BID={this.props.BID} />} */}
         <BackTop>
           <div className="ant-back-top">
             <div className="ant-back-top-content">
diff --git a/src/tabviews/commontable/index.scss b/src/tabviews/commontable/index.scss
index ba6db0a..cd027d0 100644
--- a/src/tabviews/commontable/index.scss
+++ b/src/tabviews/commontable/index.scss
@@ -63,6 +63,9 @@
       margin-bottom: 5px;
     }
   }
+  .ant-tabs + .ant-tabs {
+    margin-top: 20px;
+  }
 }
 .ant-back-top {
   bottom: 30px;
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index f8eda17..3a12c3a 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -7,11 +7,14 @@
 import SubTable from './subTable'
 import SubAction from '@/tabviews/tableshare/actionList'
 import SubSearch from '@/tabviews/tableshare/topSearch'
+// import asyncComponent from '@/utils/asyncLoadComponent'
 import NotFount from '@/components/404'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
+
+// const SubTabTable = asyncComponent(() => import('@/tabviews/subtabtable'))
 
 export default class NormalTable extends Component {
   static propTpyes = {
@@ -532,6 +535,7 @@
             buttonTrigger={this.buttonTrigger}
           />
         }
+        {/* {<SubTabTable SupMenuID={this.props.SupMenuID} MenuID={this.props.MenuID} Tab={this.props.Tab} BID={this.props.BID} />} */}
         {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
       </div>
     )
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
new file mode 100644
index 0000000..4a81988
--- /dev/null
+++ b/src/tabviews/subtabtable/index.jsx
@@ -0,0 +1,539 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { notification, Spin} from 'antd'
+import moment from 'moment'
+import Api from '@/api'
+import SubTable from '@/tabviews/subtable/subTable'
+import SubAction from '@/tabviews/tableshare/actionList'
+import SubSearch from '@/tabviews/tableshare/topSearch'
+import NotFount from '@/components/404'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import Utils from '@/utils/utils.js'
+import './index.scss'
+
+export default class NormalTable extends Component {
+  static propTpyes = {
+    Tab: PropTypes.object,       // 鏍囩淇℃伅
+    BID: PropTypes.string,       // 涓婄骇鏁版嵁ID
+    MenuID: PropTypes.string,    // 鑿滃崟Id
+    SupMenuID: PropTypes.string  // 涓婄骇鑿滃崟Id
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    loadingview: true,    // 椤甸潰鍔犺浇涓�
+    viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+    lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
+    config: {},
+    searchlist: null,
+    actions: null,
+    columns: null,
+    arr_field: '',
+    setting: null,
+    data: [],
+    total: 0,
+    loading: false,
+    pageIndex: 1,
+    pageSize: 10,
+    orderColumn: '',
+    orderType: 'asc',
+    search: '',
+    configMap: {}
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (this.props.Tab.supMenu && !is(fromJS(this.props.BID), fromJS(nextProps.BID))) {
+      this.loadmaindata()
+    }
+  }
+
+  /**
+   * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
+   */
+  async loadconfig () {
+    let param = {
+      func: 'sPC_Get_LongParam',
+      MenuID: this.props.MenuID
+    }
+    let result = await Api.getSystemCacheConfig(param)
+    if (result.status) {
+      let config = ''
+
+      try { // 閰嶇疆淇℃伅瑙f瀽
+        config = window.decodeURIComponent(window.atob(result.LongParam))
+        config = JSON.parse(config)
+      } catch (e) {
+        config = ''
+      }
+
+      // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
+      if (!config) {
+        this.setState({
+          loadingview: false,
+          viewlost: true
+        })
+        return
+      }
+
+      // 椤甸潰鏈惎鐢ㄦ椂锛屾樉绀烘湭鍚敤椤甸潰
+      if (!config.enabled) {
+        this.setState({
+          loadingview: false,
+          viewlost: true,
+          lostmsg: this.state.dict['main.view.unenabled']
+        })
+        return
+      }
+
+      let _arrField = []     // 瀛楁闆�
+      let _columns = []      // 鏄剧ず鍒�
+      let _hideCol = []      // 闅愯棌鍙婂悎骞跺垪涓瓧娈电殑uuid闆�
+      let colMap = new Map()
+
+      // 1銆佺瓫閫夊瓧娈甸泦锛�2銆佽繃婊ら殣钘忓垪鍙婂悎骞跺垪涓殑瀛楁uuid
+      config.columns.forEach(col => {
+        if (col.field) {
+          _arrField.push(col.field)
+        }
+        if (col.type === 'colspan' && col.sublist) { // 绛涢�夐殣钘忓垪
+          _hideCol = _hideCol.concat(col.sublist)
+        } else if (col.Hide === 'true') {
+          _hideCol.push(col.uuid)
+        }
+        colMap.set(col.uuid, col)
+      })
+
+      // 鐢熸垚鏄剧ず鍒楋紝澶勭悊鍚堝苟鍒椾腑鐨勫瓧娈�
+      config.columns.forEach(col => {
+        if (_hideCol.includes(col.uuid)) return
+
+        if (col.type === 'colspan' && col.sublist) {
+          let _col = JSON.parse(JSON.stringify(col))
+          let subColumn = []
+          _col.sublist.forEach(sub => {
+            if (colMap.has(sub)) {
+              subColumn.push(colMap.get(sub))
+            }
+          })
+          _col.subColumn = subColumn
+          _columns.push(_col)
+        } else {
+          _columns.push(col)
+        }
+      })
+
+      // 娣诲姞鎿嶄綔鍒楋紙瀛樺湪鏃讹級锛堟湭缁忚繃鏉冮檺杩囨护锛�
+      if (config.gridBtn && config.gridBtn.display) {
+        _columns.push({
+          ...config.gridBtn,
+          operations: config.action.filter(item => item.position === 'grid')
+        })
+      }
+
+      // 杩囨护宸ュ叿鏍忔寜閽紙鏈粡杩囨潈闄愯繃婊わ級
+      let _actions = config.action.filter(item => item.position === 'toolbar')
+
+      this.setState({
+        loadingview: false,
+        config: config,
+        setting: config.setting,
+        searchlist: config.search,
+        actions: _actions,
+        columns: _columns,
+        arr_field: _arrField.join(','),
+        search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+        loading: true
+      }, () => {
+        this.improveSearch()
+        this.loadmaindata()
+      })
+    } else {
+      this.setState({
+        loadingview: false,
+        viewlost: true
+      })
+      notification.warning({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  improveSearch = () => {
+    let searchlist = JSON.parse(JSON.stringify(this.state.searchlist))
+    let deffers = []
+    searchlist.forEach(item => {
+      if (item.type !== 'select' && item.type !== 'link') return
+      if (item.setAll === 'true') {
+        item.options.unshift({
+          key: Utils.getuuid(),
+          Value: '',
+          Text: this.state.dict['main.all']
+        })
+      }
+
+      if (item.resourceType === '1' && item.dataSource) {
+        let arrfield = item.valueField + ',' + item.valueText
+        if (item.type === 'link') {
+          arrfield = arrfield + ',' + item.linkField
+        }
+        let param = {
+          func: 'sPC_Get_SelectedList',
+          LText: item.dataSourceSql,
+          obj_name: 'data',
+          arr_field: arrfield
+        }
+
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+        let defer = new Promise(resolve => {
+          Api.getSystemCacheConfig(param).then(res => {
+            res.search = item
+            resolve(res)
+          })
+        })
+        deffers.push(defer)
+      } else if (item.resourceType === '1' && !item.dataSource) {
+        notification.warning({
+          top: 92,
+          message: item.label + ': ' + this.state.dict['main.datasource.settingerror'],
+          duration: 10
+        })
+      }
+    })
+
+    this.setState({searchlist: JSON.parse(JSON.stringify(searchlist))})
+
+    if (deffers.length === 0) return
+
+    Promise.all(deffers).then(result => {
+      result.forEach(res => {
+        if (res.status) {
+          searchlist = searchlist.map(item => {
+            if (item.uuid === res.search.uuid) {
+              res.data.forEach(cell => {
+                item.options.push({
+                  key: Utils.getuuid(),
+                  Value: cell[res.search.valueField],
+                  Text: cell[res.search.valueText]
+                })
+              })
+            }
+            return item
+          })
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.search.label + ':' + res.message,
+            duration: 10
+          })
+        }
+      })
+      this.setState({searchlist})
+    })
+  }
+
+
+  async loadmaindata () {
+    const { setting } = this.state
+    let param = ''
+
+    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
+      param = this.getCustomParam()
+    } else {
+      param = this.getDefaultParam()
+    }
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      this.setState({
+        data: result.data.map((item, index) => {
+          item.key = index
+          return item
+        }),
+        total: result.total,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 15
+      })
+    }
+  }
+
+  getCustomParam = () => {
+    const { pageIndex, pageSize, orderColumn, orderType, search, setting } = this.state
+
+    let _search = Utils.formatCustomMainSearch(search)
+
+    let param = {
+      PageIndex: pageIndex,
+      PageSize: pageSize,
+      OrderCol: orderColumn,
+      OrderType: orderType,
+      ..._search
+    }
+
+    if (setting.interType === 'inner') {
+      param.func = setting.innerFunc
+    } else {
+      param.rduri = setting.interface
+      if (setting.outerFunc) {
+        param.func = setting.outerFunc
+      }
+    }
+
+    return param
+  }
+
+  getDefaultParam = () => {
+    const { arr_field, pageIndex, pageSize, orderColumn, orderType, search, setting } = this.state
+
+    let _search = Utils.joinMainSearchkey(search)
+    _search = _search ? 'where ' + _search : ''
+
+    let param = {
+      func: 'sPC_Get_TableData',
+      obj_name: 'data',
+      arr_field: arr_field,
+      BID: this.props.BID
+    }
+
+    let orderBy = orderColumn ? (orderColumn + ' ' + orderType) : setting.order
+    let _dataresource = setting.dataresource
+
+    if (/\s/.test(_dataresource)) {
+      _dataresource = '(' + _dataresource + ') tb'
+    }
+
+    let LText = `select top ${pageSize} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable where rows > ${pageSize * (pageIndex - 1)} order by tmptable.rows`
+    let DateCount = `select count(1) as total from ${_dataresource} ${_search}`
+
+    param.LText = Utils.formatOptions(LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+    param.DateCount = Utils.formatOptions(DateCount)
+
+    return param
+  }
+
+  refreshbysearch = (searches) => {
+    // 鎼滅储鏉′欢鍙樺寲
+    this.refs.subTable.resetTable()
+
+    this.setState({
+      loading: true,
+      pageIndex: 1,
+      search: searches
+    }, () => {
+      this.loadmaindata()
+    })
+  }
+
+  refreshbytable = (pagination, filters, sorter) => {
+    // 琛ㄦ牸鏌ヨ鏉′欢淇敼
+    if (sorter.order) {
+      let _chg = {
+        ascend: 'asc',
+        descend: 'desc'
+      }
+      sorter.order = _chg[sorter.order]
+    }
+
+    this.setState({
+      loading: true,
+      pageIndex: pagination.current,
+      pageSize: pagination.pageSize,
+      orderColumn: sorter.field || this.state.setting.orderColumn,
+      orderType: sorter.order || 'asc'
+    }, () => {
+      this.loadmaindata()
+    })
+  }
+
+  reloadtable = () => {
+    this.refs.subTable.resetTable()
+    this.setState({
+      loading: true,
+      pageIndex: 1
+    }, () => {
+      this.loadmaindata()
+    })
+  }
+
+  reloadview = () => {
+    this.setState({
+      loadingview: true,    // 椤甸潰鍔犺浇涓�
+      viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+      lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
+      config: {},
+      searchlist: null,
+      actions: null,
+      columns: null,
+      arr_field: '',
+      setting: null,
+      data: null,
+      total: 0,
+      loading: false,
+      pageIndex: 1,
+      pageSize: 10,
+      orderColumn: '',
+      orderType: 'asc',
+      search: '',
+      configMap: {}
+    }, () => {
+      this.loadconfig()
+    })
+  }
+
+  refreshbyaction = (btn, type) => {
+    // 鎸夐挳鎿嶄綔鍚庡埛鏂拌〃鏍�,閲嶇疆椤电爜鍙婇�夋嫨椤�
+    if (btn.execSuccess === 'grid' && type === 'success') {
+      this.reloadtable()
+    } else if (btn.execError === 'grid' && type === 'error') {
+      this.reloadview()
+    } else if (btn.execSuccess === 'view' && type === 'success') {
+      this.reloadtable()
+    } else if (btn.execError === 'view' && type === 'error') {
+      this.reloadview()
+    } else if (type === 'excelOut') {
+      this.handleDefaultExcelout(btn)
+    }
+  }
+
+  handleDefaultExcelout = (btn) => {
+    const { MenuName } = this.props
+    const { arr_field, orderColumn, orderType, search, setting, config } = this.state
+
+    let _arr_labels = []      // 鍒楀悕绉伴泦
+    let _arr_label_field = [] // 鍒楀悕绉板瓧娈甸泦
+
+    config.columns.forEach(col => {
+      if (col.field) {
+        _arr_labels.push(col.label)
+        _arr_label_field.push(`${col.field} as ${col.label}`)
+      }
+    })
+
+    _arr_labels = _arr_labels.join(',')
+    _arr_label_field = _arr_label_field.join(',')
+
+    let _search = Utils.joinMainSearchkey(search)
+    _search = _search ? 'where (' + _search + ')' : ''
+    // 鑾峰彇鍒楄〃鏁版嵁
+    let param = {
+      func: 'sPC_Get_TableData',
+      obj_name: 'data',
+      arr_field: _arr_labels
+    }
+
+    let orderBy = orderColumn ? (orderColumn + ' ' + orderType) : setting.order
+    let _dataresource = setting.dataresource
+
+    if (/\s/.test(_dataresource)) {
+      _dataresource = '(' + _dataresource + ') tb'
+    }
+
+    let LText = `select ${_arr_label_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows`
+
+    param.LText = Utils.formatOptions(LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+    param.DateCount = ''
+
+    let name = `${MenuName}${moment().format('YYYYMMDDHHmmss')}.xlsx`
+
+    Api.getExcelOut(param, name).then(res => {
+      if (res && res.status === false) {
+        this.refs.subButton.execError(res, btn)
+      } else {
+        this.refs.subButton.execSuccess(btn)
+      }
+    })
+  }
+
+  gettableselected = () => {
+    // 鑾峰彇琛ㄦ牸閫夋嫨椤�
+    let data = []
+    this.refs.subTable.state.selectedRowKeys.forEach(item => {
+      data.push(this.refs.subTable.props.data[item])
+    })
+    return data
+  }
+
+  buttonTrigger = (btn, record) => {
+    this.refs.subButton.actionTrigger(btn, record)
+  }
+
+  UNSAFE_componentWillMount () {
+    // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
+    this.loadconfig()
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = (state, callback) => {
+      return
+    }
+  }
+
+  render() {
+    const { setting, searchlist, actions, columns, loadingview, viewlost } = this.state
+
+    return (
+      <div className="subtable" id={'subtable' + this.props.MenuID}>
+        {loadingview && <Spin size="large" />}
+        {searchlist && searchlist.length > 0 ?
+          <SubSearch
+            refreshdata={this.refreshbysearch}
+            searchlist={searchlist}
+            dict={this.state.dict}
+          /> : null
+        }
+        {actions &&
+          <SubAction
+            ref="subButton"
+            type="sub"
+            setting={setting}
+            actions={actions}
+            BID={this.props.BID}
+            dict={this.state.dict}
+            MenuID={this.props.SupMenuID}
+            refreshdata={this.refreshbyaction}
+            gettableselected={this.gettableselected}
+          />
+        }
+        {columns &&
+          <SubTable
+            ref="subTable"
+            dict={this.state.dict}
+            MenuID={this.props.MenuID}
+            setting={setting}
+            columns={columns}
+            data={this.state.data}
+            total={this.state.total}
+            loading={this.state.loading}
+            refreshdata={this.refreshbytable}
+            buttonTrigger={this.buttonTrigger}
+          />
+        }
+        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
+      </div>
+    )
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/subtabtable/index.scss b/src/tabviews/subtabtable/index.scss
new file mode 100644
index 0000000..2bf4162
--- /dev/null
+++ b/src/tabviews/subtabtable/index.scss
@@ -0,0 +1,36 @@
+.subtable {
+  position: relative;
+  min-height: 200px;
+  padding-top: 16px;
+  > .top-search {
+    padding: 0 0px 20px;
+  }
+  > .button-list {
+    padding: 10px 0px 5px;
+  }
+  .box404 {
+    padding-top: 30px;
+  }
+  .ant-modal-mask {
+    position: absolute;
+  }
+  .ant-modal-wrap {
+    position: absolute;
+  }
+  .action-modal .ant-modal {
+    top: 40px;
+    max-width: 95%;
+    .ant-modal-body {
+      max-height: calc(100vh - 265px);
+    }
+  }
+  > .ant-spin {
+    position: absolute;
+    left: calc(50% - 22px);
+    top: 100px;
+  }
+}
+.ant-back-top {
+  bottom: 30px;
+  right: 30px;
+}
\ No newline at end of file
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index 58cba79..3506b6b 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -109,6 +109,8 @@
     }
 
     _config.tabs = _config.tabs || []
+    _config.tabgroups = _config.tabgroups || ['tabs']
+    _config.setting.subtabs = _config.setting.subtabs || []
 
     this.setState({
       originActions: _oriActions,
@@ -290,30 +292,49 @@
   handleList = (type, list, card) => {
     const { config } = this.state
 
-    if (list.length > config[type].length) {
-      list = list.filter(item => !item.origin)
-
-      this.setState({
-        [type + 'loading']: true,
-        config: {...config, [type]: list }
-      }, () => {
-        // 鍒锋柊瀵瑰簲鐨勯厤缃俊鎭�
+    if (type === 'tabs') { // 鏍囩椤佃皟鏁撮『搴忔垨娣诲姞鍏冪礌
+      if (list.length > config[card.groupId].length) {
+        list = list.filter(item => !item.origin)
+  
         this.setState({
-          [type + 'loading']: false
-        })
-
-        if (type === 'search') {
-          this.handleSearch(card)
-        } else if (type === 'action') {
-          this.handleAction(card)
-        } else if (type === 'columns') {
-          this.handleColumn(card)
-        } else if (type === 'tabs') {
+          tabloading: true,
+          config: {...config, [card.groupId]: list }
+        }, () => {
+          // 鍒锋柊瀵瑰簲鐨勯厤缃俊鎭�
+          this.setState({
+            tabloading: false
+          })
           this.handleTab(card)
-        }
-      })
+        })
+      } else {
+        this.setState({config: {...config, [card.groupId]: list}})
+      }
     } else {
-      this.setState({config: {...config, [type]: list}})
+      if (list.length > config[type].length) {
+        list = list.filter(item => !item.origin)
+  
+        this.setState({
+          [type + 'loading']: true,
+          config: {...config, [type]: list }
+        }, () => {
+          // 鍒锋柊瀵瑰簲鐨勯厤缃俊鎭�
+          this.setState({
+            [type + 'loading']: false
+          })
+  
+          if (type === 'search') {
+            this.handleSearch(card)
+          } else if (type === 'action') {
+            this.handleAction(card)
+          } else if (type === 'columns') {
+            this.handleColumn(card)
+          } else if (type === 'tabs') {
+            this.handleTab(card)
+          }
+        })
+      } else {
+        this.setState({config: {...config, [type]: list}})
+      }
     }
   }
 
@@ -531,23 +552,23 @@
             MenuID: 'pop',
             text: this.state.dict['header.form.popform']
           }, {
-            MenuID: 'popview',
-            text: this.state.dict['header.form.popview']
-          }, {
             MenuID: 'prompt',
             text: this.state.dict['header.form.prompt']
           }, {
             MenuID: 'exec',
             text: this.state.dict['header.form.exec']
           }, {
-            MenuID: 'tab',
-            text: this.state.dict['header.form.tab']
-          }, {
             MenuID: 'excelIn',
             text: this.state.dict['header.form.excelIn']
           }, {
             MenuID: 'excelOut',
             text: this.state.dict['header.form.excelOut']
+          }, {
+            MenuID: 'popview',
+            text: this.state.dict['header.form.popview']
+          }, {
+            MenuID: 'tab',
+            text: this.state.dict['header.form.tab']
           }, {
             MenuID: 'blank',
             text: this.state.dict['header.form.blank']
@@ -788,11 +809,11 @@
               MenuID: 'text',
               text: this.state.dict['header.form.text']
             }, {
-              MenuID: 'picture',
-              text: this.state.dict['header.form.picture']
-            }, {
               MenuID: 'number',
               text: this.state.dict['header.form.number']
+            }, {
+              MenuID: 'picture',
+              text: this.state.dict['header.form.picture']
             }, {
               MenuID: 'textarea',
               text: this.state.dict['header.form.textarea']
@@ -964,6 +985,7 @@
       this.setState({
         visible: true,
         formtemp: 'columns',
+        modalTitle: '缂栬緫-鍚堝苟鍒�',
         card: card
       })
     }
@@ -972,24 +994,42 @@
   handleTab = (card) => {
     const { config } = this.state
 
-    let menus = []
-
-    config.tabs.forEach(item => {
-      if (item.origin || card.uuid === item.uuid) return
-
-      let menu = {
-        value: item.uuid,
-        text: item.label
+    let index = 0 // 绛涢�変笅涓�缁�
+    config.tabgroups.forEach((groupId, i) => {
+      if (groupId === card.groupId) {
+        index = i
       }
-
-      menus.push(menu)
     })
 
-    if (card.supMenu && card.supMenu !== 'mainTable') {
-      let _menu = menus.filter(item => item.value === card.supMenu)[0]
-      if (!_menu) {
-        card.supMenu = ''
-      }
+    let menus = []
+    let subtabs = card.subtabs || []
+    let nextTabId = config.tabgroups[index + 1]
+
+    if (nextTabId) {
+      let _tabMap = new Map()
+      let _usedTabMap = new Map()
+
+      config[nextTabId].forEach(tab => { // 涓嬬骇鎵�鏈夌殑鏍囩
+        menus.push(tab)
+        _tabMap.set(tab.uuid, true)
+      })
+
+      config[card.groupId].forEach(tab => { // 鍚岀骇鏍囩宸查�夌殑涓嬬骇鏍囩
+        if (tab.uuid === card.uuid) return
+
+        tab.subtabs.forEach(subtab => {
+          _usedTabMap.set(subtab, true)
+        })
+      })
+      console.log(config)
+      config.setting.subtabs.forEach(subtab => { // 涓昏〃宸查�夌殑涓嬬骇鏍囩
+        _usedTabMap.set(subtab, true)
+      })
+
+      subtabs = subtabs.filter(tab => _tabMap.has(tab.uuid) && !_usedTabMap.has(tab.uuid))
+      menus = menus.filter(tab => !_usedTabMap.has(tab.uuid))
+    } else {
+      subtabs = []
     }
 
     this.setState({
@@ -1048,21 +1088,12 @@
           }]
         },
         {
-          type: 'select',
-          key: 'supMenu',
-          label: '鍏宠仈鑿滃崟',
-          initVal: card.supMenu || '',
+          type: 'mutilselect',
+          key: 'subtabs',
+          label: '涓嬬骇鏍囩',
+          initVal: subtabs,
           required: false,
-          options: [
-            {
-              value: '',
-              text: this.state.dict['header.form.empty']
-            }, {
-              value: 'mainTable',
-              text: '涓昏〃'
-            },
-            ...menus
-          ]
+          options: menus
         }
       ]
     })
@@ -1134,19 +1165,36 @@
           })
         }
   
-        _config[res.type] = _config[res.type].map(item => {
-          if (item.uuid === res.values.uuid) {
-            isupdate = true
-            return res.values
-          } else {
-            return item
+        if (res.type !== 'tabs') {
+          _config[res.type] = _config[res.type].map(item => {
+            if (item.uuid === res.values.uuid) {
+              isupdate = true
+              return res.values
+            } else {
+              return item
+            }
+          })
+          _config[res.type] = _config[res.type].filter(item => !item.origin)
+    
+          if (!isupdate) { // 鎿嶄綔涓嶆槸淇敼锛屾坊鍔犲厓绱犺嚦鍒楄〃
+            _config[res.type].push(res.values)
           }
-        })
-        _config[res.type] = _config[res.type].filter(item => !item.origin)
-  
-        if (!isupdate) { // 鎿嶄綔涓嶆槸淇敼锛屾坊鍔犲厓绱犺嚦鍒楄〃
-          _config[res.type].push(res.values)
+        } else { // 鏍囩椤电殑娣诲姞涓庝慨鏀�
+          _config[res.values.groupId] = _config[res.values.groupId].map(item => {
+            if (item.uuid === res.values.uuid) {
+              isupdate = true
+              return res.values
+            } else {
+              return item
+            }
+          })
+          _config[res.values.groupId] = _config[res.values.groupId].filter(item => !item.origin)
+    
+          if (!isupdate) { // 鎿嶄綔涓嶆槸淇敼锛屾坊鍔犲厓绱犺嚦鍒楄〃
+            _config[res.values.groupId].push(res.values)
+          }
         }
+        
   
         if (res.type === 'action') {
           let gridbtn = _config.action.filter(act => act.position === 'grid')
@@ -1172,7 +1220,7 @@
             }
           }
         }
-  
+
         this.setState({
           config: _config,
           searchloading: true,
@@ -1833,6 +1881,7 @@
       
       let tabParam = { // 娣诲姞鑿滃崟tab椤�
         func: 'sPC_sMenusTab_AddUpt',
+        MenuID: menu.MenuID,
         LText: config.tabs.map((item, index) => {
           return `select '${menu.MenuID}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${(index + 1) * 10}' as Sort`
         })
@@ -2567,6 +2616,59 @@
     })
   }
 
+  addTabGroup = () => {
+    let _this = this
+    let _config = JSON.parse(JSON.stringify(this.state.config))
+
+    confirm({
+      content: `纭畾鏂板缓鏍囩缁勫悧锛焋,
+      okText: this.state.dict['header.confirm'],
+      cancelText: this.state.dict['header.cancel'],
+      onOk() {
+        let newgroup = 'tabs' + Utils.getuuid()
+
+        _config.tabgroups.push(newgroup)
+        _config[newgroup] = []
+
+        _this.setState({
+          config: _config,
+          tabloading: true
+        }, () => {
+          _this.setState({
+            tabloading: false
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  delTabGroup = (groupId) => {
+    let _this = this
+    let _config = JSON.parse(JSON.stringify(this.state.config))
+
+    confirm({
+      content: `纭畾鍒犻櫎鏍囩缁勫悧锛焋,
+      okText: this.state.dict['header.confirm'],
+      cancelText: this.state.dict['header.cancel'],
+      onOk() {
+
+        _config.tabgroups = _config.tabgroups.filter(group => group !== groupId)
+        delete _config[groupId]
+
+        _this.setState({
+          config: _config,
+          tabloading: true
+        }, () => {
+          _this.setState({
+            tabloading: false
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+
   render () {
     const configAction = this.state.config.action.filter(_action =>
       !_action.origin && (_action.OpenType === 'pop' || _action.OpenType === 'popview' || _action.OpenType === 'blank' || _action.OpenType === 'tab')
@@ -2740,6 +2842,7 @@
                   /> : null
                 }
               </div>
+              {/* 鏄剧ず鍒� */}
               <div className="column-list">
                 <Tooltip placement="bottomLeft" overlayClassName="middle" title="鍦ㄥ乏渚у伐鍏锋爮銆婃樉绀哄垪銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬樉绀哄垪鎷栬嚦姝ゅ娣诲姞锛涙垨鐐瑰嚮銆婃坊鍔犳樉绀哄垪銆嬫寜閽壒閲忔坊鍔狅紝閫夋嫨鎵归噺娣诲姞鏃讹紝闇�鎻愬墠閫夋嫨浣跨敤琛ㄣ�傛敞锛氭坊鍔犲悎骞跺垪鏃讹紝闇�璁剧疆鍙�夊垪銆�">
                   <Icon type="question-circle" />
@@ -2760,22 +2863,26 @@
                   /> : null
                 }
               </div>
-              <div className="tab-list">
-                <Tooltip placement="bottomLeft" overlayClassName="middle" title="鍦ㄥ乏渚у伐鍏锋爮銆婃爣绛鹃〉銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬爣绛鹃〉鎷栬嚦姝ゅ娣诲姞銆�">
-                  <Icon type="question-circle" />
-                </Tooltip>
-                {!this.state.tabloading ?
-                  <TabDragElement
-                    type="tabs"
-                    list={this.state.config.tabs}
-                    setting={this.state.config.setting}
-                    handleList={this.handleList}
-                    handleMenu={this.handleTab}
-                    deleteMenu={this.deleteElement}
-                    placeholder={this.state.dict['header.form.tab.placeholder']}
-                  /> : null
-                }
-              </div>
+              {/* 鏍囩缁� */}
+              {!this.state.tabloading && this.state.config.tabgroups.map((groupId, index) => {
+                return (
+                  <div key={index} className="tab-list">
+                    {index === 0 ? <Tooltip placement="bottomLeft" overlayClassName="middle" title="鍦ㄥ乏渚у伐鍏锋爮銆婃爣绛鹃〉銆嬩腑锛岄�夋嫨瀵瑰簲绫诲瀷鐨勬爣绛鹃〉鎷栬嚦姝ゅ娣诲姞銆�">
+                      <Icon type="question-circle" />
+                    </Tooltip> : null}
+                    {index === 0 ? <Icon type="plus" onClick={this.addTabGroup} /> : null}
+                    {index !== 0 ? <Icon type="delete" onClick={() => {this.delTabGroup(groupId)}} /> : null}
+                    <TabDragElement
+                      type="tabs"
+                      groupId={groupId}
+                      list={this.state.config[groupId]}
+                      handleList={this.handleList}
+                      handleMenu={this.handleTab}
+                      deleteMenu={this.deleteElement}
+                      placeholder={this.state.dict['header.form.tab.placeholder']}
+                    />
+                  </div>)
+              })}
             </Card>
           </div>
         </DndProvider>
@@ -2888,6 +2995,7 @@
           <SettingForm
             dict={this.state.dict}
             menu={this.props.menu}
+            config={this.state.config}
             data={this.state.config.setting}
             columns={this.state.config.columns}
             usefulFields={this.props.permFuncField}
diff --git a/src/templates/comtableconfig/index.scss b/src/templates/comtableconfig/index.scss
index d6730ff..3cd5e5a 100644
--- a/src/templates/comtableconfig/index.scss
+++ b/src/templates/comtableconfig/index.scss
@@ -126,7 +126,7 @@
     position: relative;
     width: calc(100vw - 235px);
     height: 100%;
-    overflow-y: hidden;
+    // overflow-y: hidden;
     background: #ffffff;
     .ant-switch.big {
       min-width: 60px;
@@ -156,7 +156,7 @@
     }
     .ant-card-body {
       position: relative;
-      padding: 0;
+      padding: 0 0 40px;
 
       .search-list {
         padding: 1px 24px 20px;
@@ -409,18 +409,38 @@
             }
           }
         }
-        > .anticon-setting {
+        > .anticon-question-circle {
           position: absolute;
-          font-size: 18px;
-          right: 15px;
-          top: 30px;
+          left: 5px;
+          top: 20px;
+        }
+        > .anticon-plus {
+          position: absolute;
+          font-size: 24px;
+          right: 25px;
+          top: 50px;
+          z-index: 1;
+          cursor: pointer;
+        }
+        > .anticon-delete {
+          position: absolute;
+          font-size: 24px;
+          right: 25px;
+          top: 50px;
+          z-index: 1;
+          cursor: pointer;
+        }
+        .ant-tabs-nav-container-scrolling {
+          margin-right: 50px;
         }
       }
       > .anticon-setting {
         position: absolute;
         font-size: 18px;
-        right: 15px;
+        right: 5px;
         top: 10px;
+        padding: 10px;
+        z-index: 1;
       }
     }
     .anticon-question-circle {
@@ -430,8 +450,8 @@
       top: 5px;
     }
   }
-  .setting:hover {
-    overflow-y: auto;
+  .setting {
+    overflow-y: scroll;
   }
   .setting::-webkit-scrollbar {
     width: 7px;
@@ -440,6 +460,7 @@
     border-radius: 5px;
     box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
     background: rgba(0, 0, 0, 0.13);
+    display: none;
   }
   .setting::-webkit-scrollbar-track {
     box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
@@ -447,6 +468,9 @@
     border: 1px solid rgba(0, 0, 0, 0.07);
     background: rgba(0, 0, 0, 0);
   }
+  .setting:hover::-webkit-scrollbar-thumb {
+    display: block;
+  }
   .ant-spin {
     position: absolute;
     margin-left: calc(50vw - 22px);
diff --git a/src/templates/comtableconfig/settingform/index.jsx b/src/templates/comtableconfig/settingform/index.jsx
index 8833c31..5da31f1 100644
--- a/src/templates/comtableconfig/settingform/index.jsx
+++ b/src/templates/comtableconfig/settingform/index.jsx
@@ -10,13 +10,43 @@
     dict: PropTypes.object, // 瀛楀吀椤�
     menu: PropTypes.object,
     data: PropTypes.object,
+    config: PropTypes.object,
     columns: PropTypes.array,
     usefulFields: PropTypes.array
   }
 
   state = {
     interType: this.props.data.interType || 'inner',
-    columns: this.props.columns.filter(item => item.field && item.type !== 'colspan')
+    columns: this.props.columns.filter(item => item.field && item.type !== 'colspan'),
+    currentTabs: null,
+    selectTabs: []
+  }
+
+  UNSAFE_componentWillMount() {
+    const { config, data } = this.props
+    let _tabs = []
+    let _select = []
+    let _tabMap = new Map()
+
+    config.tabgroups.forEach(groupname => {
+      config[groupname].forEach(tab => {
+        if (tab.origin) return
+
+        _tabs.push(tab)
+        _tabMap.set(tab.uuid, true)
+      })
+    })
+
+    data.subtabs && data.subtabs.forEach(tabId => {
+      if (_tabMap.has(tabId)) {
+        _select.push(tabId)
+      }
+    })
+
+    this.setState({
+      currentTabs: _tabs,
+      selectTabs: _select
+    })
   }
 
   handleConfirm = () => {
@@ -52,7 +82,7 @@
   render() {
     const { data, dict, menu, usefulFields } = this.props
     const { getFieldDecorator } = this.props.form
-    const { interType, columns } = this.state
+    const { interType, columns, selectTabs } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -229,18 +259,6 @@
             </Form.Item>
           </Col>
           <Col span={12}>
-            <Form.Item label="鏍囩">
-              {getFieldDecorator('tabshow', {
-                initialValue: data.tabshow || 'horizontal'
-              })(
-                <Select>
-                  <Select.Option value="horizontal">妯悜鏄剧ず</Select.Option>
-                  <Select.Option value="vertical">绾靛悜鏄剧ず</Select.Option>
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-          <Col span={12}>
             <Form.Item label="鍒濆鍖�">
               {getFieldDecorator('onload', {
                 initialValue: data.onload || 'true'
@@ -252,6 +270,28 @@
               )}
             </Form.Item>
           </Col>
+          <Col span={12}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="涓昏〃鍙�夊彇鍏宠仈鏍囩锛屾爣绛惧叧鑱斿悗锛屼富琛ㄦ暟鎹垏鎹㈡椂锛屼笅绾ф爣绛句細璺熼殢涓昏〃涓婚敭鍊煎彉鍖栥��">
+                <Icon type="question-circle" />
+                {'涓嬬骇鏍囩'}
+              </Tooltip>
+            }>
+              {getFieldDecorator('subtabs', {
+                initialValue: selectTabs
+              })(
+                <Select
+                  mode="multiple"
+                  style={{ width: '100%' }}
+                  placeholder="Please select"
+                >
+                  {this.state.currentTabs.map((option, index) =>
+                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
         </Row>
       </Form>
     )
diff --git a/src/templates/comtableconfig/source.jsx b/src/templates/comtableconfig/source.jsx
index d6f783b..4552f1f 100644
--- a/src/templates/comtableconfig/source.jsx
+++ b/src/templates/comtableconfig/source.jsx
@@ -21,7 +21,7 @@
       interface: '',
       outerFunc: '',
       onload: 'true',
-      tabshow: 'horizontal'
+      subtabs: []
     },
     tables: [],
     search: [
@@ -206,24 +206,29 @@
     },
     tabs: [
       {
-        origin: true,
+        origin: true,          // 鏄惁涓虹ず渚�
+        groupId: 'tabs',
         uuid: Utils.getuuid(),
         label: 'tab1',
         icon: '',
         type: 'SubTable',
         linkTab: '',
+        subtabs: [],
         supMenu: ''
       },
       {
         origin: true,
+        groupId: 'tabs',
         uuid: Utils.getuuid(),
         label: 'tab2',
         icon: '',
         type: 'SubTable',
         linkTab: '',
+        subtabs: [],
         supMenu: ''
       }
-    ]
+    ],
+    tabgroups: ['tabs']
   }
 
   searchItems = [
@@ -274,12 +279,6 @@
     },
     {
       type: 'action',
-      label: CommonDict['header.form.popview'],
-      subType: 'popview',
-      url: ''
-    },
-    {
-      type: 'action',
       label: CommonDict['header.form.prompt'],
       subType: 'prompt',
       url: ''
@@ -292,12 +291,6 @@
     },
     {
       type: 'action',
-      label: CommonDict['header.form.tab'],
-      subType: 'tab',
-      url: ''
-    },
-    {
-      type: 'action',
       label: CommonDict['header.form.excelIn'],
       subType: 'excelIn',
       url: ''
@@ -306,6 +299,18 @@
       type: 'action',
       label: CommonDict['header.form.excelOut'],
       subType: 'excelOut',
+      url: ''
+    },
+    {
+      type: 'action',
+      label: CommonDict['header.form.popview'],
+      subType: 'popview',
+      url: ''
+    },
+    {
+      type: 'action',
+      label: CommonDict['header.form.tab'],
+      subType: 'tab',
       url: ''
     },
     {
@@ -337,14 +342,14 @@
     },
     {
       type: 'columns',
-      label: CommonDict['header.form.picture'],
-      subType: 'picture',
+      label: CommonDict['header.form.number'],
+      subType: 'number',
       url: ''
     },
     {
       type: 'columns',
-      label: CommonDict['header.form.number'],
-      subType: 'number',
+      label: CommonDict['header.form.picture'],
+      subType: 'picture',
       url: ''
     },
     {
diff --git a/src/templates/comtableconfig/tabdragelement/index.jsx b/src/templates/comtableconfig/tabdragelement/index.jsx
index f8c6c37..e81ad5e 100644
--- a/src/templates/comtableconfig/tabdragelement/index.jsx
+++ b/src/templates/comtableconfig/tabdragelement/index.jsx
@@ -12,11 +12,14 @@
   SubTable: subtable
 }
 
-const Container = ({list, type, setting, placeholder, handleList, handleMenu, deleteMenu }) => {
+const Container = ({list, type, groupId, placeholder, handleList, handleMenu, deleteMenu }) => {
   let target = null
   const [cards, setCards] = useState(list)
   const moveCard = (id, atIndex) => {
     const { card, index } = findCard(id)
+
+    if (!card) return
+
     const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
     setCards(_cards)
     handleList(type, _cards)
@@ -48,7 +51,9 @@
       newcard.icon = ''
       newcard.type = item.subType
       newcard.linkTab = Utils.getuuid()
+      newcard.subtabs = []
       newcard.supMenu = ''
+      newcard.groupId = groupId
       
       let targetId = cards.length > 0 ? cards[cards.length - 1].uuid : 0
       if (target) {
diff --git a/src/templates/comtableconfig/tabform/index.jsx b/src/templates/comtableconfig/tabform/index.jsx
index cdf8c4b..1d598b0 100644
--- a/src/templates/comtableconfig/tabform/index.jsx
+++ b/src/templates/comtableconfig/tabform/index.jsx
@@ -1,6 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Select, Icon } from 'antd'
+import { Form, Row, Col, Input, Select, Icon, Tooltip } from 'antd'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
@@ -150,6 +150,31 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'mutilselect') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鏍囩鍙叧鑱斾笅绾ф爣绛撅紝涓嬬骇鏍囩鍙�夎寖鍥达細鐩搁偦鐨勪笅渚ф爣绛剧粍涓湭琚悓绾ф垨涓昏〃鍏宠仈鐨勬爣绛俱��">
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip>
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal
+              })(
+                <Select
+                  mode="multiple"
+                  style={{ width: '100%' }}
+                  placeholder="Please select"
+                >
+                  {item.options.map((option, index) =>
+                    <Select.Option id={option.uuid} title={option.label} key={index} value={option.uuid}>{option.label}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
       }
     })
 
@@ -163,6 +188,7 @@
         if (!err) {
 
           values.uuid = this.props.card.uuid
+          values.groupId = this.props.card.groupId
 
           if (!values.linkTab) { // 娌℃湁鍏宠仈鏍囩锛堟柊寤烘椂锛夛紝鍒涘缓鏂版爣绛綢d
             values.linkTab = Utils.getuuid()
diff --git a/src/templates/comtableconfig/tabform/index.scss b/src/templates/comtableconfig/tabform/index.scss
index d92e41a..930f1a5 100644
--- a/src/templates/comtableconfig/tabform/index.scss
+++ b/src/templates/comtableconfig/tabform/index.scss
@@ -1,3 +1,7 @@
 .ant-advanced-search-form.commontable-tab-form {
   min-height: 180px;
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
 }
\ No newline at end of file
diff --git a/src/templates/subtableconfig/index.scss b/src/templates/subtableconfig/index.scss
index 840a370..b2ebdfc 100644
--- a/src/templates/subtableconfig/index.scss
+++ b/src/templates/subtableconfig/index.scss
@@ -126,7 +126,7 @@
     position: relative;
     width: calc(100vw - 235px);
     height: 100%;
-    overflow-y: hidden;
+    // overflow-y: hidden;
     background: #ffffff;
     .ant-switch.big {
       min-width: 60px;
@@ -425,8 +425,8 @@
       top: 5px;
     }
   }
-  .setting:hover {
-    overflow-y: auto;
+  .setting {
+    overflow-y: scroll;
   }
   .setting::-webkit-scrollbar {
     width: 7px;
@@ -435,6 +435,7 @@
     border-radius: 5px;
     box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
     background: rgba(0, 0, 0, 0.13);
+    display: none;
   }
   .setting::-webkit-scrollbar-track {
     box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
@@ -442,6 +443,9 @@
     border: 1px solid rgba(0, 0, 0, 0.07);
     background: rgba(0, 0, 0, 0);
   }
+  .setting:hover::-webkit-scrollbar-thumb {
+    display: block;
+  }
   .ant-spin {
     position: absolute;
     margin-left: calc(50vw - 22px);
diff --git a/src/templates/tableshare/transferform/index.jsx b/src/templates/tableshare/transferform/index.jsx
index 57e10ad..318ff3f 100644
--- a/src/templates/tableshare/transferform/index.jsx
+++ b/src/templates/tableshare/transferform/index.jsx
@@ -1,6 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Transfer } from 'antd'
+import { Transfer, Icon } from 'antd'
 import './index.scss'
 
 class TransferForm extends Component {
@@ -17,6 +17,9 @@
   }
 
   handleChange = (nextTargetKeys, direction, moveKeys) => {
+    console.log(direction)
+    console.log(moveKeys)
+    console.log(nextTargetKeys)
     this.setState({ targetKeys: nextTargetKeys })
   }
 
@@ -46,6 +49,7 @@
         return {
           key: item.uuid,
           title: item.label,
+          field: item.field,
           description: ''
         }
       }),
@@ -53,8 +57,47 @@
     })
   }
 
+  changeorder = (e, item, type) => {
+    const { targetKeys } = this.state
+    e.stopPropagation()
+
+    let _index = 0
+    let keys = targetKeys.filter((key, i) => {
+      if (key === item.key) {
+        _index = i
+        return false
+      } else {
+        return true
+      }
+    })
+
+    if ((_index === 0 && type === 'up') || (_index === targetKeys.length - 1 && type === 'down')) {
+      return
+    }
+
+    if (type === 'up') {
+      keys.splice(_index - 1, 0, item.key)
+    } else {
+      keys.splice(_index + 1, 0, item.key)
+    }
+
+    this.setState({
+      targetKeys: keys
+    })
+  }
+
+  getItem = (item) => {
+    let content = item.title + '(' + item.field + ')'
+    return <span title={content}>
+      {content}
+      <Icon type="arrow-up" onClick={(e) => {this.changeorder(e, item, 'up')}} />
+      <Icon type="arrow-down" onClick={(e) => {this.changeorder(e, item, 'down')}} />
+    </span>
+  }
+
   render() {
     const { targetKeys, selectedKeys } = this.state
+
     return (
       <div className="common-table-columns-transfer">
         <Transfer
@@ -65,7 +108,7 @@
           selectedKeys={selectedKeys}
           onChange={this.handleChange}
           onSelectChange={this.handleSelectChange}
-          render={item => item.title}
+          render={this.getItem}
         />
       </div>
     )
diff --git a/src/templates/tableshare/transferform/index.scss b/src/templates/tableshare/transferform/index.scss
index 35d2ad5..cedc47b 100644
--- a/src/templates/tableshare/transferform/index.scss
+++ b/src/templates/tableshare/transferform/index.scss
@@ -2,5 +2,26 @@
   padding-left: 18px;
   .ant-transfer-list {
     width: 296px;
+    i {
+      display: none;
+    }
+  }
+  .ant-transfer-operation + .ant-transfer-list {
+    .ant-transfer-list-content-item {
+      margin-right: 50px;
+      i {
+        display: inline-block;
+      }
+      .anticon {
+        position: absolute;
+        right: 10px;
+        color: #ff4d4f;
+      }
+      .anticon-arrow-up {
+        position: absolute;
+        right: 30px;
+        color: #1890ff;
+      }
+    }
   }
 }
\ No newline at end of file

--
Gitblit v1.8.0