From a181fc113d024ed34d6b488c65882961bd1de3f4 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期二, 02 六月 2020 19:00:57 +0800
Subject: [PATCH] 2020-06-02

---
 src/components/tabview/index.jsx                                                      |    7 
 src/tabviews/treepage/index.jsx                                                       |  882 ++++++++++++
 src/tabviews/formtab/index.jsx                                                        |    6 
 src/templates/formtabconfig/index.jsx                                                 |    4 
 src/templates/comtableconfig/index.jsx                                                |    9 
 src/templates/treepageconfig/index.jsx                                                |  921 ++++++++++++
 src/templates/zshare/editcomponent/index.jsx                                          |    4 
 src/locales/zh-CN/model.js                                                            |    1 
 src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.scss |   31 
 src/templates/treepageconfig/source.jsx                                               |   57 
 src/locales/en-US/model.js                                                            |    1 
 src/templates/menuconfig/editthdmenu/index.jsx                                        |   11 
 src/templates/formtabconfig/source.jsx                                                |    2 
 src/tabviews/subtable/index.jsx                                                       |    6 
 src/templates/sharecomponent/tabscomponent/tabform/index.scss                         |   11 
 src/tabviews/commontable/index.jsx                                                    |   75 
 src/templates/treepageconfig/index.scss                                               |  266 +++
 src/templates/sharecomponent/tabscomponent/index.jsx                                  |   49 
 src/templates/sharecomponent/chartgroupcomponent/chartform/index.jsx                  |   12 
 src/utils/utils.js                                                                    |    8 
 src/templates/zshare/formconfig.jsx                                                   |  311 ++++
 src/templates/sharecomponent/tabscomponent/tabform/index.jsx                          |   46 
 src/tabviews/managetable/index.jsx                                                    |    6 
 src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.jsx  |  250 +++
 src/templates/subtableconfig/index.jsx                                                |   12 
 src/tabviews/zshare/cardcomponent/index.scss                                          |    4 
 src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx               |  765 ++++++++++
 src/templates/sharecomponent/treesettingcomponent/index.scss                          |   42 
 src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx               |   51 
 src/tabviews/treepage/index.scss                                                      |  168 ++
 src/tabviews/zshare/cardcomponent/index.jsx                                           |    3 
 src/templates/sharecomponent/treesettingcomponent/index.jsx                           |  181 ++
 src/utils/option.js                                                                   |    7 
 src/templates/sharecomponent/treesettingcomponent/settingform/index.scss              |   45 
 34 files changed, 4,163 insertions(+), 91 deletions(-)

diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index 2637efe..25a6af6 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -18,6 +18,7 @@
 
 const Home = asyncComponent(() => import('@/tabviews/home'))
 const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
+const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
 const VerupTable = asyncComponent(() => import('@/tabviews/verupmanage'))
 const ScriptTable = asyncComponent(() => import('@/tabviews/scriptmanage'))
 const TabManage = asyncComponent(() => import('@/tabviews/tabmanage'))
@@ -102,6 +103,8 @@
       return (<Home MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'CommonTable') {
       return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'TreePage') {
+      return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'VerupTable') {
       return (<VerupTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'ScriptTable') {
@@ -210,7 +213,7 @@
                       className="test"
                       tab={
                         <span className="tab-control">
-                          {['CommonTable', 'FormTab'].includes(view.type) ?
+                          {['CommonTable', 'FormTab', 'TreePage'].includes(view.type) ?
                             <Icon type="redo" onClick={(e) => {this.refreshTabview(e, view)}}/> : null
                           }
                           <span className="tab-name" onClick={(e) => {this.changeTab(e, view)}}>
@@ -224,7 +227,7 @@
                       key={view.MenuID}
                     >
                       {this.selectcomponent(view)}
-                      {view.type !== 'CommonTable' && view.type !== 'ManageTable' ?
+                      {!['CommonTable', 'TreePage', 'ManageTable'].includes(view.type) ?
                         <Button
                           icon="copy"
                           shape="circle"
diff --git a/src/locales/en-US/model.js b/src/locales/en-US/model.js
index fe21365..3004e65 100644
--- a/src/locales/en-US/model.js
+++ b/src/locales/en-US/model.js
@@ -82,6 +82,7 @@
   'header.form.refresh.view': 'Refresh the page',
   'header.form.refresh.grid': 'Refresh the table',
   'header.form.refresh.maingrid': '鍒锋柊涓昏〃',
+  'header.form.refresh.mainline': '鍒锋柊涓昏〃锛堣锛�',
   'header.form.refresh.equaltab': '鍒锋柊鍚岀骇鏍囩',
   'header.form.refresh.subgrid': '鍒锋柊瀛愯〃',
   'header.form.popClose': '鏍囩鍏抽棴',
diff --git a/src/locales/zh-CN/model.js b/src/locales/zh-CN/model.js
index 654bb1d..d235390 100644
--- a/src/locales/zh-CN/model.js
+++ b/src/locales/zh-CN/model.js
@@ -82,6 +82,7 @@
   'header.form.refresh.view': '鍒锋柊椤甸潰',
   'header.form.refresh.grid': '鍒锋柊琛ㄦ牸',
   'header.form.refresh.maingrid': '鍒锋柊涓昏〃',
+  'header.form.refresh.mainline': '鍒锋柊涓昏〃锛堣锛�',
   'header.form.refresh.equaltab': '鍒锋柊鍚岀骇鏍囩',
   'header.form.refresh.subgrid': '鍒锋柊瀛愯〃',
   'header.form.popClose': '鏍囩鍏抽棴',
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index 9d3881e..0815699 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -173,6 +173,8 @@
       config.tabgroups.forEach(group => {
         group.sublist = group.sublist.filter(tab => permAction[tab.linkTab])
       })
+      // 鍘婚櫎绌鸿鏍囩
+      config.tabgroups = config.tabgroups.filter(group => group.sublist.length > 0)
 
       // 瑙嗗浘鏉冮檺
       config.charts = config.charts.filter(item => {
@@ -255,7 +257,6 @@
       let _tabActive = {} // 绛涢�夊睍寮�鐨則ab椤�
 
       config.tabgroups.forEach(group => {
-        if (group.sublist.length === 0) return
         _tabActive[group.uuid] = group.sublist[0].uuid
       })
 
@@ -928,9 +929,11 @@
   handleMainTable = (type, tab) => {
     if (type === 'maingrid' && tab.supMenu === 'mainTable') {
       this.reloadtable()
-    } else if (type === 'maingrid' && tab.supMenu) {
+    } else if (type === 'mainline' && tab.supMenu === 'mainTable') {
+      this.reloadtable()
+    } else if ((type === 'maingrid' || type === 'mainline') && tab.supMenu) {
       this.setState({
-        refreshtabs: [tab.supMenu]
+        refreshtabs: [type, tab.supMenu]
       }, () => {
         this.setState({
           refreshtabs: null
@@ -1445,41 +1448,37 @@
             })}
           </div> : null }
           {setting && setting.onload !== 'false' &&
-            config.tabgroups.map(group => {
-              if (group.sublist.length === 0) return null
-
-              return (
-                <Tabs activeKey={tabActive[group.uuid]} key={group.uuid} onChange={(key) => this.setState({tabActive: {...tabActive, [group.uuid]: key}})}>
-                  {group.sublist.map(_tab => {
-                    return (
-                      <TabPane tab={
-                        <span>
-                          {_tab.icon ? <Icon type={_tab.icon} /> : null}
-                          {_tab.label}
-                        </span>
-                      } key={_tab.uuid}>
-                        {_tab.type === 'SubTable' ?
-                          <SubTable
-                            Tab={_tab}
-                            menuType="main"
-                            MenuID={_tab.linkTab}
-                            mainSearch={_tab.searchPass === 'true' ? search : null}
-                            userConfig={userConfig ? userConfig[_tab.uuid] : null}
-                            triggerBtn={triggerBtn}
-                            SupMenuID={this.props.MenuID}
-                            refreshtabs={this.state.refreshtabs}
-                            ContainerId={this.state.ContainerId}
-                            BID={this.state.BIDs[_tab.supMenu] || ''}
-                            BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
-                            handleTableId={this.handleTableId}
-                            handleMainTable={(type) => this.handleMainTable(type, _tab)}
-                          /> : null}
-                      </TabPane>
-                    )
-                  })}
-                </Tabs>
-              )
-            })
+            config.tabgroups.map(group => (
+              <Tabs activeKey={tabActive[group.uuid]} key={group.uuid} onChange={(key) => this.setState({tabActive: {...tabActive, [group.uuid]: key}})}>
+                {group.sublist.map(_tab => {
+                  return (
+                    <TabPane tab={
+                      <span>
+                        {_tab.icon ? <Icon type={_tab.icon} /> : null}
+                        {_tab.label}
+                      </span>
+                    } key={_tab.uuid}>
+                      {_tab.type === 'SubTable' ?
+                        <SubTable
+                          Tab={_tab}
+                          menuType="main"
+                          MenuID={_tab.linkTab}
+                          mainSearch={_tab.searchPass === 'true' ? search : null}
+                          userConfig={userConfig ? userConfig[_tab.uuid] : null}
+                          triggerBtn={triggerBtn}
+                          SupMenuID={this.props.MenuID}
+                          refreshtabs={this.state.refreshtabs}
+                          ContainerId={this.state.ContainerId}
+                          BID={this.state.BIDs[_tab.supMenu] || ''}
+                          BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
+                          handleTableId={this.handleTableId}
+                          handleMainTable={(type) => this.handleMainTable(type, _tab)}
+                        /> : null}
+                    </TabPane>
+                  )
+                })}
+              </Tabs>)
+            )
           }
           <Button
             icon="copy"
diff --git a/src/tabviews/formtab/index.jsx b/src/tabviews/formtab/index.jsx
index f185a68..4bf485d 100644
--- a/src/tabviews/formtab/index.jsx
+++ b/src/tabviews/formtab/index.jsx
@@ -570,11 +570,11 @@
    * @description 瀛愯〃鎿嶄綔瀹屾垚鍚庡埛鏂颁富琛�
    */
   handleMainTable = (type, tab) => {
-    if (type === 'maingrid' && tab.supMenu === 'mainTable') {
+    if ((type === 'maingrid' || type === 'mainline') && tab.supMenu === 'mainTable') {
       this.loadmaindata()
-    } else if (type === 'maingrid' && tab.supMenu) {
+    } else if ((type === 'maingrid' || type === 'mainline') && tab.supMenu) {
       this.setState({
-        refreshtabs: [tab.supMenu]
+        refreshtabs: [type, tab.supMenu]
       }, () => {
         this.setState({
           refreshtabs: null
diff --git a/src/tabviews/managetable/index.jsx b/src/tabviews/managetable/index.jsx
index 278fa94..991c009 100644
--- a/src/tabviews/managetable/index.jsx
+++ b/src/tabviews/managetable/index.jsx
@@ -644,9 +644,11 @@
   handleMainTable = (type, tab) => {
     if (type === 'maingrid' && tab.supMenu === 'mainTable') {
       this.reloadtable()
-    } else if (type === 'maingrid' && tab.supMenu) {
+    } else if (type === 'mainline' && tab.supMenu === 'mainTable') {
+      this.reloadtable()
+    } else if ((type === 'maingrid' || type === 'mainline') && tab.supMenu) {
       this.setState({
-        refreshtabs: [tab.supMenu]
+        refreshtabs: [type, tab.supMenu]
       }, () => {
         this.setState({
           refreshtabs: null
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index c11b488..c78e7bc 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -469,6 +469,7 @@
     let _BID = this.props.BID
 
     let requireFields = search.filter(item => item.required && (!item.value || item.value.length === 0))
+    let prex = this.props.Tab && this.props.Tab.label ? this.props.Tab.label + '-' : ''
 
     if (requireFields.length > 0) {
       let labels = requireFields.map(item => item.label)
@@ -476,7 +477,7 @@
 
       notification.warning({
         top: 92,
-        message: this.state.dict['form.required.input'] + labels.join('銆�') + ' !',
+        message: prex + this.state.dict['form.required.input'] + labels.join('銆�') + ' !',
         duration: 3
       })
       return
@@ -522,7 +523,6 @@
       this.setState({
         loading: false
       })
-      let prex = this.props.Tab && this.props.Tab.label ? this.props.Tab.label + ': ' : ''
 
       notification.error({
         top: 92,
@@ -800,6 +800,8 @@
     } else if (btn.execSuccess === 'equaltab' && type === 'success') {
       this.reloadtable()
       this.props.handleMainTable('equaltab')
+    }else if (btn.execSuccess === 'mainline' && type === 'success') {
+      this.props.handleMainTable('mainline')
     }
   }
 
diff --git a/src/tabviews/treepage/index.jsx b/src/tabviews/treepage/index.jsx
new file mode 100644
index 0000000..462efa1
--- /dev/null
+++ b/src/tabviews/treepage/index.jsx
@@ -0,0 +1,882 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { notification, Spin, Tabs, Icon, Modal, Button, message, Tree, Typography, Row, Col, Card, Input, Empty } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import asyncSpinComponent from '@/utils/asyncSpinComponent'
+import {refreshTabView, modifyTabview} from '@/store/action'
+
+import NotFount from '@/components/404'
+import './index.scss'
+
+const VerifyCard = asyncComponent(() => import('@/tabviews/zshare/verifycard'))
+const SubTable = asyncSpinComponent(() => import('@/tabviews/subtable'))
+
+const { TabPane } = Tabs
+const { TreeNode } = Tree
+const { Search } = Input
+const { Paragraph } = Typography
+
+class TreePage extends Component {
+  static propTpyes = {
+    MenuID: PropTypes.string,    // 鑿滃崟Id
+    MenuNo: PropTypes.string,    // 鑿滃崟鍙傛暟
+    MenuName: PropTypes.string   // 鑿滃崟鍚嶇О
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    ContainerId: Utils.getuuid(), // 鑿滃崟澶栧眰html Id
+    loadingview: true,    // 椤甸潰鍔犺浇涓�
+    viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+    lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
+    config: {},           // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷寜閽�佹悳绱€�佹樉绀哄垪銆佹爣绛剧瓑
+    userConfig: null,     // 鐢ㄦ埛鑷畾涔夎缃�
+    userParam: null,      // 淇濆瓨鐢ㄦ埛缂栬緫涓殑閰嶇疆
+    setting: null,        // 椤甸潰鍏ㄥ眬璁剧疆锛氭暟鎹簮銆佹寜閽強鏄剧ず鍒楀浐瀹氥�佷富閿瓑
+    treedata: null,       // 鍒楄〃鏁版嵁闆�
+    treeNodes: null,      // 鍒楄〃鏁版嵁闆�
+    loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
+    BIDs: {},             // 涓婄骇琛╥d
+    visible: false,       // 寮规鏄剧ず闅愯棌鎺у埗
+    treevisible: false,   // 鑿滃崟缁撴瀯鏍戝脊妗嗘樉绀洪殣钘忔帶鍒�
+    refreshtabs: null,    // 闇�瑕佸埛鏂扮殑鏍囩闆�
+    confirmLoading: false,// 鑷畾涔夎缃ā鎬佹鍔犺浇涓�
+    revertLoading: false, // 鎭㈠榛樿璁剧疆
+    settingVisible: false,// 鑷畾涔夎缃ā鎬佹
+    triggerBtn: null,     // 鐐瑰嚮琛ㄦ牸涓垨蹇嵎閿Е鍙戠殑鎸夐挳
+    tabActive: null,      // 鏍囩椤靛睍寮�鎺у埗
+  }
+
+  /**
+   * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
+   */
+  async loadconfig () {
+    const { permAction } = this.props
+
+    let _param = {
+      func: 'sPC_Get_LongParam',
+      MenuID: this.props.MenuID
+    }
+    let result = await Api.getSystemCacheConfig(_param)
+
+    if (result.status) {
+      let config = ''
+      let userConfig = null
+      let _curUserConfig = ''
+
+      try { // 閰嶇疆淇℃伅瑙f瀽
+        config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+      } catch (e) {
+        console.warn('Parse Failure')
+        config = ''
+      }
+      
+      if (result.LongParamUser) {
+        try { // 閰嶇疆淇℃伅瑙f瀽
+          userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
+          _curUserConfig = userConfig[this.props.MenuID]
+        } catch (e) {
+          console.warn('Parse Failure')
+          userConfig = null
+        }
+      }
+
+      // 椤甸潰閰嶇疆瑙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
+      }
+
+      // 鏉冮檺杩囨护
+      config.tabgroups.forEach(group => {
+        group.sublist = group.sublist.filter(tab => permAction[tab.linkTab])
+      })
+      config.tabgroups = config.tabgroups.filter(group => group.sublist.length > 0)
+
+      if (_curUserConfig) {
+        config.setting = {...config.setting, ..._curUserConfig.setting}
+        config.easyCode = _curUserConfig.easyCode || config.easyCode || ''
+      }
+
+      let _tabActive = {} // 绛涢�夊睍寮�鐨則ab椤�
+      let _tabgroups = []
+
+      config.tabgroups.forEach(group => {
+        let _group = fromJS(group).toJS()
+        _group.sublist = _group.sublist.filter(tab => !tab.level)
+
+        if (_group.sublist.length > 0) {
+          _tabActive[_group.uuid] = _group.sublist[0].uuid
+          _tabgroups.push(_group)
+        }
+      })
+
+      this.setState({
+        loadingview: false,
+        config: config,
+        tabActive: _tabActive,
+        userConfig: userConfig,
+        setting: config.setting,
+        tabgroups: _tabgroups
+      }, () => {
+        this.loadmaindata()
+        this.setShortcut()
+      })
+    } else {
+      this.setState({
+        loadingview: false,
+        viewlost: true
+      })
+      notification.warning({
+        top: 92,
+        message: result.message,
+        duration: 5
+      })
+    }
+  }
+
+  setShortcut = () => {
+    const { userConfig, config } = this.state
+    if (!userConfig) return
+
+    document.onkeydown = (event) => {
+      let e = event || window.event
+      let keyCode = e.keyCode || e.which || e.charCode
+      let preKey = ''
+
+      if (e.ctrlKey) {
+        preKey = 'ctrl'
+      } else if (e.shiftKey) {
+        preKey = 'shift'
+      } else if (e.altKey) {
+        preKey = 'alt'
+      }
+
+      if (!preKey) return
+
+      let istrigger = false
+
+      Object.keys(userConfig).forEach(key => {
+        if (key === this.props.MenuID || !userConfig[key].action || istrigger) return
+
+        let _actions = userConfig[key].action
+
+        Object.keys(_actions).forEach(btnkey => {
+          let item = _actions[btnkey]
+
+          if (!item.shortcut || typeof(item.shortcut) !== 'object' || item.shortcut.length === 0 || istrigger) return
+
+          if (preKey === item.shortcut[0] && keyCode === item.shortcut[1]) {
+            e.preventDefault()
+            istrigger = true
+
+            let _groupId = ''
+            let _ActiveTabId = ''
+            config.tabgroups.forEach(group => {
+              if (group.sublist.length === 0) return
+
+              let _tab = group.sublist.filter(tab => tab.uuid === key)[0]
+              if (_tab) {
+                _groupId = group.uuid
+                _ActiveTabId = _tab.uuid
+              }
+            })
+
+            if (this.state.tabActive[_groupId] === _ActiveTabId) {
+              this.setState({
+                triggerBtn: {
+                  uuid: new Date().getTime(),
+                  parentId: key,
+                  button: {...item, uuid: btnkey},
+                  data: null
+                }
+              })
+            } else {
+              this.setState({
+                tabActive: {...this.state.tabActive, [_groupId]: _ActiveTabId}
+              }, () => {
+                this.setState({
+                  triggerBtn: {
+                    uuid: new Date().getTime(),
+                    parentId: key,
+                    button: {...item, uuid: btnkey},
+                    data: null
+                  }
+                })
+              })
+            }
+          }
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 涓昏〃鏁版嵁鍔犺浇
+   */ 
+  async loadmaindata () {
+    const { setting, BIDs } = this.state
+    let param = ''
+
+    this.setState({
+      loading: true
+    })
+
+    if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
+      param = this.getCustomParam()
+    } else {
+      param = this.getDefaultParam()
+    }
+
+    this.handleTableId('mainTable', '', '')
+
+    if (!param) { // 鏈幏鍙栧弬鏁版椂锛屼笉鍙戣姹�
+      return
+    }
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let parentNodes = []
+      let _options = []
+      result.data.forEach(item => {
+        let pval = item[setting.parentField]
+        let val = item[setting.valueField]
+
+        if (!val) return
+
+        if (pval === setting.mark) {
+          parentNodes.push({
+            title: item[setting.labelField] || '',
+            key: val,
+            parentId: '',
+            level: 1
+          })
+        } else if (pval) {
+          _options.push({
+            title: item[setting.labelField] || '',
+            key: val,
+            parentId: pval
+          })
+        }
+      })
+      let _treedata = this.getTree(parentNodes, _options)
+
+      this.setState({
+        treedata: _treedata,
+        treeNodes: fromJS(_treedata).toJS(),
+        loading: false,
+        BIDs: {
+          ...BIDs,
+          mainTable: ''
+        }
+      })
+    } else {
+      this.setState({
+        loading: false,
+        treeNodes: [],
+        treedata: []
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  /**
+   * @description 鑾峰彇缁撴瀯鏍戜俊鎭�
+   */
+  getTree = (parents, options) => {
+    parents.forEach(parent => {
+      parent.children = []
+      // 娣诲姞鑿滃崟鐨勫瓙鍏冪礌
+      options = options.filter(option => {
+        if (option.parentId === parent.key) {
+          option.level = parent.level + 1
+
+          parent.children.push(option)
+          return false
+        }
+        return true
+      })
+
+      if (parent.children.length === 0) {
+        parent.children = null
+      } else {
+        parent.children = this.getTree(parent.children, options)
+      }
+    })
+    return parents
+  }
+
+  /**
+   * @description 鑾峰彇鏍戣妭鐐�
+   */
+  renderTreeNodes = (nodes) => {
+    return nodes.map(item => {
+      if (item.children) {
+        return (
+          <TreeNode icon={<Icon type="folder-open" />} title={item.title} key={item.key} dataRef={item}>
+            {this.renderTreeNodes(item.children)}
+          </TreeNode>
+        )
+      }
+      return <TreeNode icon={<Icon type="file" />} key={item.key} title={item.title} dataRef={item} isLeaf />
+    })
+  }
+
+  selectTreeNode = (selectedKeys, {selected, node}) => {
+    const { config } = this.state
+
+    let selectId = selectedKeys[0]
+    let _data = fromJS(node.props.dataRef).toJS()
+    delete _data.children
+
+    let _tabgroups = []
+    let _tabActive = {}
+
+    config.tabgroups.forEach(group => {
+      let _group = fromJS(group).toJS()
+      _group.sublist = _group.sublist.filter(tab => !tab.level || (selected && tab.level === _data.level))
+
+      if (_group.sublist.length > 0) {
+        _tabActive[_group.uuid] = _group.sublist[0].uuid
+        _tabgroups.push(_group)
+      }
+    })
+
+    this.setState({
+      tabgroups: _tabgroups,
+      tabActive: _tabActive
+    })
+
+    if (selected) {
+      this.handleTableId('mainTable', selectId, _data)
+    } else {
+      this.handleTableId('mainTable', '', '')
+    }
+  }
+
+  /**
+   * @description 鑾峰彇鐢ㄦ埛鑷畾涔夊瓨鍌ㄨ繃绋嬩紶鍙�
+   */
+  getCustomParam = () => {
+    const { setting } = this.state
+
+    let param = {
+      OrderCol: setting.order,
+      dataM: this.props.dataManager ? 'Y' : '',
+    }
+
+    if (setting.interType === 'inner') {
+      param.func = setting.innerFunc
+    } else {
+      if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) {
+        param.rduri = window.GLOB.mainSystemApi
+      } else if (setting.sysInterface !== 'true') {
+        param.rduri = setting.interface
+      }
+
+      if (setting.outerFunc) {
+        param.func = setting.outerFunc
+      }
+    }
+
+    return param
+  }
+
+  /**
+   * @description 鑾峰彇绯荤粺瀛樺偍杩囩▼ sPC_Get_TableData 鐨勫弬鏁�
+   */
+  getDefaultParam = () => {
+    const { setting } = this.state
+
+    let arr_field = `${setting.valueField},${setting.labelField},${setting.parentField}`
+    
+    let param = {
+      func: 'sPC_Get_TableData',
+      obj_name: 'data',
+      arr_field: arr_field,
+      custom_script: setting.customScript || '',
+      default_sql: setting.default || 'true',
+      dataM: this.props.dataManager ? 'Y' : ''
+    }
+    
+    let _dataresource = setting.dataresource
+
+    if (/\s/.test(_dataresource)) {
+      _dataresource = '(' + _dataresource + ') tb'
+    }
+
+    if (this.props.dataManager) { // 鏁版嵁鏉冮檺
+      _dataresource = _dataresource.replace(/\$@/ig, '/*')
+      _dataresource = _dataresource.replace(/@\$/ig, '*/')
+      param.custom_script = param.custom_script.replace(/\$@/ig, '/*')
+      param.custom_script = param.custom_script.replace(/@\$/ig, '*/')
+    } else {
+      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+      param.custom_script = param.custom_script.replace(/@\$|\$@/ig, '')
+    }
+
+    let LText = ''
+
+    if (setting.default !== 'false') {
+      LText = `select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows`
+    }
+
+    if (param.custom_script) {
+      param.custom_script = param.custom_script.replace(/@orderBy@/ig, setting.order)
+
+      if (LText) {
+        LText += `
+          aaa:
+          if @ErrorCode!=''
+            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+        `
+      } else {
+        param.custom_script += `
+          aaa:
+          if @ErrorCode!=''
+            insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+        `
+      }
+
+      param.custom_script = Utils.formatOptions(param.custom_script)
+    }
+
+    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 = ''
+
+    return param
+  }
+
+  /**
+   * @description 椤甸潰鍒锋柊锛岄噸鏂拌幏鍙栭厤缃�
+   */
+  reloadview = () => {
+    this.setState({
+      loadingview: true,
+      viewlost: false,
+      lostmsg: '',
+      config: {},
+      userConfig: null,
+      userParam: null,
+      setting: null,
+      treedata: null,
+      treeNodes: null,
+      loading: false,
+      BIDs: {},
+    }, () => {
+      this.loadconfig()
+    })
+  }
+
+  /**
+   * @description 琛ㄦ牸鍒锋柊
+   */
+  reloadtable = () => {
+    this.setState({
+      pageIndex: 1
+    }, () => {
+      this.loadmaindata()
+    })
+  }
+
+  /**
+   * @description 瀛愯〃鎿嶄綔瀹屾垚鍚庡埛鏂颁富琛�
+   */
+  handleMainTable = (type, tab) => {
+    if (type === 'maingrid' && tab.supMenu === 'mainTable') {
+      this.reloadtable()
+    } else if (type === 'mainline' && tab.supMenu === 'mainTable') {
+      this.reloadtable()
+    } else if ((type === 'maingrid' || type === 'mainline') && tab.supMenu) {
+      this.setState({
+        refreshtabs: [type, tab.supMenu]
+      }, () => {
+        this.setState({
+          refreshtabs: null
+        })
+      })
+    } else if (type === 'equaltab' && tab.equalTab && tab.equalTab.length > 0) {
+      this.setState({
+        refreshtabs: tab.equalTab
+      }, () => {
+        this.setState({
+          refreshtabs: null
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 琛ㄦ牸Id鍙樺寲
+   */
+  handleTableId = (type, id, data) => {
+    const { BIDs } = this.state
+
+    this.setState({
+      BIDs: {
+        ...BIDs,
+        [type]: id,
+        [type + 'data']: data
+      }
+    })
+  }
+
+  handleviewconfig = (e) => {
+    e.stopPropagation()
+
+    const { MenuNo } = this.props
+    const { config } = this.state
+
+    if (config && config.funcs && config.funcs.length > 0) {
+      this.setState({
+        treevisible: true
+      })
+    } else {
+      let oInput = document.createElement('input')
+      oInput.value = MenuNo || ''
+      document.body.appendChild(oInput)
+      oInput.select()
+      document.execCommand('Copy')
+      document.body.removeChild(oInput)
+      message.success(this.state.dict['main.copy.success'])
+    }
+  }
+
+  getTreeNode = (data) => {
+    let _type = {
+      view: '椤甸潰',
+      btn: '鎸夐挳',
+      tab: '鏍囩'
+    }
+
+    return data.map(item => {
+      let _title = _type[item.subtype]
+      let _others = []
+
+      _others.push(
+        (item.menuNo ? item.menuNo + '(鑿滃崟鍙傛暟)' : ''),
+        (item.tableName ? item.tableName + '(琛ㄥ悕) ' : ''),
+        (item.innerFunc ? item.innerFunc + '(鍐呴儴鍑芥暟) ' : ''),
+        (item.outerFunc ? item.outerFunc + '(澶栭儴鍑芥暟)' : '')
+      )
+      _others = _others.filter(Boolean)
+      _others = _others.join('銆�')
+
+      if (item.label) {
+        _title = _title + '(' + item.label + ')'
+      }
+      if (_others) {
+        _title = _title + ': ' + _others
+      }
+
+      if (item.subfuncs && item.subfuncs.length > 0) {
+        return (
+          <TreeNode title={_title} key={item.uuid} dataRef={item} selectable={false}>
+            {this.getTreeNode(item.subfuncs)}
+          </TreeNode>
+        )
+      }
+      return <TreeNode key={item.uuid} title={_title} isLeaf selectable={false} />
+    })
+  }
+
+  controlCustomSetting = () => {
+    this.setState({
+      settingVisible: true,
+      confirmLoading: false,
+      revertLoading: false
+    })
+  }
+
+  changeMenuParam = (param) => {
+    this.setState({userParam: param})
+  }
+
+  settingRevert = () => {
+    let param = {
+      func: 's_TrdMenu_UserParam_del',
+      MenuID: this.props.MenuID
+    }
+    this.setState({
+      revertLoading: true
+    })
+
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
+        this.setState({
+          revertLoading: false
+        })
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      }
+      this.setState({
+        settingVisible: false,
+        revertLoading: false
+      }, () => {
+        window.GLOB.CacheMap = new Map()
+        this.reloadview()
+      })
+    })
+  }
+
+  settingSubmit = () => {
+    const { userParam } = this.state
+    let _LongParam = ''
+
+    try {
+      _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(userParam)))
+    } catch (e) {
+      notification.warning({
+        top: 92,
+        message: '缂栬瘧閿欒',
+        duration: 5
+      })
+      return
+    }
+
+    let easyCode = userParam[this.props.MenuID] ? userParam[this.props.MenuID].easyCode : ''
+
+    let param = {
+      func: 'sPC_TrdMenu_UserParam',
+      MenuID: this.props.MenuID,
+      EasyCode: easyCode || '',
+      LongParam: _LongParam
+    }
+
+    this.setState({
+      confirmLoading: true
+    })
+
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
+        this.setState({
+          confirmLoading: false
+        })
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      }
+      this.setState({
+        settingVisible: false,
+        confirmLoading: false
+      }, () => {
+        window.GLOB.CacheMap = new Map()
+        this.reloadview()
+      })
+    })
+  }
+
+  UNSAFE_componentWillMount () {
+    // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
+    this.loadconfig()
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (nextProps.refreshTab && nextProps.refreshTab.MenuID === this.props.MenuID) {
+      if (nextProps.refreshTab.position === 'view') {
+        this.reloadview()
+      }
+      this.props.refreshTabView('')
+    } else if (!is(fromJS(this.props.tabviews), fromJS(nextProps.tabviews))) {
+      let selectTab = nextProps.tabviews.filter(tab => tab.selected)[0]
+      if (selectTab && selectTab.MenuID === this.props.MenuID) {
+        this.setShortcut()
+      }
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    document.onkeydown = () => {}
+  }
+
+  render() {
+    const { setting, loadingview, viewlost, config, triggerBtn, userConfig, tabActive, tabgroups, treeNodes, treedata } = this.state
+
+    return (
+      <div className="tree-page" id={this.state.ContainerId}>
+        {loadingview && <Spin size="large" />}
+        {setting ? <Row gutter={16}>
+          <Col span={setting.width}>
+            <Card
+              className="tree-card"
+              title={
+                <span className="tree-title">
+                  <span className="title">{setting.title}</span>
+                  {setting.searchable !== 'false' ? <Search onSearch={this.treeFilter} /> : null}
+                </span>
+              }
+              bordered={false}
+            >
+              {treeNodes && treeNodes.length > 0 ? <div className="tree-box">
+                <Tree
+                  blockNode
+                  onSelect={this.selectTreeNode}
+                  showIcon={setting.showIcon === 'true'}
+                  showLine={setting.showLine === 'true'}
+                >
+                  {this.renderTreeNodes(treeNodes)}
+                </Tree>
+              </div> : null}
+              {treeNodes && treeNodes.length === 0 ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+              {!treedata ? <Spin /> : null}
+            </Card>
+          </Col>
+          <Col span={24 - setting.width} style={{paddingTop: 6}}>
+            {tabgroups.map(group => (
+              <Tabs activeKey={tabActive[group.uuid]} key={group.uuid} onChange={(key) => this.setState({tabActive: {...tabActive, [group.uuid]: key}})}>
+                {group.sublist.map(_tab => {
+                  return (
+                    <TabPane tab={
+                      <span>
+                        {_tab.icon ? <Icon type={_tab.icon} /> : null}
+                        {_tab.label}
+                      </span>
+                    } key={_tab.uuid}>
+                      {_tab.type === 'SubTable' ?
+                        <SubTable
+                          Tab={_tab}
+                          menuType="main"
+                          MenuID={_tab.linkTab}
+                          mainSearch={null}
+                          userConfig={userConfig ? userConfig[_tab.uuid] : null}
+                          triggerBtn={triggerBtn}
+                          SupMenuID={this.props.MenuID}
+                          refreshtabs={this.state.refreshtabs}
+                          ContainerId={this.state.ContainerId}
+                          BID={this.state.BIDs[_tab.supMenu] || ''}
+                          BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
+                          handleTableId={this.handleTableId}
+                          handleMainTable={(type) => this.handleMainTable(type, _tab)}
+                        /> : null}
+                    </TabPane>
+                  )
+                })}
+              </Tabs>)
+            )}
+          </Col>
+        </Row> : null}
+        
+        <Button
+          icon="copy"
+          shape="circle"
+          className="tree-page-copy"
+          onClick={this.handleviewconfig}
+        />
+        <Modal
+          className="menu-tree-modal"
+          title={'鑿滃崟缁撴瀯鏍�'}
+          width={'650px'}
+          maskClosable={false}
+          visible={this.state.treevisible}
+          onCancel={() => this.setState({treevisible: false})}
+          footer={[
+            <Button key="close" onClick={() => this.setState({treevisible: false})}>{this.state.dict['main.close']}</Button>
+          ]}
+          destroyOnClose
+        >
+          <div className="menu-header">
+            <span>鑿滃崟鍚嶇О锛歿this.props.MenuName}</span>
+            <span>鑿滃崟鍙傛暟锛歿<Paragraph copyable>{this.props.MenuNo}</Paragraph>}</span>
+          </div>
+          {this.state.treevisible ? <Tree defaultExpandAll showLine={true}>
+            {this.getTreeNode(config.funcs)}
+          </Tree> : null}
+        </Modal>
+        {/* 鎸夐挳浣跨敤绯荤粺瀛樺偍杩囩▼鏃讹紝楠岃瘉淇℃伅妯℃�佹 */}
+        <Modal
+          wrapClassName="common-table-custom-modal"
+          title={'鑷畾涔夎缃�'}
+          maskClosable={false}
+          width={950}
+          visible={this.state.settingVisible}
+          onCancel={() => { this.setState({ settingVisible: false }) }}
+          footer={[
+            <Button key="revert" type="danger" loading={this.state.revertLoading} onClick={this.settingRevert}>{this.state.dict['main.revert.default']}</Button>,
+            <Button key="cancel" onClick={() => { this.setState({ settingVisible: false }) }}>{this.state.dict['main.cancel']}</Button>,
+            <Button key="confirm" type="primary" loading={this.state.confirmLoading} onClick={this.settingSubmit}>{this.state.dict['main.submit']}</Button>
+          ]}
+          destroyOnClose
+        >
+          {this.state.settingVisible ?
+            <VerifyCard
+              MenuID={this.props.MenuID}
+              MenuName={this.props.MenuName}
+              permAction={this.props.permAction}
+              permRoles={this.props.permRoles}
+              config={this.state.config}
+              userConfig={this.state.userConfig}
+              columns={[]}
+              handleParam={this.changeMenuParam}
+            /> : null
+          }
+        </Modal>
+        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    tabviews: state.tabviews,
+    refreshTab: state.refreshTab,
+    permAction: state.permAction,
+    permMenus: state.permMenus,
+    permRoles: state.permRoles,
+    memberLevel: state.memberLevel,
+    dataManager: state.dataManager
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    refreshTabView: (refreshTab) => dispatch(refreshTabView(refreshTab)),
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(TreePage)
\ No newline at end of file
diff --git a/src/tabviews/treepage/index.scss b/src/tabviews/treepage/index.scss
new file mode 100644
index 0000000..c3833fc
--- /dev/null
+++ b/src/tabviews/treepage/index.scss
@@ -0,0 +1,168 @@
+.tree-page {
+  position: relative;
+  min-height: calc(100vh - 94px);
+  padding: 16px 16px 80px;
+  .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;
+    z-index: 10;
+    left: calc(50% - 22px);
+    top: calc(50vh - 70px);
+  }
+  .tree-card {
+    min-height: calc(100vh - 125px);
+    box-shadow: 0px 0px 2px #dddddd;
+    .ant-card-head {
+      padding: 0 10px;
+      .ant-card-head-title {
+        padding: 13px 0 7px;
+        .tree-title {
+          display: inline-block;
+          width: 100%;
+          color: #1890ff;
+          .ant-input-affix-wrapper {
+            width: calc(100% - 140px);
+            margin-top: 0px;
+            float: right;
+            height: 28px;
+
+            .ant-input {
+              max-width: 130px;
+              border-radius: 20px;
+              height: 30px;
+            }
+            .ant-input-suffix {
+              margin-top: 3px;
+            }
+          }
+          .title {
+            display: inline-block;
+            max-width: 50%;
+            overflow: hidden;
+            text-overflow: ellipsis;
+            white-space: nowrap;
+          }
+        }
+      }
+    }
+    >.ant-card-body {
+      padding: 0px;
+      .ant-menu-inline {
+        border-right: 0;
+        margin-top: 2px;
+      }
+
+      .tree-box {
+        min-height: calc(100vh - 180px);
+        overflow-x: auto;
+        padding-bottom: 20px;
+      }
+      .tree-box::-webkit-scrollbar {
+        height: 10px;
+      }
+      .tree-box::-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);
+      }
+      .tree-box::-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-tree-node-selected
+      .ant-tree.ant-tree-directory > li span.ant-tree-node-content-wrapper::before,
+      .ant-tree.ant-tree-directory .ant-tree-child-tree > li span.ant-tree-node-content-wrapper::before {
+        right: 0px;
+      }
+
+      >.ant-spin {
+        margin: 10px;
+      }
+    }
+  }
+  
+  > .ant-tabs {
+    padding: 0px 20px;
+    margin-bottom: 20px;
+    .ant-tabs-tab:not(.ant-tabs-tab-active) {
+      cursor: pointer;
+    }
+    .ant-tabs-tab.ant-tabs-tab-active {
+      cursor: default;
+    }
+  }
+  .tree-page-copy {
+    position: fixed;
+    z-index: 2;
+    bottom: 65px;
+    right: 30px;
+    width: 40px;
+    height: 40px;
+  }
+  
+}
+
+.menu-tree-modal {
+  .ant-modal-body {
+    min-height: 300px;
+    .menu-header {
+      text-align: center;
+      span {
+        font-weight: 600;
+        margin-right: 20px;
+      }
+      .ant-typography {
+        font-weight: 600;
+        display: inline-block;
+      }
+    }
+    .ant-tree li .ant-tree-node-content-wrapper {
+      cursor: default;
+    }
+  }
+}
+.common-table-custom-modal {
+  .ant-modal {
+    top: 50px;
+    padding-bottom: 5px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+      min-height: 250px;
+      overflow-y: auto;
+      .ant-empty {
+        margin: 15vh 8px;
+      }
+    }
+    .ant-modal-body::-webkit-scrollbar {
+      width: 7px;
+    }
+    .ant-modal-body::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+      background: rgba(0, 0, 0, 0.13);
+    }
+    .ant-modal-body::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/zshare/cardcomponent/index.jsx b/src/tabviews/zshare/cardcomponent/index.jsx
index 7877d82..d17f336 100644
--- a/src/tabviews/zshare/cardcomponent/index.jsx
+++ b/src/tabviews/zshare/cardcomponent/index.jsx
@@ -295,7 +295,7 @@
               {card.details.map((detail, i) => {
                 return (
                   <div className={detail.class} key={i}>
-                    {this.getContent(detail.column)}
+                    {detail.column ? this.getContent(detail.column) : detail.content}
                   </div>
                 )
               })}
@@ -538,6 +538,7 @@
           </Tabs> : null
         }
         {!data || data.length === 0 ? <Empty description={false}/> : null}
+        <div className="clear"></div>
       </div>
     )
   }
diff --git a/src/tabviews/zshare/cardcomponent/index.scss b/src/tabviews/zshare/cardcomponent/index.scss
index 097bebe..75c7e06 100644
--- a/src/tabviews/zshare/cardcomponent/index.scss
+++ b/src/tabviews/zshare/cardcomponent/index.scss
@@ -1,6 +1,7 @@
 .card-row-box {
   min-height: 50px;
   padding: 0px 10px;
+  margin-bottom: 15px;
 
   .chart-card-box {
     padding: 0 10px;
@@ -218,4 +219,7 @@
       }
     }
   }
+  .clear {
+    clear: both;
+  }
 }
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index e032fdd..a9f7bb8 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -26,7 +26,6 @@
 
 const { Panel } = Collapse
 const { confirm } = Modal
-const CommonDict = (!localStorage.getItem('lang') || localStorage.getItem('lang') === 'zh-CN') ? zhCN : enUS
 const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/settingcomponent'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
 const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
@@ -44,13 +43,12 @@
   }
 
   state = {
-    dict: CommonDict,        // 瀛楀吀
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     config: null,            // 椤甸潰閰嶇疆
     tableFields: [],         // 琛ㄦ牸鏄剧ず鍒�
     fields: null,            // 鎼滅储鏉′欢鍙婃樉绀哄垪锛屽彲閫夊瓧娈�
     menuformlist: null,      // 鍩烘湰淇℃伅琛ㄥ崟瀛楁
     formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
-    card: null,              // 缂栬緫鍏冪礌
     menuloading: false,      // 鑿滃崟淇濆瓨涓�
     menucloseloading: false, // 鑿滃崟鍏抽棴鏃讹紝閫夋嫨淇濆瓨
     loading: false,          // 鍔犺浇涓紝椤甸潰spin
@@ -254,7 +252,7 @@
   }
 
   /**
-   * @description 涓夌骇鑿滃崟鍒囨崲妯℃澘
+   * @description 涓夌骇鑿滃崟鍒囨崲妯℃澘(寮冪敤)
    */
   changeTemplate = () => {
     this.props.handleView({tabview: 'template'})
@@ -1371,7 +1369,7 @@
               <div>
                 <EditComponent dict={this.state.dict} type="maintable" config={this.state.config} MenuID={this.props.menu.MenuID} thawButtons={this.state.thawButtons} refresh={this.editConfig}/>
                 <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={this.state.config.enabled} onChange={this.onEnabledChange} />
-                <Button type="primary" onClick={this.changeTemplate}>{this.state.dict['header.menu.template.change']}</Button>
+                {/* <Button type="primary" onClick={this.changeTemplate}>{this.state.dict['header.menu.template.change']}</Button> */}
                 <Button type="primary" onClick={this.submitConfig} loading={this.state.menuloading}>{this.state.dict['header.save']}</Button>
                 <Button onClick={this.cancelConfig}>{this.state.dict['header.return']}</Button>
               </div>
@@ -1451,7 +1449,6 @@
               </div>
               {/* 鏍囩缁� */}
               <TabsComponent
-                type="main"
                 config={config}
                 tabs={this.state.tabviews}
                 setSubConfig={(item) => this.setSubConfig(item, 'tab')}
diff --git a/src/templates/formtabconfig/index.jsx b/src/templates/formtabconfig/index.jsx
index 8f9a540..c726398 100644
--- a/src/templates/formtabconfig/index.jsx
+++ b/src/templates/formtabconfig/index.jsx
@@ -146,6 +146,9 @@
       columns = menu.LongParam.columns
     }
 
+    // 閰嶇疆榛樿鍊硷紝鍏煎
+    _config.Template = 'FormTab'
+
     this.setState({
       config: _config,
       activeKey: btnTab.activeKey || '0',
@@ -2003,7 +2006,6 @@
               </div>
               {/* 鏍囩缁� */}
               <TabsComponent
-                type="formtab"
                 config={config}
                 tabs={this.state.tabviews}
                 setSubConfig={(item) => this.setSubConfig(item, 'tab')}
diff --git a/src/templates/formtabconfig/source.jsx b/src/templates/formtabconfig/source.jsx
index 90bd820..cb82f99 100644
--- a/src/templates/formtabconfig/source.jsx
+++ b/src/templates/formtabconfig/source.jsx
@@ -6,7 +6,7 @@
 
 class CommonTableBaseData {
   baseConfig = {
-    type: 'FormTab',
+    Template: 'FormTab',
     enabled: false,
     setting: {
       datatype: 'query',
diff --git a/src/templates/menuconfig/editthdmenu/index.jsx b/src/templates/menuconfig/editthdmenu/index.jsx
index e667b34..bdf7e4c 100644
--- a/src/templates/menuconfig/editthdmenu/index.jsx
+++ b/src/templates/menuconfig/editthdmenu/index.jsx
@@ -21,6 +21,7 @@
 import './index.scss'
 
 const ComTableConfig = asyncLoadComponent(() => import('@/templates/comtableconfig'))
+const TreePageConfig = asyncLoadComponent(() => import('@/templates/treepageconfig'))
 const FormTabConfig = asyncLoadComponent(() => import('@/templates/formtabconfig'))
 const ModalConfig = asyncLoadComponent(() => import('@/templates/modalconfig'))
 const SubTable = asyncLoadComponent(() => import('@/templates/subtableconfig'))
@@ -790,7 +791,7 @@
                 <TabPane tab="绯荤粺妯℃澘" key="1">
                   <Row>
                     {this.state.sysTemplates.map((template, index) => {
-                      if (template.hidden) return ''
+                      if (template.hidden) return null
 
                       return (
                         <Col key={`${index}`} span={8}>
@@ -836,6 +837,14 @@
             </div>
           </div> : null
         }
+        {this.state.tabview === 'TreePage' ?
+          <TreePageConfig
+            menu={this.state.editMenu}
+            optionLibs={this.state.optionLibs}
+            reloadmenu={() => {this.props.reload()}}
+            handleView={this.handleView}
+          /> : null
+        }
         {this.state.tabview === 'CommonTable' ?
           <ComTableConfig
             menu={this.state.editMenu}
diff --git a/src/templates/sharecomponent/chartgroupcomponent/chartform/index.jsx b/src/templates/sharecomponent/chartgroupcomponent/chartform/index.jsx
index 0dd5540..a1f35d5 100644
--- a/src/templates/sharecomponent/chartgroupcomponent/chartform/index.jsx
+++ b/src/templates/sharecomponent/chartgroupcomponent/chartform/index.jsx
@@ -16,7 +16,7 @@
 import card1 from '@/assets/img/card1.png'
 import card2 from '@/assets/img/card2.png'
 import card3 from '@/assets/img/card3.png'
-import card4 from '@/assets/img/card4.png'
+// import card4 from '@/assets/img/card4.png'
 import card5 from '@/assets/img/card5.png'
 import './index.scss'
 
@@ -125,11 +125,11 @@
       url: card3,
       subelement: ['content', 'avatar', 'header']
     },
-    {
-      uuid: 'card4',
-      url: card4,
-      subelement: ['content', 'avatar', 'bottom']
-    },
+    // {
+    //   uuid: 'card4',
+    //   url: card4,
+    //   subelement: ['content', 'avatar', 'bottom']
+    // },
     {
       uuid: 'card5',
       url: card5,
diff --git a/src/templates/sharecomponent/tabscomponent/index.jsx b/src/templates/sharecomponent/tabscomponent/index.jsx
index 5749ffe..79711ac 100644
--- a/src/templates/sharecomponent/tabscomponent/index.jsx
+++ b/src/templates/sharecomponent/tabscomponent/index.jsx
@@ -16,7 +16,6 @@
 
 class TablesComponent extends Component {
   static propTpyes = {
-    type: PropTypes.string,       // 鑿滃崟绫诲瀷
     tabs: PropTypes.array,        // 鏍囩缁�
     config: PropTypes.object,     // 椤甸潰閰嶇疆
     setSubConfig: PropTypes.func, // 瀛愭爣绛鹃厤缃�
@@ -28,6 +27,7 @@
     tabgroups: [],     // 鏍囩缁�
     card: [],          // 缂栬緫鏍囩
     group: [],         // 缂栬緫缁�
+    levels: null,      // 鏍戝舰椤甸潰鐨勬爣绛剧骇鍒叧鑱�
     visible: false     // 妯℃�佹鎺у埗
   }
 
@@ -83,22 +83,28 @@
    * @description 鏍囩缂栬緫锛岀瓫閫夊彲閫夌殑涓嬬骇鏍囩涓庡凡鍏宠仈鐨勪笅绾ф爣绛�
    */
   handleTab = (card, _group) => {
+    const { config } = this.props
     let tabgroups = fromJS(this.state.tabgroups).toJS()
 
     let menus = [
       {value: '', text: '绌�'},
-      {value: 'mainTable', text: this.props.type === 'main' ? '涓昏〃' : '涓绘暟鎹�'}
+      {value: 'mainTable', text: '涓昏〃'}
     ]
     let equalTabs = []
     let supMenu = card.supMenu || ''
     let equalTab = card.equalTab || []
     let isuptab = true
     let equalTabIds = []
+    let levels = {}
 
-    tabgroups.forEach((group, i) => {
+    tabgroups.forEach(group => {
       if (group.uuid === _group.uuid) {
         isuptab = false
         group.sublist.forEach(tab => { // 鍙叧鑱旂殑鍚岀骇鏍囩
+          if (tab.level) {
+            levels[tab.uuid] = tab.level
+          }
+
           if (tab.uuid === card.uuid) return
 
           equalTabIds.push(tab.uuid)
@@ -106,6 +112,10 @@
         })
       } else if (isuptab) {
         group.sublist.forEach(tab => {
+          if (tab.level) {
+            levels[tab.uuid] = tab.level
+          }
+
           menus.push({
             value: tab.uuid,
             text: tab.label
@@ -122,11 +132,16 @@
       equalTab = equalTab.filter(tabId => equalTabIds.includes(tabId))
     }
 
+    if (config.Template !== 'TreePage') {
+      levels = null
+    }
+
     this.setState({
       visible: true,
       card: card,
+      levels: levels,
       group: _group,
-      formlist: getTabForm(card, supMenu, menus, equalTab, equalTabs, this.props.type)
+      formlist: getTabForm(card, supMenu, menus, equalTab, equalTabs, config.Template)
     })
   }
 
@@ -176,6 +191,20 @@
         return _group
       })
 
+      // 鏇存柊涓庝箣鍏宠仈鐨勬爣绛炬樉绀虹骇鍒�
+      if (config.Template === 'TreePage' && res.level !== card.level) {
+        tabgroups = tabgroups.map(_group => {
+          _group.sublist = _group.sublist.map(item => {
+            if (item.supMenu === res.uuid) {
+              item.level = res.level
+            }
+
+            return item
+          })
+          return _group
+        })
+      }
+
       this.setState({
         card: null,
         group: null,
@@ -204,6 +233,17 @@
           if (_group.uuid === group.uuid) {
             _group.sublist = _group.sublist.filter(item => item.uuid !== card.uuid)
           }
+          return _group
+        })
+
+        // 娓呴櫎涓庝箣鍏宠仈鏍囩鐨勪笂绾ф爣绛捐缃�
+        tabgroups = tabgroups.map(_group => {
+          _group.sublist = _group.sublist.map(item => {
+            if (item.supMenu === card.uuid) {
+              item.supMenu = ''
+            }
+            return item
+          })
           return _group
         })
 
@@ -386,6 +426,7 @@
             dict={this.state.dict}
             card={this.state.card}
             tabs={this.props.tabs}
+            levels={this.state.levels}
             formlist={this.state.formlist}
             inputSubmit={this.handleSubmit}
             wrappedComponentRef={(inst) => this.tabsFormRef = inst}
diff --git a/src/templates/sharecomponent/tabscomponent/tabform/index.jsx b/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
index eb0bedf..37d686b 100644
--- a/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
+++ b/src/templates/sharecomponent/tabscomponent/tabform/index.jsx
@@ -1,6 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Select, Icon, Tooltip, Radio } from 'antd'
+import { Form, Row, Col, Input, Select, Icon, Tooltip, Radio, InputNumber } from 'antd'
 import { formRule } from '@/utils/option.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
@@ -11,6 +11,7 @@
     tabs: PropTypes.array,       // 鍙叧鑱旀爣绛鹃泦
     dict: PropTypes.object,      // 瀛楀吀椤�
     formlist: PropTypes.any,     // 琛ㄥ崟
+    levels: PropTypes.any,       // 鏍囩鏄剧ず绾у埆
     card: PropTypes.object,      // 鏍囩椤典俊鎭�
     inputSubmit: PropTypes.any   // 鍥炶溅鎻愪氦浜嬩欢
   }
@@ -79,20 +80,19 @@
               ..._tabs
             ]
             item.initVal = ''
-            item.hidden = true
           }
           return item
         })
       }, () => {
-        this.setState({
-          formlist: this.state.formlist.map(item => {
-            if (item.key === 'linkTab') {
-              item.hidden = false
-            }
-            return item
-          })
-        })
+        if (this.props.form.getFieldValue('linkTab') !== undefined) {
+          this.props.form.setFieldsValue({linkTab: ''})
+        }
       })
+    } else if (key === 'supMenu' && this.props.levels) {
+
+      if (value && value !== 'mainTable') {
+        this.props.form.setFieldsValue({level: this.props.levels[value]})
+      }
     }
   }
 
@@ -111,7 +111,7 @@
     this.state.formlist.forEach((item, index) => {
       if (item.hidden || item.forbid) return
 
-      if (item.type === 'text') { // 鏂囨湰鎼滅储
+      if (item.type === 'text') {
         let rules = []
 
         if (item.key === 'foreignKey') {
@@ -143,6 +143,28 @@
                   ...rules
                 ]
               })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'number') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={
+              item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<InputNumber disabled={item.readonly} min={item.min} max={item.max} precision={0} />)}
             </Form.Item>
           </Col>
         )
@@ -269,7 +291,7 @@
       }
     }
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form commontable-tab-form">
+      <Form {...formItemLayout} className="model-tab-form">
         <Row gutter={24}>{this.getFields()}</Row>
       </Form>
     )
diff --git a/src/templates/sharecomponent/tabscomponent/tabform/index.scss b/src/templates/sharecomponent/tabscomponent/tabform/index.scss
index 930f1a5..d9327f1 100644
--- a/src/templates/sharecomponent/tabscomponent/tabform/index.scss
+++ b/src/templates/sharecomponent/tabscomponent/tabform/index.scss
@@ -1,7 +1,16 @@
-.ant-advanced-search-form.commontable-tab-form {
+.model-tab-form {
   min-height: 180px;
   .anticon-question-circle {
     color: #c49f47;
     margin-right: 3px;
   }
+  .ant-input-number {
+    width: 100%;
+    .ant-input-number-input:read-only {
+      color: red;
+      :hover {
+        border-color: #d9d9d9;
+      }
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/index.jsx b/src/templates/sharecomponent/treesettingcomponent/index.jsx
new file mode 100644
index 0000000..a45f4f5
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/index.jsx
@@ -0,0 +1,181 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Icon, Modal, Button, notification } from 'antd'
+
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import { getTreeSettingForm } from '@/templates/zshare/formconfig'
+
+import SettingForm from './settingform'
+import CreateFunc from '@/templates/zshare/createfunc'
+import CreateInterface from '@/templates/zshare/createinterface'
+
+import './index.scss'
+
+class TreeSettingComponent extends Component {
+  static propTpyes = {
+    MenuID: PropTypes.string,        // 鑿滃崟ID
+    config: PropTypes.object,        // 鑿滃崟閰嶇疆淇℃伅
+    permFuncField: PropTypes.array,  // 瀛樺偍杩囩▼鍙敤寮�澶村瓧娈�
+    menuformRef: PropTypes.any,      // 鑿滃崟鍩烘湰淇℃伅琛ㄥ崟
+    updatesetting: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    menu: null,          // 鑿滃崟淇℃伅
+    formlist: null,      // 琛ㄥ崟淇℃伅
+    visible: false,      // 妯℃�佹鎺у埗
+    loading: false       // 璁剧疆淇℃伅楠岃瘉淇濆瓨涓�
+  }
+
+  /**
+   * @description 鍏ㄥ眬璁剧疆瑙﹀彂
+   */
+  changeSetting = () => {
+    const { menuformRef, MenuID, config, permFuncField } = this.props
+    let menu = {MenuID: MenuID}
+
+    if (menuformRef) {
+      menu = {MenuID: MenuID, MenuName: menuformRef.props.form.getFieldValue('MenuName') || '', MenuNo: menuformRef.props.form.getFieldValue('MenuNo') || ''}
+    }
+
+    let _config = fromJS(config).toJS()
+
+    this.setState({
+      visible: true,
+      formlist: getTreeSettingForm(config.setting, permFuncField, MenuID),
+      menu: menu,
+      config: _config
+    })
+  }
+
+  /**
+   * @description 淇濆瓨椤甸潰閰嶇疆淇℃伅
+   */
+  settingSave = () => {
+    const { config } = this.props
+
+    this.setState({
+      loading: true
+    })
+    this.settingRef.handleConfirm().then(res => {
+      this.setState({
+        visible: false,
+        loading: false
+      })
+      this.props.updatesetting({...config, setting: res})
+    }, () => {
+      this.setState({
+        loading: false
+      })
+    })
+  }
+
+  /**
+   * @description 鍒涘缓琛ㄦ牸瀛樺偍杩囩▼
+   */
+  tableCreatFunc = () => {
+    const { config } = this.props
+    const { menu } = this.state
+
+    this.settingRef.handleConfirm().then(setting => {
+
+      if (!(setting.interType === 'inner') || !setting.innerFunc) {
+        notification.warning({
+          top: 92,
+          message: '鎺ュ彛绫诲瀷涓�-鍐呴儴锛屼笖瀛樺湪鍐呴儴鍑芥暟鏃讹紝鎵嶅彲浠ュ垱寤哄瓨鍌ㄨ繃绋嬶紒',
+          duration: 5
+        })
+        return
+      }
+
+      let _config = {...config, setting: setting}
+      let newLText = Utils.formatOptions(Utils.getTableFunc(setting, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
+      let DelText = Utils.formatOptions(Utils.dropfunc(setting.innerFunc))          // 鍒犻櫎瀛樺偍杩囩▼sql
+
+      this.refs.funcCreatComponent.exec(setting.innerFunc, newLText, DelText).then(result => {
+        if (result === 'success') {
+          this.props.updatesetting(_config)
+        }
+      })
+    })
+  }
+
+  /**
+   * @description 鍒涘缓琛ㄦ牸鎺ュ彛锛堣鍑猴級
+   */
+  tableCreatInterface = () => {
+    const { config } = this.props
+    const { menu } = this.state
+
+    this.settingRef.handleConfirm().then(setting => {
+      if (setting.interType !== 'inner' || setting.innerFunc) {
+        notification.warning({
+          top: 92,
+          message: '鎺ュ彛绫诲瀷涓�-鍐呴儴锛屼笖涓嶅瓨鍦ㄥ唴閮ㄥ嚱鏁版椂锛屾墠鍙互鍒涘缓鎺ュ彛锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let _config = {...config, setting: setting}
+      let _menu = {
+        type: 'main',
+        MenuID: menu.MenuID,
+        menuName: menu.MenuName,
+        menuNo: menu.MenuNo
+      }
+
+      this.refs.tableCreatInterface.triggerOutInterface(_menu, _config)
+    })
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  render() {
+    const { dict, visible, config } = this.state
+
+    return (
+      <div className="model-tree-menu-setting">
+        <Icon type="setting" onClick={this.changeSetting} />
+        {/* 璁剧疆鍏ㄥ眬閰嶇疆鍙婂垪琛ㄦ暟鎹簮 */}
+        <Modal
+          wrapClassName="model-tree-setting-verify-modal"
+          title={dict['model.edit']}
+          visible={visible}
+          width={900}
+          maskClosable={false}
+          onCancel={() => { this.setState({ visible: false })}}
+          footer={[
+            <CreateInterface key="interface" dict={dict} ref="tableCreatInterface" trigger={this.tableCreatInterface}/>,
+            <CreateFunc key="create" dict={dict} ref="funcCreatComponent" trigger={this.tableCreatFunc}/>,
+            <Button key="cancel" onClick={() => { this.setState({ visible: false }) }}>{this.state.dict['header.cancel']}</Button>,
+            <Button key="confirm" type="primary" loading={this.state.loading} onClick={this.settingSave}>{this.state.dict['model.confirm']}</Button>
+          ]}
+          destroyOnClose
+        >
+          <SettingForm
+            dict={dict}
+            config={config}
+            menu={this.state.menu}
+            inputSubmit={this.settingSave}
+            formlist={this.state.formlist}
+            wrappedComponentRef={(inst) => this.settingRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default TreeSettingComponent
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/index.scss b/src/templates/sharecomponent/treesettingcomponent/index.scss
new file mode 100644
index 0000000..89b8f64
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/index.scss
@@ -0,0 +1,42 @@
+.model-tree-menu-setting {
+  > .anticon-setting {
+    position: absolute;
+    font-size: 18px;
+    right: 7px;
+    top: 5px;
+    padding: 10px;
+    z-index: 1;
+    color: #1890ff;
+  }
+}
+
+.model-tree-setting-verify-modal {
+  .ant-modal {
+    top: 50px;
+    padding-bottom: 5px;
+    .ant-modal-body {
+      max-height: calc(100vh - 190px);
+      overflow-y: auto;
+    }
+    .ant-modal-body::-webkit-scrollbar {
+      width: 7px;
+    }
+    .ant-modal-body::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+      background: rgba(0, 0, 0, 0.13);
+    }
+    .ant-modal-body::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
+    .ant-empty-normal {
+      margin: 5px 0px;
+    }
+    .ant-input-number {
+      width: 100%;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.jsx
new file mode 100644
index 0000000..1a81170
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.jsx
@@ -0,0 +1,250 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Button, notification, Modal, Select } from 'antd'
+import moment from 'moment'
+
+import Utils from '@/utils/utils.js'
+import SettingUtils from '../utils.jsx'
+import Api from '@/api'
+import './index.scss'
+
+const { TextArea } = Input
+
+class CustomForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,         // 瀛楀吀椤�
+    setting: PropTypes.object,      // 璁剧疆
+    systemScripts: PropTypes.array, // 绯荤粺鑴氭湰
+    scriptsChange: PropTypes.func   // 琛ㄥ崟
+  }
+
+  state = {
+    editItem: null,
+    loading: false,
+  }
+
+  UNSAFE_componentWillMount() {
+
+  }
+
+  edit = (record) => {
+    this.setState({
+      editItem: record
+    })
+
+    this.props.form.setFieldsValue({
+      sql: record.sql
+    })
+  }
+
+  handleCancel = () => {
+    this.setState({
+      editItem: null
+    })
+    this.props.form.setFieldsValue({
+      sql: ''
+    })
+  }
+
+  handleConfirm = () => {
+    const { setting } = this.props
+    
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (!err) {
+        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
+
+        let _quot = values.sql.match(/'{1}/g)
+        let _lparen = values.sql.match(/\({1}/g)
+        let _rparen = values.sql.match(/\){1}/g)
+
+        _quot = _quot ? _quot.length : 0
+        _lparen = _lparen ? _lparen.length : 0
+        _rparen = _rparen ? _rparen.length : 0
+
+        if (_quot % 2 !== 0) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓璡'蹇呴』鎴愬鍑虹幇',
+            duration: 5
+          })
+          return
+        } else if (_lparen !== _rparen) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓�()蹇呴』鎴愬鍑虹幇',
+            duration: 5
+          })
+          return
+        } else if (/--/ig.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/',
+            duration: 5
+          })
+          return
+        }
+
+        let error = Utils.verifySql(values.sql, 'customscript')
+
+        if (error) {
+          notification.warning({
+            top: 92,
+            message: 'sql涓笉鍙娇鐢�' + error,
+            duration: 5
+          })
+          return
+        }
+
+        let _customScript = ''
+        setting.scripts && setting.scripts.forEach(script => {
+          if (this.state.editItem && this.state.editItem.uuid === script.uuid) {
+            _customScript += `
+            ${values.sql}
+            `
+          } else if (script.status !== 'false') {
+            _customScript += `
+            ${script.sql}
+            `
+          }
+        })
+
+        if (!this.state.editItem) {
+          _customScript += `
+            ${values.sql}
+            `
+        }
+
+        if (_customScript) {
+          _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
+            ${_customScript}
+          `
+        }
+
+        let _setting = {...setting, customScript: _customScript}
+
+        let param = {
+          func: 's_debug_sql',
+          LText: SettingUtils.getDebugSql(_setting)
+        }
+
+        param.LText = Utils.formatOptions(param.LText)
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+        
+        this.setState({loading: true})
+        Api.getLocalConfig(param).then(res => {
+          if (res.status) {
+            this.setState({
+              loading: false,
+              editItem: null
+            }, () => {
+              this.props.scriptsChange(values)
+            })
+            this.props.form.setFieldsValue({
+              sql: ''
+            })
+          } else {
+            this.setState({loading: false})
+
+            Modal.error({
+              title: res.message
+            })
+          }
+        })
+      }
+    })
+  }
+
+  selectScript = (value, option) => {
+    let _sql = this.props.form.getFieldValue('sql')
+    if (_sql) {
+      _sql = _sql + ` 
+
+      `
+    }
+
+    _sql = _sql.replace(/\s{6}$/, '')
+    _sql = _sql + `/*${option.props.children}*/
+    `
+    _sql = _sql.replace(/\s{4}$/, '')
+    _sql = _sql + value
+
+    this.props.form.setFieldsValue({
+      sql: _sql
+    })
+  }
+
+  render() {
+    const { systemScripts, setting } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="modal-tree-menu-setting-script">
+        <Row gutter={24}>
+          {setting.tableName ? <Col span={8}>
+            <Form.Item label={'琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+              {setting.tableName}
+            </Form.Item>
+          </Col> : null}
+          <Col span={16}>
+            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+              ErrorCode, retmsg
+            </Form.Item>
+          </Col>
+          <Col span={24} className="sqlfield">
+            <Form.Item label={'鍙敤瀛楁'}>
+              id, bid, loginuid, sessionuid, userid, appkey, time_id, orderBy, {setting.valueField}, {setting.labelField}, {setting.parentField}
+            </Form.Item>
+          </Col>
+          <Col span={10}>
+            <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
+              <Select
+                showSearch
+                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                onChange={this.selectScript}
+              >
+                {systemScripts.map((option, i) =>
+                  <Select.Option style={{whiteSpace: 'normal'}} key={i} value={option.value}>{option.name}</Select.Option>
+                )}
+              </Select>
+            </Form.Item>
+          </Col>
+          <Col span={6} className="add">
+            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginTop: 5, marginBottom: 15, marginLeft: 30}}>
+              淇濆瓨
+            </Button>
+            <Button onClick={this.handleCancel} style={{marginTop: 5, marginBottom: 15, marginLeft: 10}}>
+              鍙栨秷
+            </Button>
+          </Col>
+          <Col span={24} className="sql">
+            <Form.Item label={'sql'}>
+              {getFieldDecorator('sql', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: this.props.dict['form.required.input'] + 'sql!'
+                  }
+                ]
+              })(<TextArea rows={15} />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.scss b/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.scss
new file mode 100644
index 0000000..d453a92
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/customscript/index.scss
@@ -0,0 +1,31 @@
+.modal-tree-menu-setting-script {
+  .sqlfield {
+    .ant-form-item {
+      margin-bottom: 5px;
+    }
+    .ant-form-item-control {
+      line-height: 24px;
+    }
+    .ant-form-item-label {
+      line-height: 25px;
+    }
+    .ant-form-item-children {
+      line-height: 22px;
+    }
+    .ant-col-sm-8 {
+      width: 10.5%;
+    }
+    .ant-col-sm-16 {
+      width: 89.5%;
+    }
+  }
+  .sql {
+    .ant-col-sm-8 {
+      width: 10.5%;
+    }
+    .ant-col-sm-16 {
+      width: 89.5%;
+      padding-top: 4px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
new file mode 100644
index 0000000..c9f9b3f
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
@@ -0,0 +1,765 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, Row, Col, Input, Radio, Select, Tooltip, Icon, notification, InputNumber, Modal, Table, Popconfirm, Typography, Button } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import SettingUtils from './utils.jsx'
+import CustomScript from './customscript'
+import './index.scss'
+
+const { TextArea } = Input
+const { confirm } = Modal
+const { Paragraph } = Typography
+
+class SettingForm extends Component {
+  static propTpyes = {
+    type: PropTypes.string,      // 鑿滃崟绫诲瀷
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    menu: PropTypes.object,      // 鑿滃崟淇℃伅
+    config: PropTypes.object,    // 椤甸潰閰嶇疆淇℃伅
+    formlist: PropTypes.array,   // 琛ㄥ崟淇℃伅
+    inputSubmit: PropTypes.any   // 鍥炶溅鎻愪氦浜嬩欢
+  }
+
+  state = {
+    formlist: [],
+    btnloading: false,
+    setting: null,
+    view: 'normal',
+    systemScripts: [{
+      name: '榛樿sql',
+      value: ''
+    }],
+    scriptsColumns: [
+      {
+        title: 'SQL',
+        dataIndex: 'sql',
+        width: '70%',
+        render: (text) => (
+          <Paragraph copyable ellipsis={{ rows: 5, expandable: true }}>{text}</Paragraph>
+        )
+      },
+      {
+        title: '鐘舵��',
+        dataIndex: 'status',
+        width: '10%',
+        render: (text, record) => record.status === 'false' ?
+          (
+            <div>
+              {this.props.dict['header.form.status.forbidden']}
+              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
+            </div>
+          ) :
+          (
+            <div>
+              {this.props.dict['header.form.status.open']}
+              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
+            </div>
+          )
+      },
+      {
+        title: '鎿嶄綔',
+        align: 'center',
+        width: '20%',
+        dataIndex: 'operation',
+        render: (text, record) =>
+          (<div>
+            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record)} style={{color: '#1890ff'}}><Icon type="edit" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.up']} onClick={() => this.handleUpDown(record, 'up')} style={{color: '#1890ff'}}><Icon type="arrow-up" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.down']} onClick={() => this.handleUpDown(record, 'down')} style={{color: '#ff4d4f'}}><Icon type="arrow-down" /></span>
+            <span className="operation-btn" title={this.props.dict['header.form.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
+            <Popconfirm
+              title={this.props.dict['header.form.query.delete']}
+              okText={this.props.dict['model.confirm']}
+              cancelText={this.props.dict['header.cancel']}
+              onConfirm={() => this.handleDelete(record)
+            }>
+              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
+            </Popconfirm>
+          </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount() {
+    let _formlist = fromJS(this.props.formlist).toJS()
+    let interType = 'inner'
+
+    _formlist.forEach(item => {
+      if (item.key === 'interType') {
+        interType = item.initVal
+      }
+    })
+
+    let _setting = fromJS(this.props.config.setting).toJS()
+    _setting.scripts = _setting.scripts || []
+    _setting.default = _setting.default || 'true'
+
+    this.setState({
+      setting: _setting,
+      formlist: _formlist.map(item => {
+        if (interType === 'inner' && ['sysInterface', 'interface', 'outerFunc'].includes(item.key)) {
+          item.hidden = true
+        } else if (interType === 'outer' && ['innerFunc', 'dataresource', 'queryType'].includes(item.key)) {
+          item.hidden = true
+        }
+
+        return item
+      })
+    })
+  }
+
+  componentDidMount () {
+    this.getsysScript()
+  }
+
+  getsysScript = () => {
+    let _scriptSql = `Select distinct func+Remark as funcname,longparam, s.Sort from聽 s_custom_script s inner join (select OpenID from sapp where ID=@Appkey@) p on s.openid = case when s.appkey='' then s.openid else p.OpenID end order by s.Sort`
+
+    _scriptSql = Utils.formatOptions(_scriptSql)
+
+    let _sParam = {
+      func: 'sPC_Get_SelectedList',
+      LText: _scriptSql,
+      obj_name: 'data',
+      arr_field: 'funcname,longparam'
+    }
+    
+    _sParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+    _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp)
+    
+    Api.getSystemConfig(_sParam).then(res => {
+      if (res.status) {
+        let _scripts = res.data.map(item => {
+          let _item = {
+            name: item.funcname,
+            value: Utils.formatOptions(item.longparam, true)
+          }
+
+          return _item
+        })
+
+        this.setState({
+          systemScripts: [...this.state.systemScripts, ..._scripts]
+        })
+      } else {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  handleConfirm = (otype) => {
+    const { menu } = this.props
+    const { view, setting } = this.state
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+
+    if (view !== 'custom') {
+      return new Promise((resolve, reject) => {
+        this.props.form.validateFieldsAndScroll((err, values) => {
+          if (!err) {
+            values = {...setting, ...values}
+
+            // 鏁版嵁婧愬墠绔獙璇�
+            if (values.interType === 'inner' && !values.innerFunc && values.default !== 'false' && !values.dataresource) {
+              notification.warning({
+                top: 92,
+                message: '璇峰~鍐欏唴閮ㄥ嚱鏁版垨鏁版嵁婧愶紒',
+                duration: 5
+              })
+              reject()
+              return
+            } else if (values.interType === 'inner' && !values.innerFunc && values.default !== 'false' && values.dataresource) {
+              let error = Utils.verifySql(values.dataresource)
+  
+              if (error) {
+                notification.warning({
+                  top: 92,
+                  message: '鏁版嵁婧愪腑涓嶅彲浣跨敤' + error,
+                  duration: 5
+                })
+                reject()
+                return
+              }
+            }
+
+            // 鏁版嵁婧愪繚瀛�
+            if (
+              values.interType === 'inner' && !values.innerFunc &&
+              values.default !== 'false' &&
+              /[^\s]+\s+[^\s]+/ig.test(values.dataresource) &&
+              this.props.config.setting.dataresource !== values.dataresource
+            ) {
+              let param = {
+                func: 's_DataSrc_Save',
+                LText: values.dataresource,
+                MenuID: menu.MenuID
+              }
+      
+              param.LText = Utils.formatOptions(param.LText)
+              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+              param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+      
+              Api.getLocalConfig(param)
+            }
+
+            if (otype === 'change') {
+              this.setState({
+                setting: values,
+              }, () => {
+                resolve()
+              })
+            } else {
+              values.customScript = this.getCustomScript(values)
+
+              this.sqlverify(values, resolve, reject)
+            }
+          } else {
+            reject(err)
+          }
+        })
+      })
+    } else {
+      let _setting = fromJS(this.state.setting).toJS()
+      _setting.customScript = this.getCustomScript(_setting)
+
+      let _this = this
+
+      return new Promise((resolve, reject) => {
+        if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+          confirm({
+            content: `瀛樺湪鏈繚瀛橀」锛岀‘瀹氭彁浜ゅ悧锛焋,
+            okText: this.props.dict['model.confirm'],
+            cancelText: this.props.dict['header.cancel'],
+            onOk() {
+              _this.sqlverify(_setting, resolve, reject)
+            },
+            onCancel() {
+              reject()
+            }
+          })
+        } else {
+          this.sqlverify(_setting, resolve, reject)
+        }
+      })
+    }
+  }
+
+  getCustomScript = (setting) => {
+    let _customScript = ''
+    if (setting.scripts && setting.scripts.length > 0) {
+      setting.scripts.forEach(item => {
+        if (item.status === 'false') return
+        _customScript += `
+          ${item.sql}
+        `
+      })
+    }
+
+    if (_customScript) {
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
+        ${_customScript}
+      `
+    }
+
+    return _customScript
+  }
+
+  sqlverify = (_setting, _resolve, _reject, isChange = false) => {
+    if (!isChange && _setting.interType === 'inner' && !_setting.innerFunc && _setting.default === 'false' && !_setting.customScript) {
+      notification.warning({
+        top: 92,
+        message: '涓嶆墽琛岄粯璁ql鏃讹紝璇锋坊鍔犺嚜瀹氫箟鑴氭湰锛�',
+        duration: 5
+      })
+      _reject()
+      return
+    }
+
+    if (_setting.interType === 'inner' && !_setting.innerFunc && _setting.default !== 'false') {
+      let param = {
+        func: 's_debug_sql',
+        LText: SettingUtils.getDebugSql(_setting)
+      }
+      param.LText = Utils.formatOptions(param.LText)
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+      
+      Api.getLocalConfig(param).then(result => {
+        if (result.status) {
+          _resolve(_setting)
+        } else {
+          _reject()
+          Modal.error({
+            title: result.message
+          })
+        }
+      })
+    } else {
+      _resolve(_setting)
+    }
+  }
+
+  selectChange = (key, val) => {
+    if (key === 'primaryKey' && val) {
+      this.props.form.setFieldsValue({
+        order: `${val} desc`
+      })
+    }
+  }
+
+  onRadioChange = (e, key) => {
+    let value = e.target.value
+    let _formlist = fromJS(this.state.formlist).toJS()
+
+    if (key === 'interType') {
+      this.setState({
+        formlist: _formlist.map(item => {
+          item.hidden = false
+
+          if (value === 'inner' && ['sysInterface', 'interface', 'outerFunc'].includes(item.key)) {
+            item.initVal = this.props.form.getFieldValue(item.key)
+            item.hidden = true
+          } else if (value === 'outer' && ['innerFunc', 'dataresource', 'queryType'].includes(item.key)) {
+            item.initVal = this.props.form.getFieldValue(item.key)
+            item.hidden = true
+          }
+          
+          return item
+        })
+      })
+    } else if (key === 'sysInterface') {
+      if (value === 'true') {
+        this.props.form.setFieldsValue({
+          interface: window.GLOB.mainSystemApi || ''
+        })
+      }
+      this.setState({
+        formlist: _formlist.map(item => {
+          if (item.key === 'interface') {
+            item.readonly = value === 'true'
+          }
+          
+          return item
+        })
+      })
+    }
+  }
+
+  changeView = () => {
+    const { view } = this.state
+    let _this = this
+
+    if (view === 'normal') {
+      this.handleConfirm('change').then(() => {
+        const { setting } = this.state
+
+        if (setting.interType !== 'inner' || (setting.interType === 'inner' && setting.innerFunc)) {
+          notification.warning({
+            top: 92,
+            message: '浣跨敤澶栭儴鎺ュ彛鎴栧唴閮ㄦ帴鍙g殑鑷畾涔夊嚱鏁帮紝涓嶅彲娣诲姞鑷畾涔夎缃紒',
+            duration: 5
+          })
+          return
+        }
+
+        let _dataresource = setting.dataresource
+
+        if (/\s/.test(_dataresource)) {
+          _dataresource = '(' + _dataresource + ') tb'
+        }
+
+        let arr_field = `${setting.valueField}, ${setting.labelField}, ${setting.parentField}`
+
+        let LText = `select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by @orderBy@) as rows from ${_dataresource}) tmptable order by tmptable.rows`
+        let _scripts = fromJS(this.state.systemScripts).toJS()
+        _scripts[0].value = LText
+
+
+        if (setting.default === 'false') {
+          this.setState({
+            view: 'custom',
+            btnloading: false,
+            systemScripts: _scripts
+          })
+          this.scrolltop()
+        } else {
+          this.setState({
+            btnloading: true
+          })
+          new Promise((resolve, reject) => {
+            this.sqlverify(setting, resolve, reject, true)
+          }).then(() => {
+            this.setState({
+              view: 'custom',
+              btnloading: false,
+              systemScripts: _scripts
+            })
+            this.scrolltop()
+          }, () => {
+            this.setState({
+              btnloading: false
+            })
+          })
+        }
+      })
+    } else {
+      let _loading = false
+
+      if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+        _loading = true
+      }
+
+      if (_loading) {
+        confirm({
+          content: `瀛樺湪鏈繚瀛橀」锛岀‘瀹氬垏鎹㈠悧锛焋,
+          okText: this.props.dict['model.confirm'],
+          cancelText: this.props.dict['header.cancel'],
+          onOk() {
+            _this.setState({
+              view: 'normal'
+            })
+    
+            _this.scrolltop()
+          },
+          onCancel() {}
+        })
+      } else {
+        _this.setState({
+          view: 'normal'
+        })
+
+        _this.scrolltop()
+      }
+    }
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  getFields(formlist) {
+    const { getFieldDecorator } = this.props.form
+    const fields = []
+
+    formlist.forEach((item, index) => {
+      if (item.hidden || item.forbid) return
+
+      if (item.type === 'text') { // 鏂囨湰鎼滅储
+        let rules = item.rules || []
+        
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement={item.placement || 'topLeft'} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  },
+                  ...rules
+                ]
+              })(<Input placeholder={item.placeholder || ''} autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'number') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || 6,
+                rules: [
+                  {
+                    required: item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<InputNumber min={item.min} max={item.max} precision={0} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'select') { // 涓嬫媺鎼滅储
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.label}>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Select
+                  showSearch
+                  filterOption={(input, option) => {
+                    return option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0 ||
+                      option.props.value.toLowerCase().indexOf(input.toLowerCase()) >= 0
+                  }}
+                  onChange={(value) => {this.selectChange(item.key, value)}}
+                  getPopupContainer={() => document.getElementById('model-table-setting-form')}
+                >
+                  {item.options.map((option, i) =>
+                    <Select.Option id={i} key={i} value={option.value}>
+                      {option.text}
+                    </Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'radio') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Radio.Group onChange={(e) => {this.onRadioChange(e, item.key)}}>
+                  {
+                    item.options.map((option, i) => {
+                      return (
+                        <Radio key={i} value={option.value}>{option.text}</Radio>
+                      )
+                    })
+                  }
+                </Radio.Group>,
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'datasource') {
+        fields.push(
+          <Col span={24} key={index} style={{paddingLeft: '7px'}}>
+            <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} } help={item.help} label={
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip>
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal
+              })(<TextArea rows={4} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'textarea') {
+        fields.push(
+          <Col span={20} offset={4} key={index}>
+            <Form.Item className="text-area">
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={4} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'multiselect') { // 澶氶��
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.label}>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || []
+              })(
+                <Select
+                  showSearch
+                  mode="multiple"
+                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                >
+                  {item.options.map((option, i) =>
+                    <Select.Option id={i} key={i} value={option.value}>{option.text}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      }
+    })
+
+    return fields
+  }
+
+  handleEdit = (record) => {
+    this.scriptsForm.edit(record)
+
+    this.scrolltop()
+  }
+
+  scrolltop = () => {
+    let node = document.getElementById('model-tree-setting-form-box').parentNode
+
+    if (node && node.scrollTop) {
+      let inter = Math.ceil(node.scrollTop / 10)
+
+      let timer = setInterval(() => {
+        if (node.scrollTop - inter > 0) {
+          node.scrollTop = node.scrollTop - inter
+        } else {
+          node.scrollTop = 0
+          clearInterval(timer)
+        }
+      }, 10)
+    }
+  }
+
+  handleUpDown = (record, direction) => {
+    let scripts = fromJS(this.state.setting.scripts).toJS()
+    let index = 0
+
+    scripts = scripts.filter((item, i) => {
+      if (item.uuid === record.uuid) {
+        index = i
+      }
+
+      return item.uuid !== record.uuid
+    })
+    if ((index === 0 && direction === 'up') || (index === scripts.length && direction === 'down')) {
+      return
+    }
+
+    if (direction === 'up') {
+      scripts.splice(index - 1, 0, record)
+    } else {
+      scripts.splice(index + 1, 0, record)
+    }
+
+    this.setState({
+      setting: {...this.state.setting, scripts: scripts}
+    })
+  }
+
+  handleStatus = (record) => {
+    let scripts = fromJS(this.state.setting.scripts).toJS()
+    record.status = record.status === 'false' ? 'true' : 'false'
+
+    scripts = scripts.map(item => {
+      if (item.uuid === record.uuid) {
+        return record
+      } else {
+        return item
+      }
+    })
+
+    this.setState({
+      setting: {...this.state.setting, scripts: scripts}
+    })
+  }
+
+  handleDelete = (record) => {
+    let scripts = fromJS(this.state.setting.scripts).toJS()
+    scripts = scripts.filter(item => item.uuid !== record.uuid)
+
+    this.setState({ setting: {...this.state.setting, scripts: scripts} })
+  }
+
+  scriptsChange = (values) => {
+    let scripts = fromJS(this.state.setting.scripts).toJS()
+
+    if (values.uuid) {
+      scripts = scripts.map(item => {
+        if (item.uuid === values.uuid) {
+          return values
+        } else {
+          return item
+        }
+      })
+    } else {
+      values.uuid = Utils.getuuid()
+      scripts.push(values)
+    }
+
+    this.setState({
+      setting: {...this.state.setting, scripts: scripts}
+    })
+  }
+
+  render() {
+    const { config, type } = this.props
+    const { formlist, view, setting, scriptsColumns, systemScripts, btnloading } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div className="model-tree-setting-form-box" id="model-tree-setting-form-box">
+        {view ==='custom' ? <div>
+          <Icon className="setting-custom-back" onClick={this.changeView} type="arrow-left" />
+          <CustomScript
+            type={type}
+            setting={setting}
+            dict={this.props.dict}
+            searches={config.search}
+            systemScripts={systemScripts}
+            scriptsChange={this.scriptsChange}
+            wrappedComponentRef={(inst) => this.scriptsForm = inst}
+          />
+          <Table
+            bordered
+            rowKey="uuid"
+            className="custom-table"
+            dataSource={setting.scripts}
+            columns={scriptsColumns}
+            pagination={false}
+          />
+        </div> : null }
+        <Form {...formItemLayout} className="model-tree-setting-form" id="model-table-setting-form">
+          {view !=='custom' ? <Row gutter={24}>{this.getFields(formlist)}</Row> : null}
+          <Row gutter={24}>
+            {view !=='custom' ? <Button onClick={this.changeView} className="to-custom-script" loading={btnloading}>鑷畾涔夎缃�<Icon style={{marginLeft: 5}} type="right" /></Button> : null}
+            {view ==='custom' ? <span onClick={this.changeView} style={{float: 'left', color: '#1890ff', marginLeft: 12, marginTop: 15, cursor: 'pointer'}}><Icon style={{marginRight: 5}} type="left" />鍩虹璁剧疆</span> : null}
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/index.scss b/src/templates/sharecomponent/treesettingcomponent/settingform/index.scss
new file mode 100644
index 0000000..aa3e0b8
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/index.scss
@@ -0,0 +1,45 @@
+.model-tree-setting-form-box {
+  position: relative;
+  .model-tree-setting-form {
+    .textarea {
+      .ant-form-item-label {
+        width: 16.3%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 83.33333333%;
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+  }
+  .operation-btn {
+    display: inline-block;
+    font-size: 16px;
+    padding: 0 5px;
+    cursor: pointer;
+  }
+  td {
+    word-break: break-all;
+  }
+  .setting-custom-back {
+    position: absolute;
+    top: -20px;
+    left: -10px;
+    font-size: 16px;
+    z-index: 1;
+    cursor: pointer;
+    padding: 10px;
+    color: rgb(24, 144, 255);
+  }
+  .to-custom-script {
+    float: right;
+    color: #1890ff;
+    margin-right: 12px;
+    margin-top: 15px;
+    cursor: pointer;
+    border: 0;
+    box-shadow: unset;
+  }
+}
\ No newline at end of file
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
new file mode 100644
index 0000000..ae1a6b6
--- /dev/null
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
@@ -0,0 +1,51 @@
+
+export default class SettingUtils {
+  /**
+   * @description 鐢熸垚椤甸潰鏌ヨ璇彞
+   * @return {Object}  setting       椤甸潰璁剧疆
+   */
+  static getDebugSql (setting) {
+    let sql = ''
+    let _dataresource = setting.dataresource
+    let _customScript = setting.customScript
+
+    if (setting.interType === 'inner' && !setting.innerFunc && setting.default === 'false') {
+      _dataresource = ''
+    }
+    
+    if (_dataresource) {
+      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+    }
+    if (_customScript) {
+      _customScript = _customScript.replace(/@\$|\$@/ig, '')
+    }
+
+    if (_customScript) {
+      _customScript = _customScript.replace(/@orderBy@/ig, setting.order)
+    }
+
+    // 鏁版嵁婧愬鐞�
+    if (_dataresource) {
+      if (/\s/.test(_dataresource)) {
+        _dataresource = '(' + _dataresource + ') tb'
+      }
+
+      let arr_field = `${setting.valueField}, ${setting.labelField}, ${setting.parentField}`
+
+      _dataresource = `select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows`
+    }
+
+    if (_customScript) {
+      sql = `${_customScript}
+        ${_dataresource}
+        aaa:
+        if @ErrorCode!=''
+          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
+      `
+    } else {
+      sql = _dataresource
+    }
+    
+    return sql
+  }
+}
\ No newline at end of file
diff --git a/src/templates/subtableconfig/index.jsx b/src/templates/subtableconfig/index.jsx
index 991316a..217740f 100644
--- a/src/templates/subtableconfig/index.jsx
+++ b/src/templates/subtableconfig/index.jsx
@@ -33,7 +33,7 @@
 const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 const ChartGroupComponent = asyncComponent(() => import('@/templates/sharecomponent/chartgroupcomponent'))
 const ChartComponent = asyncComponent(() => import('@/templates/sharecomponent/chartcomponent'))
-// const CardComponent = asyncComponent(() => import('@/templates/sharecomponent/cardcomponent'))
+const CardComponent = asyncComponent(() => import('@/templates/sharecomponent/cardcomponent'))
 
 class SubTableConfig extends Component {
   static propTpyes = {
@@ -1169,6 +1169,16 @@
                         />
                       </Col>
                     )
+                  } else if (item.chartType === 'card') {
+                    return (
+                      <Col span={item.width} key={item.uuid}>
+                        <CardComponent
+                          card={item}
+                          config={config}
+                          plotchange={this.updateconfig}
+                        />
+                      </Col>
+                    )
                   } else {
                     return (
                       <Col span={item.width} key={item.uuid}>
diff --git a/src/templates/treepageconfig/index.jsx b/src/templates/treepageconfig/index.jsx
new file mode 100644
index 0000000..61dd4a9
--- /dev/null
+++ b/src/templates/treepageconfig/index.jsx
@@ -0,0 +1,921 @@
+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, Row, Col, Tree } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import { getMainMenuForm } from '@/templates/zshare/formconfig'
+
+import asyncComponent from '@/utils/asyncComponent'
+
+import MenuForm from '@/templates/zshare/menuform'
+import EditComponent from '@/templates/zshare/editcomponent'
+import SourceElement from '@/templates/zshare/dragsource'
+import Source from './source'
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+const { TreeNode } = Tree
+const TreeSettingComponent = asyncComponent(() => import('@/templates/sharecomponent/treesettingcomponent'))
+const TabsComponent = asyncComponent(() => import('@/templates/sharecomponent/tabscomponent'))
+
+class ComTableConfig extends Component {
+  static propTpyes = {
+    menu: PropTypes.any,
+    optionLibs: PropTypes.any,
+    reloadmenu: PropTypes.func,
+    handleView: PropTypes.func
+  }
+
+  state = {
+    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    config: null,            // 椤甸潰閰嶇疆
+    menuformlist: null,      // 鍩烘湰淇℃伅琛ㄥ崟瀛楁
+    formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
+    menuloading: false,      // 鑿滃崟淇濆瓨涓�
+    menucloseloading: false, // 鑿滃崟鍏抽棴鏃讹紝閫夋嫨淇濆瓨
+    loading: false,          // 鍔犺浇涓紝椤甸潰spin
+    closeVisible: false,     // 鍏抽棴妯℃�佹
+    tables: [],              // 鍙敤琛ㄥ悕
+    originMenu: null,        // 鍘熷鑿滃崟
+    delTabs: [],             // 鍒犻櫎鏍囩鍒楄〃
+    tabviews: [],            // 鎵�鏈夋爣绛鹃〉
+    optionLibs: null,        // 鑷畾涔変笅鎷夐�夐」搴�
+    activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
+    pasteContent: null,      // 绮樿创閰嶇疆淇℃伅
+    openEdition: ''          // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
+  }
+
+  /**
+   * @description 鏁版嵁棰勫鐞�
+   * 1銆佽缃〉闈㈤厤缃俊鎭紝鏂板缓鎴栨棤閰嶇疆淇℃伅鏃讹紙鍒囨崲妯℃澘鍚庢棤閰嶇疆淇℃伅锛夛紝浣跨敤妯℃澘榛樿閰嶇疆
+   * 2銆佽缃搷浣滅被鍨嬨�佸師濮嬭彍鍗曚俊鎭紙姣忔淇濆瓨鍚庨噸缃級銆佸凡浣跨敤琛ㄥ強鍩烘湰淇℃伅琛ㄥ崟
+   */
+  UNSAFE_componentWillMount () {
+    const { menu, optionLibs } = this.props
+
+    let _LongParam = menu.LongParam
+    let _config = ''
+
+    if (!_LongParam) {
+      _config = fromJS(Source.baseConfig).toJS()
+      _config.isAdd = true
+    } else {
+      _config = _LongParam
+    }
+
+    // 閰嶇疆榛樿鍊硷紝鍏煎
+    _config.Template = 'TreePage'
+    _config.easyCode = _config.easyCode || ''
+    
+    if (_config.type === 'user') {
+      // 閲嶇疆鏍囩ID
+      _config.tabgroups.forEach(group => {
+        group.sublist = group.sublist.map(tab => {
+          tab.uuid = Utils.getuuid()
+          
+          if (tab.linkTab) {
+            tab.linkTab = ''
+          }
+
+          return tab
+        })
+      })
+    }
+
+    this.setState({
+      config: _config,
+      openEdition: menu.open_edition || '',
+      activeKey: menu.activeKey || '0',
+      optionLibs: optionLibs,
+      originMenu: fromJS(menu).toJS(),
+      menuformlist: getMainMenuForm(menu, _config)
+    })
+  }
+
+  /**
+   * @description 鍔犺浇瀹屾垚鍚�, 鑾峰彇鎵�鏈夋爣绛鹃〉淇℃伅
+   */
+  componentDidMount () {
+    this.reloadTab(false)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 鍔犺浇鎴栧埛鏂版爣绛句俊鎭�
+   */
+  reloadTab = (type) => {
+    this.setState({
+      loading: type,
+      tabviews: []
+    })
+    Api.getSystemConfig({func: 'sPC_Get_UserTemp', TypeCharTwo: 'tab'}).then(res => {
+      if (res.status) {
+        this.setState({
+          loading: false,
+          tabviews: res.UserTemp.map(temp => {
+            return {
+              uuid: temp.MenuID,
+              value: temp.MenuID,
+              text: temp.MenuName,
+              type: temp.Template,
+              MenuNo: temp.MenuNo
+            }
+          })
+        })
+
+        if (type) {
+          notification.success({
+            top: 92,
+            message: '鍒锋柊鎴愬姛銆�',
+            duration: 2
+          })
+        }
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  getFuncNames = (data, funcNames, tableNames) => {
+    data.forEach(item => {
+      if (item.subfuncs) {
+        this.getFuncNames(item.subfuncs, funcNames, tableNames)
+      } else {
+        if (item.tableName) {
+          tableNames.push(item.tableName)
+        }
+        if (item.innerFunc) {
+          funcNames.push({func: item.innerFunc, label: item.label || ''})
+        }
+
+        if (item.callbackFunc) {
+          funcNames.push({func: item.callbackFunc, label: item.label || ''})
+        }
+      }
+    })
+
+    return {
+      func: funcNames,
+      table: tableNames
+    }
+  }
+
+  /**
+   * @description 涓夌骇鑿滃崟淇濆瓨
+   */
+  submitConfig = () => {
+    const { menu } = this.props
+    const { originMenu, delTabs, openEdition } = this.state
+
+    let config = fromJS(this.state.config).toJS()
+
+    this.menuformRef.handleConfirm().then(res => {
+      if (config.isAdd) {
+        config.tabgroups[0].sublist = config.tabgroups[0].sublist.filter(item => !item.origin)
+      }
+
+      if (config.type === 'user') { // 浣跨敤宸叉湁鑿滃崟鏃讹紝榛樿娣诲姞鍏宠仈鏍囩id
+        config.tabgroups.forEach(group => {
+          group.sublist = group.sublist.map(tab => {
+            if (!tab.linkTab) {
+              tab.linkTab = Utils.getuuid()
+            }
+            return tab
+          })
+        })
+      }
+
+      let _LongParam = ''
+      let _config = {...config, easyCode: res.easyCode}
+      let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+
+      // 鏈缃暟鎹簮鎴栨爣绛句笉鍚堟硶鏃讹紝鍚敤鐘舵�佷负false
+      let vresult = this.verifyconfig(_config)
+      if (vresult !== true) {
+        _config.enabled = false
+      }
+
+      _config.funcs = [] // 椤甸潰鍙婂瓙椤甸潰瀛樺偍杩囩▼闆�
+
+      _config.funcs.push({
+        type: 'view',
+        subtype: 'view',
+        uuid: menu.MenuID,
+        intertype: _config.setting.interType || 'inner',
+        interface: _config.setting.interface || '',
+        tableName: _config.setting.tableName || '',
+        innerFunc: _config.setting.innerFunc || '',
+        outerFunc: _config.setting.outerFunc || ''
+      })
+
+      _config.tabgroups.forEach(group => {
+        group.sublist.forEach(tab => {
+          _config.funcs.push({
+            type: 'tab',
+            subtype: 'tab',
+            uuid: tab.uuid,
+            label: tab.label,
+            linkTab: tab.linkTab
+          })
+        })
+      })
+
+      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 銆乽ser
+        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 _sort = 0
+        let btntabs = []
+        
+        let tabParam = { // 娣诲姞鑿滃崟tab椤�
+          func: 'sPC_sMenusTab_AddUpt',
+          MenuID: menu.MenuID
+        }
+
+        let _LText = []
+
+        btntabs.forEach(item => {
+          _LText.push(`select '${item.uuid}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${item.sort * 10}' as Sort`)
+        })
+        _config.tabgroups.forEach(group => {
+          group.sublist.forEach(item => {
+            _sort++
+            _LText.push(`select '${menu.MenuID}' as MenuID ,'${item.linkTab}' as Tabid,'${item.label}' as TabName ,'${_sort * 10}' as Sort`)
+          })
+        })
+
+        _LText = _LText.join(' union all ')
+
+        tabParam.LText = Utils.formatOptions(_LText)
+        tabParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        tabParam.secretkey = Utils.encrypt(tabParam.LText, tabParam.timestamp)
+  
+        let _vals = this.getFuncNames(_config.funcs, [], [])
+        let _tables = Array.from(new Set(_vals.table))
+
+        let param = {
+          func: 'sPC_TrdMenu_AddUpt',
+          FstID: res.fstMenuId,
+          SndID: res.parentId,
+          ParentID: res.parentId,
+          MenuID: menu.MenuID,
+          MenuNo: res.MenuNo,
+          EasyCode: res.easyCode,
+          Template: menu.PageParam.Template || '',
+          MenuName: res.MenuName,
+          PageParam: JSON.stringify(_pageParam),
+          LongParam: _LongParam,
+          LText: _vals.func.map(item => `select '${menu.MenuID}' as MenuID,'${item.func}' as ProcName,'${item.label}' as MenuName`),
+          LTexttb: _tables.map(item => `select '${menu.MenuID}' as MenuID,'${item}' as tbName`)
+        }
+
+        if (menu.menuSort) { // 鑿滃崟鏂板缓鏃惰缃帓搴�
+          param.Sort = menu.menuSort
+        }
+
+        param.LText = param.LText.join(' union all ')
+        param.LText = Utils.formatOptions(param.LText)
+        param.LTexttb = param.LTexttb.join(' union all ')
+        param.LTexttb = Utils.formatOptions(param.LTexttb)
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+        param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+        if (openEdition) { // 鐗堟湰绠$悊
+          param.open_edition = openEdition
+        }
+
+        // 鏈夋寜閽垨鏍囩鍒犻櫎鏃讹紝鍏堣繘琛屽垹闄ゆ搷浣�
+        // 鍒犻櫎鎴愬姛鍚庯紝淇濆瓨椤甸潰閰嶇疆
+        new Promise(resolve => {
+          if (delTabs.length > 0) {
+            let deffers = delTabs.map(item => {
+              let _param = {
+                func: 'sPC_MainMenu_Del',
+                MenuID: item.uuid
+              }
+
+              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({
+                  delTabs: []
+                })
+                resolve(true)
+              }
+            })
+          } else if (delTabs.length === 0) {
+            resolve(true)
+          }
+        }).then(resp => {
+          if (resp === false) return
+          let localParam = fromJS(param).toJS()
+
+          Api.getSystemConfig(param).then(response => {
+            if (response.status) {
+              let _FMenu = originMenu.fstMenuList.filter(fstM => fstM.MenuID === res.fstMenuId)[0]
+              let _supMenuList = []
+              if (_FMenu) {
+                _supMenuList = _FMenu.options
+              }
+
+              this.setState({
+                config: _config,
+                openEdition: response.open_edition || '',
+                originMenu: {
+                  ...originMenu,
+                  LongParam: _config,
+                  PageParam: _pageParam,
+                  MenuName: res.MenuName,
+                  MenuNo: res.MenuNo,
+                  ParentID: res.parentId,
+                  fstMenuId: res.fstMenuId,
+                  supMenuList: _supMenuList
+                }
+              })
+
+              this.props.reloadmenu()
+              
+              // 瀛樺湪鏍囩椤垫椂
+              if (tabParam.LText) {
+                Api.getSystemConfig(tabParam).then(result => {
+                  if (result.status) {
+                    notification.success({
+                      top: 92,
+                      message: '淇濆瓨鎴愬姛',
+                      duration: 2
+                    })
+                    if (this.state.closeVisible) {
+                      this.props.handleView()
+                    } else {
+                      this.setState({
+                        menuloading: false,
+                        menucloseloading: false
+                      })
+                    }
+                  } else {
+                    notification.warning({
+                      top: 92,
+                      message: result.message,
+                      duration: 5
+                    })
+                    this.setState({
+                      menuloading: false,
+                      menucloseloading: false
+                    })
+                  }
+                })
+              } else {
+                notification.success({
+                  top: 92,
+                  message: '淇濆瓨鎴愬姛',
+                  duration: 2
+                })
+                if (this.state.closeVisible) {
+                  this.props.handleView()
+                } else {
+                  this.setState({
+                    menuloading: false,
+                    menucloseloading: false
+                  })
+                }
+              }
+
+              localParam.func = 'sPC_TrdMenu_AddUpt_For_Local'
+              delete localParam.LongParam
+              delete localParam.PageParam
+              delete localParam.Template
+              delete localParam.Sort
+              delete localParam.EasyCode
+              delete localParam.open_edition
+
+              Api.getLocalConfig(localParam)
+            } else {
+              this.setState({
+                menuloading: false,
+                menucloseloading: false
+              })
+              notification.warning({
+                top: 92,
+                message: response.message,
+                duration: 5
+              })
+            }
+          })
+        })
+      })
+    }, () => {
+      notification.warning({
+        top: 92,
+        message: this.state.dict['header.menu.basemsg'],
+        duration: 5
+      })
+    })
+  }
+
+  /**
+   * @description 鐐瑰嚮杩斿洖鏃讹紝鍒ゆ柇閰嶇疆淇濆瓨鐘舵��
+   */
+  cancelConfig = () => {
+    const { menu } = this.props
+    const { config, originMenu } = this.state
+
+    let _this = this
+
+    if (config.isAdd) {
+      confirm({
+        content: '鑿滃崟灏氭湭鎻愪氦锛岀‘瀹氭斁寮冧繚瀛樺悧锛�',
+        okText: this.state.dict['model.confirm'],
+        cancelText: this.state.dict['header.cancel'],
+        onOk() {
+          _this.props.handleView()
+        },
+        onCancel() {}
+      })
+    } else {
+      this.menuformRef.handleConfirm().then(res => {
+        let _config = {...config, easyCode: res.easyCode}
+        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+        let _originMenu = {
+          ...originMenu,
+          LongParam: _config,
+          PageParam: _pageParam,
+          MenuName: res.MenuName,
+          MenuNo: res.MenuNo,
+          ParentID: res.parentId,
+          fstMenuId: res.fstMenuId
+        }
+
+        if (!is(fromJS(originMenu), fromJS(_originMenu))) {
+          this.setState({
+            closeVisible: true
+          })
+        } else {
+          this.props.handleView()
+        }
+      }, () => {
+        this.setState({
+          closeVisible: true
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 璁剧疆鍙厤缃寜閽�
+   */
+  setSubConfig = (item, type) => {
+    const { menu } = this.props
+    const { config, originMenu, optionLibs, activeKey, openEdition } = this.state
+
+    if (!originMenu.MenuID) { // menuID涓嶅瓨鍦ㄦ椂锛屼负鏂板缓鑿滃崟锛屾彁绀鸿彍鍗曞皻鏈繚瀛�
+      notification.warning({
+        top: 92,
+        message: this.state.dict['header.menu.config.notsave'],
+        duration: 5
+      })
+    } else {
+      this.menuformRef.handleConfirm().then(res => {
+        let _config = {...config, easyCode: res.easyCode}
+        let _pageParam = {...menu.PageParam, OpenType: res.opentype}
+        let _originMenu = {
+          ...originMenu,
+          LongParam: _config,
+          PageParam: _pageParam,
+          MenuName: res.MenuName,
+          MenuNo: res.MenuNo,
+          ParentID: res.parentId,
+          fstMenuId: res.fstMenuId
+        }
+
+        if (!is(fromJS(originMenu), fromJS(_originMenu))) { // 鑿滃崟淇℃伅鍙樺寲鏃讹紝鎻愮ず淇濆瓨
+          notification.warning({
+            top: 92,
+            message: this.state.dict['header.menu.config.update'],
+            duration: 5
+          })
+          return
+        }
+
+        _originMenu.activeKey = activeKey       // 淇濆瓨褰撳墠鎵撳紑椤电
+        _originMenu.open_edition = openEdition  // 鏇存柊鐗堟湰鍙�
+
+        let param = {
+          optionLibs: optionLibs,
+          editMenu: _originMenu,
+          editTab: item,
+          tabConfig: null,
+          editSubTab: null,
+          subTabConfig: null,
+          btnTab: null,
+          btnTabConfig: null,
+          editAction: null,
+          subConfig: '',
+          tabview: item.type
+        }
+
+        this.setState({
+          loading: true
+        })
+
+        Api.getSystemConfig({
+          func: 'sPC_Get_LongParam',
+          MenuID: item.linkTab
+        }).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 === 'SubTable' && _LongParam.Template === 'SubTable') {
+              param.subConfig = _LongParam
+            }
+
+            if (param.editTab) {
+              param.editTab.open_edition = res.open_edition || ''
+            }
+
+            this.props.handleView(param)
+          } else {
+            this.setState({
+              loading: false
+            })
+            notification.warning({
+              top: 92,
+              message: res.message,
+              duration: 5
+            })
+          }
+        })
+      }, () => {
+        notification.warning({
+          top: 92,
+          message: this.state.dict['header.menu.config.update'],
+          duration: 5
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 椤甸潰鍚敤鍋滄鍒囨崲
+   */
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    let _enabled = !config.enabled
+    let result = this.verifyconfig(config)
+    if (_enabled && result !== true) {
+      notification.warning({
+        top: 92,
+        message: result,
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: _enabled}
+    })
+  }
+
+  /**
+   * @description 鏍¢獙閰嶇疆淇℃伅鐨勫悎娉曟��
+   */
+  verifyconfig = (config) => {
+    let tabinvalid = true
+    if (config.tabgroups.length > 1) {
+      config.tabgroups.forEach(group => {
+        if (group.sublist.length === 0) {
+          tabinvalid = false
+        }
+      })
+    }
+
+    if (!config.setting.tableName || !config.setting.valueField || !config.setting.labelField || !config.setting.parentField) {
+      return '璇峰畬鍠勬爲褰㈢粨鏋勯厤缃俊鎭紒'
+    } else if (!tabinvalid) {
+      return '鑿滃崟鏍囩椤佃缃敊璇紙瀛樺湪澶氳鏍囩鏃讹紝琛屾爣绛句笉鍙负绌猴級锛�'
+    } else {
+      return true
+    }
+  }
+
+  /**
+   * @description 閫夋嫨涓嶄繚瀛樻椂锛屽鏈夊鍒舵寜閽紝鍒欏垹闄�
+   */
+  notsave = () => {
+    this.props.handleView()
+  }
+
+  /**
+   * @description 缂栬緫鍔熻兘瀹屾垚鏇存柊锛屽寘鎷В鍐绘寜閽�佺矘璐淬�佹浛鎹㈢瓑
+   */
+  editConfig = (res) => {
+    if (res.type === 'paste') {
+      this.setState({
+        pasteContent: res.content
+      }, () => {
+        this.setState({
+          pasteContent: null
+        })
+      })
+    }
+  }
+
+  /**
+   * @description 鏇存柊鏍囩閰嶇疆淇℃伅
+   */
+  updatetabs = (config, delcards) => {
+    const { delTabs } = this.state
+
+    this.setState({
+      config: config,
+      delTabs: delcards ? [...delTabs, ...delcards] : delTabs
+    })
+  }
+
+  /**
+   * @description 鏇存柊閰嶇疆淇℃伅
+   */
+  updateconfig = (config) => {
+    this.setState({
+      config: config
+    })
+  }
+
+  render () {
+    const { activeKey, config } = this.state
+
+    let configTabs = []
+    config.tabgroups.forEach(group => {
+      configTabs.push(...group.sublist)
+    })
+
+    return (
+      <div className="tree-page-board">
+        <DndProvider backend={HTML5Backend}>
+          {/* 宸ュ叿鏍� */}
+          <div className="tools">
+            <Collapse accordion defaultActiveKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+              {/* 鍩烘湰淇℃伅 */}
+              <Panel forceRender={true} header={this.state.dict['header.menu.basedata']} key="0" id="main-basedata">
+                {/* 鑿滃崟淇℃伅 */}
+                <MenuForm
+                  dict={this.state.dict}
+                  formlist={this.state.menuformlist}
+                  wrappedComponentRef={(inst) => this.menuformRef = inst}
+                />
+              </Panel>
+              {/* 娣诲姞鏍囩 */}
+              <Panel header={this.state.dict['header.menu.tab']} key="4">
+                <div className="search-element">
+                  {Source.tabItems.map((item, index) => {
+                    return (<SourceElement key={index} content={item}/>)
+                  })}
+                </div>
+                {configTabs.length > 0 ?
+                  <p className="config-btn-title">
+                    <Tooltip placement="topLeft" title="鐐瑰嚮鎸夐挳锛屽彲瀹屾垚鎴栨煡鐪嬫爣绛鹃厤缃俊鎭��">
+                      <Icon type="question-circle" />
+                    </Tooltip>
+                    {this.state.dict['header.menu.tab.configurable']}
+                  </p> : null
+                }
+                {configTabs.map((item, index) => {
+                  return (
+                    <div key={index}>
+                      <Button
+                        className="config-button"
+                        icon={item.icon}
+                        style={{marginBottom: '10px'}}
+                        onClick={() => this.setSubConfig(item, 'tab')}
+                      >{item.label}</Button>
+                    </div>
+                  )
+                })}
+              </Panel>
+            </Collapse>
+          </div>
+          <div className="setting">
+            <Card title={
+              <div>
+                {this.state.dict['header.menu.page.configurable']} 
+                <Icon type="redo" style={{marginLeft: '10px'}} title="鍒锋柊鏍囩鍒楄〃" onClick={() => this.reloadTab(true)} />
+              </div>
+            } bordered={false} extra={
+              <div>
+                <EditComponent dict={this.state.dict} type="TreePage" config={this.state.config} MenuID={this.props.menu.MenuID} refresh={this.editConfig}/>
+                <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>
+              </div>
+            } style={{ width: '100%' }}>
+              <Row gutter={16}>
+                <Col span={config.setting.width}>
+                  <TreeSettingComponent
+                    config={config}
+                    MenuID={this.props.menu.MenuID}
+                    menuformRef={this.menuformRef}
+                    permFuncField={this.props.permFuncField}
+                    updatesetting={this.updateconfig}
+                  />
+                  <Card
+                    className="tree-card"
+                    title={
+                      <span className="tree-title">
+                        <span className="title">{config.setting.title}</span>
+                        {config.setting.searchable !== 'false' ? <span className="ant-input-search ant-input-affix-wrapper"><span className="ant-input-suffix"><Icon type="search" /></span></span> : null}
+                      </span>
+                    }
+                    bordered={false}
+                  >
+                    <div className="tree-box">
+                      <Tree
+                        defaultExpandAll={true}
+                        blockNode
+                        showIcon={config.setting.showIcon === 'true'}
+                        showLine={config.setting.showLine === 'true'}
+                      >
+                        <TreeNode icon={<Icon type="folder-open" />} title="parent 0" key="0-0">
+                          <TreeNode icon={<Icon type="file" />} title="leaf 0-0" key="0-0-0" isLeaf />
+                          <TreeNode icon={<Icon type="file" />} title="leaf 0-1" key="0-0-1" isLeaf />
+                        </TreeNode>
+                        <TreeNode icon={<Icon type="folder-open" />} title="parent 1" key="0-1">
+                          <TreeNode icon={<Icon type="file" />} title="leaf 1-0" key="0-1-0" isLeaf />
+                          <TreeNode icon={<Icon type="file" />} title="leaf 1-1" key="0-1-1" isLeaf />
+                        </TreeNode>
+                      </Tree>
+                    </div>
+                  </Card>
+                </Col>
+                <Col span={24 - config.setting.width}>
+                  {/* 鏍囩缁� */}
+                  <TabsComponent
+                    config={config}
+                    tabs={this.state.tabviews}
+                    setSubConfig={(item) => this.setSubConfig(item, 'tab')}
+                    updatetabs={this.updatetabs}
+                  />
+                </Col>
+              </Row>
+            </Card>
+          </div>
+        </DndProvider>
+        {/* 杩斿洖鏃舵湭淇濆瓨鎻愮ず */}
+        <Modal
+          bodyStyle={{textAlign: 'center', color: '#000000', fontSize: '16px'}}
+          closable={false}
+          maskClosable={false}
+          visible={this.state.closeVisible}
+          onCancel={() => { this.setState({closeVisible: false}) }}
+          footer={[
+            <Button key="save" className="mk-btn mk-green" loading={this.state.menucloseloading} onClick={this.submitConfig}>{this.state.dict['header.save']}</Button>,
+            <Button key="notsave" className="mk-btn mk-yellow" onClick={this.notsave}>{this.state.dict['header.notsave']}</Button>,
+            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['header.cancel']}</Button>
+          ]}
+          destroyOnClose
+        >
+          {this.state.dict['header.menu.config.placeholder']}
+        </Modal>
+        {this.state.loading && <Spin size="large" />}
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    sysRoles: state.sysRoles,
+    permFuncField: state.permFuncField,
+    memberLevel: state.memberLevel
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(ComTableConfig)
diff --git a/src/templates/treepageconfig/index.scss b/src/templates/treepageconfig/index.scss
new file mode 100644
index 0000000..45cb419
--- /dev/null
+++ b/src/templates/treepageconfig/index.scss
@@ -0,0 +1,266 @@
+.tree-page-board {
+  position: fixed;
+  z-index: 1070;
+  padding-top: 48px;
+  top: 0px;
+  left: 0px;
+  right: 0px;
+  bottom: 0px;
+  background: rgba(0, 0, 0, 0.35);
+  display: flex;
+  .tools {
+    flex: 1;
+    background: #ffffff;
+    border-right: 1px solid #d9d9d9;
+    height: 100%;
+    overflow-y: hidden;
+    padding-bottom: 30px;
+    .ant-collapse-item {
+      position: relative;
+      border: 0;
+    }
+    .ant-input-search {
+      margin-top: 10px;
+    }
+    .ant-collapse-item.ant-collapse-item-active {
+      border-bottom: 1px solid #d9d9d9;
+    }
+    .ant-collapse .ant-collapse-header {
+      padding: 11px 16px 10px 40px;
+      border-bottom: 1px solid #d9d9d9;
+      background: #1890ff;
+      color: #ffffff;
+    }
+    .ant-collapse-content-box {
+      .ant-form-item {
+        margin-bottom: 10px;
+        .ant-form-item-label {
+          text-align: left;
+          height: 25px;
+          line-height: 25px;
+        }
+      }
+    }
+    .search-element {
+      padding-top: 10px;
+      li {
+        padding: 0px 16px 10px;
+        div {
+          cursor: move;
+        }
+      }
+    }
+    
+    .ant-list {
+      margin-top: 20px;
+      .ant-list-item {
+        display: -webkit-box;
+        padding-right: 20px;
+        position: relative;
+        padding-left: 5px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        -webkit-line-clamp: 2;
+        -webkit-box-orient: vertical;
+        min-height: 55px;
+        width: 100%;
+        .anticon {
+          position: absolute;
+          top: 0px;
+          right: 0px;
+          padding: 3px 3px 10px 10px;
+          cursor: pointer;
+        }
+        .bottom-mask {
+          position: absolute;
+          width: 100%;
+          height: 8px;
+          bottom: 0;
+          left: 0;
+          background: #ffffff;
+          border-radius: 8px;
+        }
+      }
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+    .config-button {
+      min-width: 65px;
+    }
+  }
+  .tools:hover {
+    overflow-y: auto;
+  }
+  .tools::-webkit-scrollbar {
+    width: 7px;
+  }
+  .tools::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
+    background: rgba(0, 0, 0, 0);
+  }
+  .tools::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0);
+    background: rgba(0, 0, 0, 0);
+  }
+  .tools:hover::-webkit-scrollbar-thumb {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+    background: rgba(0, 0, 0, 0.13);
+  }
+  .tools:hover::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border: 1px solid rgba(0, 0, 0, 0.07);
+  }
+  .setting {
+    position: relative;
+    width: calc(100vw - 235px);
+    height: 100%;
+    background: #ffffff;
+    .ant-switch.big {
+      min-width: 60px;
+      height: 28px;
+      line-height: 28px;
+      margin-top: -2px;
+      .ant-switch-inner {
+        font-size: 14px;
+      }
+    }
+    .ant-switch.big::after {
+      width: 24px;
+      height: 24px;
+    }
+    .ant-card-head {
+      min-height: 44px;
+    }
+    .ant-card-head-title {
+      padding: 5px 0;
+      color: #1890ff;
+    }
+    .ant-card-extra {
+      padding: 5px 0;
+      button {
+        margin-left: 20px;
+      }
+    }
+    .ant-card-body {
+      position: relative;
+      padding: 10px 10px 40px;
+
+      .tree-card {
+        min-height: calc(100vh - 125px);
+        box-shadow: 0px 0px 2px #dddddd;
+        .ant-card-head {
+          padding: 0 10px;
+          .ant-card-head-title {
+            padding: 15px 0 10px;
+            .tree-title {
+              display: inline-block;
+              width: 100%;
+              color: #1890ff;
+              .ant-input-affix-wrapper {
+                width: calc(100% - 140px);
+                max-width: 130px;
+                margin-top: 0px;
+                float: right;
+                height: 28px;
+                border-radius: 20px;
+                border: 1px solid #d9d9d9;
+                opacity: 0.6;
+              }
+              .title {
+                display: inline-block;
+                max-width: 50%;
+                overflow: hidden;
+                text-overflow:ellipsis;
+                white-space: nowrap;
+              }
+            }
+          }
+        }
+        >.ant-card-body {
+          padding: 0px;
+          .ant-menu-inline {
+            border-right: 0;
+            margin-top: 2px;
+          }
+
+          .tree-box {
+            min-height: calc(100vh - 180px);
+            overflow-x: auto;
+          }
+          .tree-box::-webkit-scrollbar {
+            height: 10px;
+          }
+          .tree-box::-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);
+          }
+          .tree-box::-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-tree-node-selected
+          .ant-tree.ant-tree-directory > li span.ant-tree-node-content-wrapper::before,
+          .ant-tree.ant-tree-directory .ant-tree-child-tree > li span.ant-tree-node-content-wrapper::before {
+            right: 0px;
+          }
+        }
+      }
+
+      .model-table-tab-list .tab-line-list {
+        padding-top: 0px;
+        > .anticon-question-circle {
+          top: 10px;
+        }
+        > .anticon-plus {
+          top: 15px;
+        }
+        > .ant-row .ant-tabs-tab .edit {
+          top: 10px;
+        }
+        .anticon-arrow-down, .anticon-arrow-up, .anticon-delete {
+          top: 15px;
+        }
+      }
+      .model-tree-menu-setting > .anticon-setting {
+        font-size: 16px;
+        right: 0px;
+        top: -9px;
+      }
+    }
+  }
+  .setting {
+    overflow-y: scroll;
+  }
+  .setting::-webkit-scrollbar {
+    width: 7px;
+  }
+  .setting::-webkit-scrollbar-thumb {
+    border-radius: 5px;
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+    background: rgba(0, 0, 0, 0.13);
+    display: none;
+  }
+  .setting::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
+  }
+  .setting:hover::-webkit-scrollbar-thumb {
+    display: block;
+  }
+  .ant-spin {
+    position: absolute;
+    margin-left: calc(50vw - 22px);
+    margin-top: 30vh;
+  }
+}
+
diff --git a/src/templates/treepageconfig/source.jsx b/src/templates/treepageconfig/source.jsx
new file mode 100644
index 0000000..1b3afed
--- /dev/null
+++ b/src/templates/treepageconfig/source.jsx
@@ -0,0 +1,57 @@
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+
+const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+
+class CommonTableBaseData {
+  baseConfig = {
+    type: 'system',
+    Template: 'TreePage',
+    enabled: false,
+    setting: {
+      tableName: '',
+      title: 'Tree',
+      order: '',
+      dataresource: '',
+      interType: 'inner',
+      sysInterface: 'false',
+      searchable: 'true',
+      innerFunc: '',
+      interface: '',
+      outerFunc: '',
+      width: 5
+    },
+    tabgroups: [{
+      uuid: 'tabs',
+      sublist: [
+        {
+          origin: true,          // 鏄惁涓虹ず渚�
+          uuid: Utils.getuuid(),
+          label: 'tab1',
+          type: 'SubTable',
+          linkTab: '',
+          supMenu: ''
+        },
+        {
+          origin: true,
+          uuid: Utils.getuuid(),
+          label: 'tab2',
+          type: 'SubTable',
+          linkTab: '',
+          supMenu: ''
+        }
+      ]
+    }]
+  }
+
+  tabItems = [
+    {
+      type: 'tabs',
+      label: CommonDict['header.menu.tab.subtable'],
+      subType: 'SubTable',
+    }
+  ]
+}
+
+export default new CommonTableBaseData()
diff --git a/src/templates/zshare/editcomponent/index.jsx b/src/templates/zshare/editcomponent/index.jsx
index 25c8cb3..8de343f 100644
--- a/src/templates/zshare/editcomponent/index.jsx
+++ b/src/templates/zshare/editcomponent/index.jsx
@@ -12,7 +12,7 @@
     type: PropTypes.string,
     MenuID: PropTypes.any,
     config: PropTypes.object,
-    thawButtons: PropTypes.array,
+    thawButtons: PropTypes.any,
     refresh: PropTypes.func
   }
 
@@ -185,7 +185,7 @@
   render() {
     const menu = (
       <Menu onClick={this.handleMenuClick}>
-        <Menu.Item key="thaw"><Icon type="unlock" />{this.props.dict['header.form.thawbutton']}</Menu.Item>
+        {this.props.type !== 'TreePage' ? <Menu.Item key="thaw"><Icon type="unlock" />{this.props.dict['header.form.thawbutton']}</Menu.Item> : null}
         <Menu.Item key="paste"><Icon type="snippets" />{this.props.dict['header.form.paste']}</Menu.Item>
         {/* <Menu.Item key="replace"><Icon type="retweet" />鏇挎崲</Menu.Item> */}
       </Menu>
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index eb6d5de..2d43a25 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -119,7 +119,7 @@
       key: 'dataresource',
       label: '鏁版嵁婧�',
       initVal: setting.dataresource || '',
-      tooltip: '浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩��',
+      tooltip: '浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� \'\'銆� @$ -> */ 鎴� \'\'',
       help: '鏁版嵁ID锛�' + MenuID,
       required: false,
       readonly: false,
@@ -232,6 +232,282 @@
         { value: 'false', text: '涓嶆墽琛�' }
       ]
     },
+  ]
+}
+
+/**
+ * @description 鑾峰彇鏍戝舰椤甸潰璁剧疆琛ㄥ崟閰嶇疆淇℃伅
+ * @param {object} setting   // 鑿滃崟鍏ㄥ眬璁剧疆淇℃伅
+ * @param {string} type      // 鑿滃崟绫诲瀷
+ */
+export function getTreeSettingForm (setting, usefulFields = [], MenuID) {
+  let str = '^(' + usefulFields.join('|') + ')'
+  let _patten = new RegExp(str + formRule.func.innerPattern + '$', 'g')
+
+  return [
+    {
+      type: 'text',
+      key: 'tableName',
+      label: '琛ㄥ悕',
+      initVal: setting.tableName || '',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          max: formRule.input.max,
+          message: formRule.input.message
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'title',
+      label: '鏍囬',
+      initVal: setting.title || '',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          max: formRule.input.max,
+          message: formRule.input.message
+        }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'interType',
+      label: Formdict['header.form.intertype'],
+      initVal: setting.interType || 'inner',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'inner', text: Formdict['header.form.interface.inner'] },
+        { value: 'outer', text: Formdict['header.form.interface.outer'] }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'sysInterface',
+      label: Formdict['header.form.sysInterface'],
+      initVal: setting.sysInterface || 'false',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'true', text: Formdict['header.form.true'] },
+        { value: 'false', text: Formdict['header.form.false'] }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'interface',
+      label: Formdict['header.form.interface'],
+      initVal: setting.sysInterface === 'true' ? (window.GLOB.mainSystemApi || '') : (setting.interface || ''),
+      required: true,
+      readonly: setting.sysInterface === 'true',
+      rules: [
+        {
+          max: formRule.input.max,
+          message: formRule.input.message
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'outerFunc',
+      label: Formdict['header.form.outerFunc'],
+      initVal: setting.outerFunc || '',
+      required: false,
+      readonly: false,
+      rules: [
+        {
+          pattern: formRule.func.pattern,
+          message: formRule.func.message
+        }, {
+          max: formRule.func.max,
+          message: formRule.func.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'innerFunc',
+      label: Formdict['header.form.innerFunc'],
+      initVal: setting.innerFunc || '',
+      tooltip: '寮�澶村彲鐢ㄥ瓧绗︼細' + usefulFields.join(', '),
+      placement: 'bottomLeft',
+      required: false,
+      readonly: false,
+      rules: [
+        {
+          pattern: _patten,
+          message: formRule.func.innerMessage
+        }, {
+          max: formRule.func.max,
+          message: formRule.func.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'datasource',
+      key: 'dataresource',
+      label: '鏁版嵁婧�',
+      initVal: setting.dataresource || '',
+      tooltip: '浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� \'\'銆� @$ -> */ 鎴� \'\'',
+      help: '鏁版嵁ID锛�' + MenuID,
+      required: false,
+      readonly: false,
+      rules: [
+        {
+          pattern: _patten,
+          message: formRule.func.innerMessage
+        }, {
+          max: formRule.func.max,
+          message: formRule.func.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'valueField',
+      label: 'Value',
+      initVal: setting.valueField || '',
+      tooltip: '鏁版嵁鍊煎瓧娈点��',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          pattern: formRule.field.pattern,
+          message: formRule.field.message
+        }, {
+          max: formRule.field.max,
+          message: formRule.field.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'labelField',
+      label: 'Label',
+      initVal: setting.labelField || '',
+      tooltip: '鏄剧ず鏂囧瓧瀛楁銆�',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          pattern: formRule.field.pattern,
+          message: formRule.field.message
+        }, {
+          max: formRule.field.max,
+          message: formRule.field.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'parentField',
+      label: 'Parent',
+      initVal: setting.parentField || '',
+      tooltip: '鐖剁骇瀛楁銆�',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          pattern: formRule.field.pattern,
+          message: formRule.field.message
+        }, {
+          max: formRule.field.max,
+          message: formRule.field.maxMessage
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'order',
+      label: '鎺掑簭',
+      initVal: setting.order || '',
+      placeholder: 'ID asc, UID desc',
+      required: true,
+      readonly: false,
+      rules: [
+        {
+          max: formRule.input.max,
+          message: formRule.input.message
+        }
+      ]
+    },
+    {
+      type: 'text',
+      key: 'mark',
+      label: '椤剁骇鏍囪瘑',
+      initVal: setting.mark || '',
+      tooltip: '鐖剁骇瀛楁鍊间笌椤剁骇鏍囪瘑锛堥粯璁ゅ�间负绌猴級鐩稿悓鏃讹紝瑙嗕负椤剁骇鑺傜偣銆�',
+      required: false,
+      readonly: false,
+      rules: [
+        {
+          max: formRule.input.max,
+          message: formRule.input.message
+        }
+      ]
+    },
+    {
+      type: 'number',
+      key: 'width',
+      min: 2,
+      max: 12,
+      label: '瀹藉害',
+      tooltip: '姣忚鍒嗕负24浠斤紝鏍戝舰姣斾緥鍙缃负2-12锛堟渶澶�50%锛�',
+      initVal: setting.width || 5,
+      required: true
+    },
+    {
+      type: 'radio',
+      key: 'searchable',
+      label: '鎼滅储',
+      initVal: setting.searchable || 'true',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'true', text: '鏄剧ず' },
+        { value: 'false', text: '闅愯棌' }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'default',
+      label: '榛樿sql',
+      initVal: setting.default || 'true',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'true', text: '鎵ц' },
+        { value: 'false', text: '涓嶆墽琛�' }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'showIcon',
+      label: '鏄剧ず鍥炬爣',
+      initVal: setting.showIcon || 'false',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'true', text: Formdict['header.form.true'] },
+        { value: 'false', text: Formdict['header.form.false'] }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'showLine',
+      label: '鏄剧ず鍒嗗壊绾�',
+      initVal: setting.showLine || 'false',
+      required: false,
+      readonly: false,
+      options: [
+        { value: 'true', text: Formdict['header.form.true'] },
+        { value: 'false', text: Formdict['header.form.false'] }
+      ]
+    }
   ]
 }
 
@@ -687,6 +963,20 @@
     })
   }
 
+  let refresh = []
+  if (type === 'subtable') { // 瀛愯〃椤甸潰锛屽彲璁剧疆鍒锋柊涓昏〃鍙婂悓绾ф爣绛�
+    refresh.push({
+      value: 'maingrid',
+      text: Formdict['header.form.refresh.maingrid']
+    }, {
+      value: 'equaltab',
+      text: Formdict['header.form.refresh.equaltab']
+    }, {
+      value: 'mainline',
+      text: Formdict['header.form.refresh.mainline']
+    })
+  }
+
   return [
     {
       type: 'select',
@@ -928,7 +1218,8 @@
       }, {
         value: 'view',
         text: Formdict['header.form.refresh.view']
-      }]
+      },
+      ...refresh]
     },
     {
       type: 'select',
@@ -962,7 +1253,8 @@
       }, {
         value: 'view',
         text: Formdict['header.form.refresh.view']
-      }]
+      },
+      ...refresh]
     },
     {
       type: 'select',
@@ -2142,13 +2434,24 @@
       required: false
     },
     {
+      type: 'number',
+      key: 'level',
+      label: '鏄剧ず绾у埆',
+      tooltip: '鏍囩鏄剧ず鎺у埗锛岄�夋嫨鎸囧畾绾у埆鏃舵樉绀烘爣绛撅紝绾у埆涓虹┖鏃跺缁堟樉绀恒��',
+      initVal: card.level,
+      min: 1,
+      max: 10,
+      required: false,
+      forbid: type !== 'TreePage',
+    },
+    {
       type: 'radio',
       key: 'searchPass',
       label: '涓昏〃鎼滅储',
       initVal: card.searchPass || 'false',
       tooltip: '浣跨敤涓昏〃鎼滅储鏉′欢鏃讹紝涓昏〃鐨勬悳绱㈡潯浠朵細浼犲叆瀛愯〃涓��',
       required: false,
-      forbid: type !== 'main',
+      forbid: type !== 'CommonTable',
       options: [{
         value: 'true',
         text: '浣跨敤'
diff --git a/src/utils/option.js b/src/utils/option.js
index fd91b87..769ea29 100644
--- a/src/utils/option.js
+++ b/src/utils/option.js
@@ -62,6 +62,13 @@
     isSubtable: true
   },
   {
+    title: '鏍戝舰椤甸潰',
+    type: 'TreePage',
+    url: mainsubtable,
+    baseconfig: '',
+    isSystem: true
+  },
+  {
     title: '瑙掕壊鏉冮檺鍒嗛厤',
     type: 'RolePermission',
     url: rolemanage,
diff --git a/src/utils/utils.js b/src/utils/utils.js
index 3a918f8..dd981d9 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -67,10 +67,10 @@
       {key: 'alter', reg: /(^|\s)alter\s/ig},
       {key: 'truncate', reg: /(^|\s)truncate\s/ig},
       {key: 'if', reg: /(^|\s)if\s/ig},
-      {key: 'exec', reg: /exec/ig},
-      {key: 'OBJECT', reg: /object/ig},
-      {key: 'sys.', reg: /sys\./ig},
-      {key: 'kill', reg: /kill/ig}
+      {key: 'exec', reg: /(^|\s)exec(\s|\()/ig},
+      {key: 'OBJECT', reg: /(^|\s)object(\s|\()/ig},
+      {key: 'sys.', reg: /(^|\s)sys\./ig},
+      {key: 'kill', reg: /(^|\s)kill\s/ig}
     ]
 
     if (type === 'customscript') {

--
Gitblit v1.8.0