From 89f85223ff7b1b5ee3931ae01efa3dd17d5524fc Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期一, 21 十一月 2022 16:03:27 +0800
Subject: [PATCH] Merge branch 'develop'

---
 src/templates/comtableconfig/updatetable/index.scss                                |    0 
 src/templates/zshare/modalform/index.jsx                                           |   23 
 src/menu/components/form/formaction/index.scss                                     |    3 
 src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx                     |   11 
 src/tabviews/custom/components/chart/antv-scatter/index.scss                       |    6 
 src/tabviews/custom/components/iframe/index.scss                                   |   63 
 src/tabviews/formtab/index.jsx                                                     |    3 
 src/menu/components/timeline/normal-timeline/index.jsx                             |    5 
 src/tabviews/custom/components/editor/braft-editor/index.jsx                       |    2 
 src/menu/components/form/dragtitle/options.jsx                                     |    4 
 src/mob/mobshell/index.jsx                                                         |   14 
 src/menu/components/card/balcony/options.jsx                                       |    5 
 src/tabviews/zshare/settingcomponent/index.jsx                                     |   25 
 src/templates/zshare/verifycard/customscript/index.jsx                             |   13 
 src/views/menudesign/printmenuform/index.jsx                                       |   14 
 src/tabviews/custom/popview/index.jsx                                              | 1016 ++
 src/menu/components/chart/antv-scatter/index.jsx                                   |   11 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx  |   12 
 src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx               |   11 
 src/menu/datasource/verifycard/index.jsx                                           |  121 
 src/views/menudesign/popview/menuform/index.jsx                                    |   38 
 src/assets/css/main.scss                                                           |   22 
 src/menu/components/table/edit-table/index.scss                                    |   15 
 src/menu/components/card/table-card/index.scss                                     |   10 
 src/templates/formtabconfig/settingform/index.jsx                                  |    7 
 src/menu/components/chart/antv-G6/index.scss                                       |   68 
 src/menu/components/card/cardcellcomponent/index.jsx                               |   73 
 src/menu/components/form/tab-form/index.jsx                                        |    5 
 src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx     |   18 
 src/pc/menushell/card.jsx                                                          |   10 
 src/menu/components/carousel/prop-card/index.jsx                                   |   49 
 src/menu/components/card/cardcomponent/index.jsx                                   |    6 
 src/menu/components/chart/antv-pie/index.jsx                                       |   29 
 src/menu/components/table/edit-table/columns/index.scss                            |    5 
 src/pc/components/navbar/normal-navbar/index.scss                                  |   28 
 src/views/menudesign/index.scss                                                    |   17 
 src/menu/components/card/prop-card/index.jsx                                       |   49 
 src/templates/zshare/customscript/index.jsx                                        |    2 
 src/menu/components/tabs/tabcomponents/card.jsx                                    |    6 
 src/templates/sharecomponent/treesettingcomponent/index.jsx                        |    6 
 src/menu/components/tabs/paste/index.jsx                                           |   26 
 src/menu/components/table/edit-table/columns/index.jsx                             |   41 
 src/utils/sqlFormatter-origin.js                                                   | 3381 +++++++
 src/menu/modalconfig/controller.jsx                                                |    2 
 src/components/encrypts/index.jsx                                                  |    8 
 src/menu/datasource/verifycard/utils.jsx                                           |    8 
 src/tabviews/custom/components/chart/antv-bar-line/index.jsx                       |   68 
 package-lock.json                                                                  |   51 
 src/tabviews/custom/components/chart/antv-dashboard/index.jsx                      |   35 
 src/views/menudesign/popview/index.jsx                                             |  297 
 src/menu/components/tabs/table-tabs/options.jsx                                    |   27 
 src/templates/zshare/codemirror/index.scss                                         |   11 
 src/menu/components/share/actioncomponent/dragaction/card.jsx                      |   11 
 src/tabviews/zshare/topSearch/index.jsx                                            |    2 
 src/menu/components/card/prop-card/index.scss                                      |   18 
 src/templates/comtableconfig/index.jsx                                             |   27 
 src/menu/components/chart/chart-custom/index.jsx                                   |   11 
 src/menu/components/form/simple-form/options.jsx                                   |   39 
 src/menu/components/form/simple-form/index.jsx                                     |   14 
 src/tabviews/custom/components/table/edit-table/index.jsx                          |   38 
 src/mob/components/tabs/tabcomponents/index.jsx                                    |    8 
 src/tabviews/custom/components/chart/antv-bar-line/asyncButtonComponent.jsx        |    7 
 src/api/index.js                                                                   |  187 
 src/templates/sharecomponent/actioncomponent/index.scss                            |    4 
 src/utils/asyncSpinComponent.jsx                                                   |    6 
 src/menu/components/chart/antv-dashboard/index.scss                                |   19 
 src/menu/stylecombcontroller/styleInput/index.scss                                 |    3 
 src/tabviews/custom/components/iframe/index.jsx                                    |  249 
 src/mob/mobshell/card.jsx                                                          |    9 
 src/templates/zshare/codemirror/index.jsx                                          |   60 
 src/menu/sysinterface/index.jsx                                                    |   37 
 src/mob/components/search/single-search/index.jsx                                  |    2 
 src/menu/components/table/base-table/columns/index.scss                            |   26 
 src/menu/components/table/base-table/columns/editColumn/index.jsx                  |   16 
 src/tabviews/custom/components/table/base-table/index.scss                         |   47 
 src/tabviews/zshare/tablenodes/index.jsx                                           |  451 
 src/components/qrcode/index.scss                                                   |    1 
 src/views/pcdesign/index.jsx                                                       | 1054 -
 src/menu/components/tabs/table-tabs/index.scss                                     |    9 
 src/menu/datasource/verifycard/settingform/index.jsx                               |  102 
 src/mob/components/tabs/tabcomponents/card.jsx                                     |    6 
 src/menu/components/share/actioncomponent/index.jsx                                |  104 
 src/menu/components/card/cardsimplecomponent/index.jsx                             |   20 
 src/tabviews/custom/components/form/step-form/index.jsx                            |   39 
 src/menu/components/share/copycomponent/index.jsx                                  |    6 
 src/pc/modulesource/option.jsx                                                     |    2 
 src/menu/components/group/paste/index.jsx                                          |   18 
 src/mob/components/tabs/antv-tabs/index.jsx                                        |   10 
 src/tabviews/custom/components/tree/antd-tree/index.jsx                            |   21 
 src/tabviews/zshare/actionList/popupbutton/index.jsx                               |   23 
 src/tabviews/custom/components/tabs/antv-tabs/index.jsx                            |    2 
 src/tabviews/zshare/normalTable/index.jsx                                          |    9 
 src/menu/components/share/searchcomponent/index.jsx                                |    1 
 src/templates/comtableconfig/updatetable/index.jsx                                 | 1123 ++
 src/tabviews/custom/components/group/normal-group/index.scss                       |   22 
 src/views/menudesign/homeform/index.jsx                                            |   15 
 src/templates/treepageconfig/index.jsx                                             |   25 
 src/views/menudesign/menuform/index.jsx                                            |   71 
 src/menu/datasource/index.jsx                                                      |   17 
 src/menu/components/chart/antv-bar/index.scss                                      |    5 
 src/menu/components/module/voucher/index.jsx                                       |    2 
 src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx              |    1 
 src/menu/components/table/base-table/index.scss                                    |   16 
 src/menu/components/form/formaction/index.jsx                                      |   23 
 src/menu/components/tree/antd-tree/index.jsx                                       |    2 
 src/views/billprint/index.jsx                                                      |  122 
 src/tabviews/subtabtable/index.jsx                                                 |   18 
 src/menu/tableshell/card.jsx                                                       |    4 
 src/tabviews/zshare/actionList/normalbutton/index.jsx                              |   46 
 src/views/tabledesign/index.scss                                                   |   43 
 src/tabviews/custom/components/table/normal-table/index.scss                       |   12 
 src/views/menudesign/index.jsx                                                     |  733 
 src/menu/modulesource/option.jsx                                                   |   10 
 src/tabviews/custom/components/card/data-card/index.jsx                            |   20 
 src/menu/replaceField/settingform/index.jsx                                        |    4 
 src/menu/components/code/sandbox/codecontent/index.jsx                             |   76 
 src/templates/calendarconfig/index.jsx                                             |   24 
 src/router/index.js                                                                |    2 
 src/views/tabledesign/popview/index.scss                                           |    0 
 src/menu/pastecontroller/index.jsx                                                 |   39 
 src/menu/components/card/data-card/index.jsx                                       |   86 
 src/menu/components/chart/antv-G6/chartcompile/formconfig.jsx                      |  239 
 src/menu/components/chart/antv-dashboard/index.jsx                                 |   13 
 src/mob/components/menubar/common-menubar/index.jsx                                |    2 
 src/menu/components/carousel/data-card/index.scss                                  |   24 
 src/menu/components/tabs/antv-tabs/index.jsx                                       |   11 
 src/templates/sharecomponent/settingcomponent/index.jsx                            |   19 
 src/menu/components/chart/antv-G6/index.jsx                                        | 1461 +++
 src/components/header/index.jsx                                                    |    7 
 src/tabviews/custom/components/chart/antv-bar-line/index.scss                      |    6 
 src/views/pcdesign/menuform/index.jsx                                              |   19 
 src/templates/formtabconfig/index.jsx                                              |    4 
 src/templates/zshare/verifycard/callbackcustomscript/index.jsx                     |    1 
 src/templates/zshare/unattended/index.jsx                                          |   21 
 src/menu/components/chart/antv-G6/chartcompile/index.scss                          |    6 
 src/menu/transfer/index.jsx                                                        |  260 
 src/menu/components/iframe/index.jsx                                               |  193 
 src/tabviews/calendar/index.jsx                                                    |   19 
 src/views/rolemanage/index.jsx                                                     |    8 
 src/tabviews/custom/components/carousel/data-card/index.jsx                        |  147 
 src/templates/sharecomponent/fieldscomponent/index.jsx                             |    5 
 src/menu/components/code/sandbox/index.jsx                                         |   13 
 src/views/billprint/index.scss                                                     |    8 
 src/views/tabledesign/menuform/index.jsx                                           |   93 
 src/menu/components/table/normal-table/index.scss                                  |   16 
 src/templates/zshare/verifycard/customform/index.jsx                               |   12 
 src/menu/components/table/normal-table/columns/index.scss                          |    5 
 src/templates/sharecomponent/settingcalcomponent/index.jsx                         |    6 
 src/tabviews/custom/components/chart/custom-chart/index.jsx                        |    9 
 src/templates/sharecomponent/settingcomponent/settingform/utils.jsx                |    6 
 src/menu/components/card/balcony/index.jsx                                         |    2 
 src/tabviews/zshare/actionList/tabbutton/index.jsx                                 |   11 
 src/components/normalform/modalform/index.jsx                                      |    4 
 src/assets/mobimg/kapmap.jfif                                                      |    0 
 src/utils/asyncComponent.jsx                                                       |    6 
 src/menu/stylecontroller/index.jsx                                                 |   14 
 src/views/menudesign/popview/menuform/index.scss                                   |    0 
 src/menu/components/table/base-table/options.jsx                                   |    2 
 src/templates/zshare/verifycard/index.jsx                                          |   88 
 src/menu/components/share/sourcecomponent/index.jsx                                |    2 
 src/menu/modulecell/index.jsx                                                      |    5 
 src/tabviews/zshare/actionList/printbutton/index.jsx                               |   24 
 src/menu/components/card/cardcellcomponent/formconfig.jsx                          |   74 
 src/templates/sharecomponent/actioncomponent/index.jsx                             |    6 
 src/menu/components/editor/braft-editor/index.jsx                                  |    2 
 package.json                                                                       |    1 
 src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx   |   19 
 src/tabviews/zshare/actionList/popupbutton/index.scss                              |    6 
 src/views/menudesign/popview/index.scss                                            |    0 
 src/mob/searchconfig/index.jsx                                                     |    1 
 src/menu/components/tree/antd-tree/options.jsx                                     |   12 
 src/menu/components/share/markcomponent/index.jsx                                  |   24 
 src/mob/components/tabs/antv-tabs/options.jsx                                      |   15 
 src/menu/components/card/cardcellcomponent/index.scss                              |    3 
 src/menu/components/card/cardcellcomponent/dragaction/action.jsx                   |   10 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx                   |   12 
 src/menu/components/share/actioncomponent/dragaction/index.jsx                     |    2 
 src/menu/stylecombcontroller/styleInput/index.jsx                                  |    2 
 src/templates/modalconfig/index.jsx                                                |    4 
 src/tabviews/custom/components/form/step-form/index.scss                           |    4 
 src/menu/tablenodes/index.scss                                                     |    4 
 src/mob/components/official/index.jsx                                              |    2 
 src/mob/modalconfig/index.jsx                                                      |    3 
 src/tabviews/commontable/index.jsx                                                 |   86 
 src/menu/components/chart/chart-custom/index.scss                                  |   25 
 src/menu/stylecontroller/styleInput/index.scss                                     |    3 
 src/utils/sqlFormatter.js                                                          | 3387 +++++++
 src/menu/components/form/formaction/formconfig.jsx                                 |   18 
 src/menu/components/share/normalheader/index.scss                                  |    6 
 src/tabviews/custom/components/card/cardcellList/index.jsx                         |   75 
 src/tabviews/custom/components/card/prop-card/index.jsx                            |   22 
 src/menu/tableshell/index.jsx                                                      |   79 
 src/menu/components/carousel/data-card/options.jsx                                 |   63 
 src/menu/components/card/data-card/options.jsx                                     |   12 
 src/mob/components/menubar/normal-menubar/index.jsx                                |    2 
 src/tabviews/custom/components/table/normal-table/index.jsx                        |   65 
 src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx |    7 
 src/menu/components/chart/antv-pie/index.scss                                      |   19 
 src/menu/components/tabs/antv-tabs/options.jsx                                     |   15 
 src/tabviews/custom/components/chart/antv-pie/index.jsx                            |   61 
 src/tabviews/custom/components/timeline/normal-timeline/index.jsx                  |   21 
 src/tabviews/home/defaulthome/index.scss                                           |    1 
 src/templates/subtableconfig/index.jsx                                             |   24 
 src/menu/components/group/groupcomponents/card.jsx                                 |    6 
 src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx            |    1 
 src/components/breadview/index.jsx                                                 |   16 
 src/menu/components/share/actioncomponent/actionform/index.jsx                     |   62 
 src/menu/components/tabs/table-tabs/dragabletabs.jsx                               |    1 
 src/tabviews/custom/index.jsx                                                      |  349 
 src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx            |    2 
 src/menu/components/table/normal-table/columns/index.jsx                           |   52 
 src/views/appmanage/submutilform/index.jsx                                         |   28 
 src/tabviews/zshare/cardcomponent/index.jsx                                        |   12 
 src/tabviews/treepage/index.jsx                                                    |   21 
 src/tabviews/custom/components/chart/antv-dashboard/index.scss                     |    3 
 src/tabviews/custom/components/chart/antv-scatter/index.jsx                        |   33 
 src/menu/components/group/groupcomponents/index.jsx                                |    8 
 src/menu/components/form/formaction/actionform/index.jsx                           |    5 
 src/tabviews/custom/components/carousel/data-card/index.scss                       |   93 
 src/menu/components/card/cardcellcomponent/dragaction/index.jsx                    |    8 
 src/menu/components/table/base-table/columns/editColumn/formconfig.jsx             |   38 
 src/pc/menushell/index.jsx                                                         |   14 
 src/tabviews/custom/popview/index.scss                                             |   60 
 src/menu/components/tabs/table-tabs/index.jsx                                      |  166 
 src/tabviews/custom/components/card/cardcellList/asyncButtonComponent.jsx          |    6 
 src/tabviews/zshare/actionList/exceloutbutton/index.jsx                            |   56 
 src/menu/components/card/cardcellcomponent/dragaction/card.jsx                     |   15 
 src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx                 |   11 
 src/menu/components/card/balcony/index.scss                                        |   18 
 src/menu/components/chart/antv-bar/index.jsx                                       |   16 
 src/views/tabledesign/index.jsx                                                    |  564 
 src/tabviews/zshare/actionList/index.jsx                                           |    6 
 src/tabviews/custom/components/form/simple-form/index.jsx                          |   34 
 src/tabviews/subtable/index.jsx                                                    |   29 
 src/utils/asyncIconComponent.jsx                                                   |    6 
 src/menu/components/table/edit-table/index.jsx                                     |   47 
 src/menu/components/carousel/data-card/index.jsx                                   |   45 
 src/tabviews/custom/components/card/table-card/index.jsx                           |   21 
 src/menu/components/module/voucher/options.jsx                                     |    5 
 src/menu/components/chart/antv-G6/chartcompile/index.jsx                           |  121 
 src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx                     |   14 
 src/menu/components/form/step-form/index.jsx                                       |    5 
 src/menu/components/share/actioncomponent/index.scss                               |    6 
 src/menu/components/chart/antv-scatter/index.scss                                  |    5 
 src/menu/components/share/normalheader/index.jsx                                   |   18 
 src/templates/modalconfig/settingform/index.jsx                                    |    3 
 src/tabviews/zshare/chartcomponent/asyncButtonComponent.jsx                        |    7 
 src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx              |    3 
 src/menu/components/search/main-search/index.jsx                                   |   47 
 src/utils/asyncLoadComponent.jsx                                                   |    6 
 src/tabviews/zshare/pageMessage/index.jsx                                          |    4 
 src/components/keyInterface/index.scss                                             |   48 
 src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx        |   12 
 src/components/keyInterface/index.jsx                                              |  273 
 src/utils/utils-datamanage.js                                                      |   27 
 src/menu/picturecontroller/index.jsx                                               |   20 
 src/tabviews/custom/components/code/sand-box/index.jsx                             |   38 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx   |    2 
 src/menu/components/card/table-card/index.jsx                                      |   33 
 src/menu/components/share/pastebasetable/index.jsx                                 |  135 
 src/utils/utils-custom.js                                                          |  445 
 src/views/main/index.jsx                                                           |    2 
 src/mob/modulesource/option.jsx                                                    |    4 
 src/menu/picturecontroller/index.scss                                              |    8 
 src/tabviews/custom/components/share/normalTable/index.scss                        |    5 
 src/templates/sharecomponent/fieldscomponent/editcard/index.jsx                    |   27 
 src/tabviews/zshare/actionList/excelInbutton/index.jsx                             |   14 
 src/menu/components/share/pastebasetable/index.scss                                |    0 
 src/tabviews/custom/components/carousel/prop-card/index.scss                       |   85 
 src/views/tabledesign/source.jsx                                                   |  135 
 src/menu/components/tabs/tabcomponents/index.jsx                                   |    8 
 src/menu/components/table/normal-table/columns/editColumn/index.jsx                |    3 
 src/tabviews/basetable/index.jsx                                                   | 1471 --
 src/menu/tablenodes/index.jsx                                                      |   69 
 src/menu/components/card/data-card/index.scss                                      |   10 
 src/views/pcdesign/index.scss                                                      |   14 
 src/menu/components/iframe/index.scss                                              |   53 
 src/menu/menushell/card.jsx                                                        |   12 
 src/templates/sharecomponent/settingcomponent/settingform/index.jsx                |   70 
 src/menu/components/code/sandbox/options.jsx                                       |   14 
 src/menu/components/iframe/options.jsx                                             |  124 
 src/views/mobdesign/index.jsx                                                      |  771 
 src/views/interface/history/index.jsx                                              |    2 
 src/api/cacheutils.js                                                              |   19 
 src/mob/colorsketch/index.jsx                                                      |   25 
 src/menu/components/share/actioncomponent/formconfig.jsx                           |  140 
 src/tabviews/custom/components/group/normal-group/index.jsx                        |  171 
 src/menu/components/group/normal-group/options.jsx                                 |    7 
 src/menu/versions/index.jsx                                                        |    1 
 src/tabviews/custom/components/carousel/prop-card/index.jsx                        |  155 
 src/menu/components/table/base-table/columns/index.jsx                             |  160 
 src/views/imdesign/index.jsx                                                       |   58 
 src/menu/components/table/normal-table/index.jsx                                   |   52 
 src/tabviews/zshare/tablenodes/index.scss                                          |   74 
 src/menu/components/group/normal-group/index.jsx                                   |   12 
 src/tabviews/zshare/actionList/asyncButtonComponent.jsx                            |    7 
 src/components/tabview/index.jsx                                                   |   31 
 src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx           |   38 
 src/assets/mobimg/indent.jfif                                                      |    0 
 src/tabviews/custom/components/table/base-table/index.jsx                          |  616 +
 src/components/normalform/modalform/styleInput/index.jsx                           |    4 
 src/menu/components/timeline/normal-timeline/index.scss                            |   18 
 src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx |   12 
 src/menu/components/carousel/prop-card/index.scss                                  |   22 
 src/menu/datasource/verifycard/customscript/index.jsx                              |   15 
 src/assets/mobimg/mindmap.png                                                      |    0 
 src/tabviews/custom/components/chart/antv-pie/index.scss                           |    4 
 src/templates/sharecomponent/searchcomponent/index.jsx                             |    1 
 src/tabviews/zshare/mutilform/mkColor/index.jsx                                    |   15 
 src/tabviews/custom/components/share/normalheader/index.scss                       |    1 
 src/views/mobdesign/index.scss                                                     |   15 
 src/tabviews/custom/components/share/normalTable/index.jsx                         |  188 
 src/menu/components/form/step-form/options.jsx                                     |   25 
 src/utils/utils.js                                                                 |   33 
 src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx              |  149 
 src/tabviews/custom/components/form/tab-form/index.jsx                             |   25 
 src/menu/components/share/pastecomponent/index.jsx                                 |   30 
 src/tabviews/custom/components/share/tabtransfer/index.jsx                         |   87 
 src/tabviews/debugtable/index.jsx                                                  |    3 
 src/templates/zshare/formconfig.jsx                                                |    7 
 src/tabviews/custom/components/chart/custom-chart/index.scss                       |    7 
 src/tabviews/zshare/settingcomponent/index.scss                                    |    6 
 src/components/querylog/index.jsx                                                  |   85 
 src/tabviews/custom/components/interfaces/interItem/index.jsx                      |   23 
 src/menu/replaceField/index.jsx                                                    |   57 
 src/menu/components/chart/chart-custom/chartcompile/formconfig.jsx                 |   11 
 src/pc/createview/index.jsx                                                        |   39 
 src/mob/modalconfig/controller.jsx                                                 |    2 
 src/pc/components/login/normal-login/index.jsx                                     |    2 
 src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx         |    7 
 /dev/null                                                                          |  730 -
 src/tabviews/zshare/cardcomponent/asyncButtonComponent.jsx                         |    6 
 src/menu/modalconfig/index.jsx                                                     |    9 
 src/menu/transfer/index.scss                                                       |    0 
 src/tabviews/custom/components/card/balcony/index.jsx                              |   18 
 src/menu/components/table/base-table/index.jsx                                     |   99 
 src/templates/zshare/verifycard/baseform/index.jsx                                 |   13 
 src/menu/components/share/normalform/index.jsx                                     |    3 
 src/components/normalform/modalform/styleInput/index.scss                          |    3 
 src/menu/menushell/index.jsx                                                       |   16 
 src/utils/option.js                                                                |   18 
 src/views/tabledesign/popview/index.jsx                                            |  234 
 342 files changed, 21,113 insertions(+), 6,572 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 9386635..380e9ac 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6906,6 +6906,11 @@
         }
       }
     },
+    "discontinuous-range": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/discontinuous-range/-/discontinuous-range-1.0.0.tgz",
+      "integrity": "sha512-c68LpLbO+7kP/b1Hr1qs8/BJ09F5khZGTxqxZuhzxpmwJKOgRFHJWIb9/KmqnqHhLdO55aOxFH/EGBvUQbL/RQ=="
+    },
     "dnd-core": {
       "version": "9.4.0",
       "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-9.4.0.tgz",
@@ -14493,6 +14498,11 @@
       "resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
       "integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
     },
+    "moo": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/moo/-/moo-0.5.2.tgz",
+      "integrity": "sha512-iSAJLHYKnX41mKcJKjqvnAN9sf0LMDTXDEvFv+ffuRR9a1MIuXLjMNL6EsnDHSkKLTWNqQQ5uo61P4EbU4NU+Q=="
+    },
     "mousetrap": {
       "version": "1.6.5",
       "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz",
@@ -14574,6 +14584,17 @@
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
       "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc="
+    },
+    "nearley": {
+      "version": "2.20.1",
+      "resolved": "https://registry.npmjs.org/nearley/-/nearley-2.20.1.tgz",
+      "integrity": "sha512-+Mc8UaAebFzgV+KpI5n7DasuuQCHA89dmwm7JXw3TV43ukfNQ9DnBH3Mdb2g/I4Fdxc26pwimBWvjIw0UAILSQ==",
+      "requires": {
+        "commander": "^2.19.0",
+        "moo": "^0.5.0",
+        "railroad-diagrams": "^1.0.0",
+        "randexp": "0.4.6"
+      }
     },
     "negotiator": {
       "version": "0.6.2",
@@ -16827,6 +16848,20 @@
       "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==",
       "requires": {
         "performance-now": "^2.1.0"
+      }
+    },
+    "railroad-diagrams": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/railroad-diagrams/-/railroad-diagrams-1.0.0.tgz",
+      "integrity": "sha512-cz93DjNeLY0idrCNOH6PviZGRN9GJhsdm9hpn1YCS879fj4W+x5IFJhhkRZcwVgMmFF7R82UA/7Oh+R8lLZg6A=="
+    },
+    "randexp": {
+      "version": "0.4.6",
+      "resolved": "https://registry.npmjs.org/randexp/-/randexp-0.4.6.tgz",
+      "integrity": "sha512-80WNmd9DA0tmZrw9qQa62GPPWfuXJknrmVmLcxvq4uZBdYqb1wYoKTmnlGUchvVWe0XiLupYkBoXVOxz3C8DYQ==",
+      "requires": {
+        "discontinuous-range": "1.0.0",
+        "ret": "~0.1.10"
       }
     },
     "randomatic": {
@@ -19843,6 +19878,22 @@
       "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw="
     },
+    "sql-formatter": {
+      "version": "11.0.2",
+      "resolved": "https://registry.npmjs.org/sql-formatter/-/sql-formatter-11.0.2.tgz",
+      "integrity": "sha512-6QumAdGHEnI5dXEq1d0aBRP876AyA9Wp/UE7wopKNA2Mp9sKGRKVqGgoWHk4dr0J0nceesC85Y0p36qmGoNqhw==",
+      "requires": {
+        "argparse": "^2.0.1",
+        "nearley": "^2.20.1"
+      },
+      "dependencies": {
+        "argparse": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+          "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="
+        }
+      }
+    },
     "ssf": {
       "version": "0.10.2",
       "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.10.2.tgz",
diff --git a/package.json b/package.json
index c177abb..8963e6d 100644
--- a/package.json
+++ b/package.json
@@ -96,6 +96,7 @@
     "sass-loader": "7.2.0",
     "semver": "6.3.0",
     "spark-md5": "^3.0.1",
+    "sql-formatter": "^11.0.2",
     "style-loader": "1.0.0",
     "terser-webpack-plugin": "1.4.1",
     "ts-pnp": "1.1.2",
diff --git a/src/api/cacheutils.js b/src/api/cacheutils.js
index ecf5475..542bfde 100644
--- a/src/api/cacheutils.js
+++ b/src/api/cacheutils.js
@@ -81,7 +81,6 @@
               resolve({version: '', createDate: ''})
             }
           }, (tx, results) => {
-            window.GLOB.WebSql = null
             console.warn(results)
             resolve({version: '', createDate: ''})
           })
@@ -99,7 +98,6 @@
             }
             resolve(menus)
           }, (tx, results) => {
-            window.GLOB.WebSql = null
             console.warn(results)
             resolve([])
           })
@@ -130,12 +128,14 @@
 
     if (!keys) {
       window.GLOB.WebSql.transaction(tx => {
-        tx.executeSql(`DELETE FROM CONFIGS`, [], () => {}, () => { window.GLOB.WebSql = null })
+        tx.executeSql(`DELETE FROM CONFIGS`, [], () => {}, (tx, results) => {
+          console.warn(results)
+        })
       })
     } else {
       window.GLOB.WebSql.transaction(tx => {
-        tx.executeSql(`DELETE FROM CONFIGS where menuid in (${keys})`, [], () => {}, () => {
-          window.GLOB.WebSql = null
+        tx.executeSql(`DELETE FROM CONFIGS where menuid in (${keys})`, [], () => {}, (tx, results) => {
+          console.warn(results)
         })
       })
     }
@@ -150,8 +150,8 @@
       window.GLOB.WebSql.transaction(tx => {
         tx.executeSql(`DELETE FROM CONFIGS where menuid='${menuId}'`, [], () => {
           resolve()
-        }, () => {
-          window.GLOB.WebSql = null
+        }, (tx, results) => {
+          console.warn(results)
           resolve()
         })
       })
@@ -168,8 +168,8 @@
       tx.executeSql(`DELETE FROM VERSIONS where CDefine1='LongParam'`)
 
       if (version) {
-        tx.executeSql('INSERT INTO VERSIONS (version, createDate, CDefine1) VALUES (?, ?, ?)', [version, curTime, 'LongParam'], () => {}, () => {
-          window.GLOB.WebSql = null
+        tx.executeSql('INSERT INTO VERSIONS (version, createDate, CDefine1) VALUES (?, ?, ?)', [version, curTime, 'LongParam'], () => {}, (tx, results) => {
+          console.warn(results)
         })
       }
     })
@@ -199,7 +199,6 @@
             reject()
           }
         }, (tx, results) => {
-          window.GLOB.WebSql = null
           console.warn(results)
           reject()
         })
diff --git a/src/api/index.js b/src/api/index.js
index d86149f..5179db8 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -2,6 +2,7 @@
 import qs from 'qs'
 import { notification } from 'antd'
 import md5 from 'md5'
+import CryptoJS from 'crypto-js'
 import jsSHA from 'jssha'
 import moment from 'moment'
 import Utils from '@/utils/utils.js'
@@ -10,6 +11,7 @@
 
 window.GLOB.WebSql = null
 window.GLOB.IndexDB = null
+window.GLOB.OuterToken = {}
 const systemMenuKeys = `1581067625930haged11ieaivpavv77k,1581734956310scks442ul2d955g9tu5,1583991994144ndddg0bhh0is6shi0v1,1583979633842550imkchl4qt4qppsiv,1578900109100np8aqd0a77q3na46oas,
   1585192949946f3et2ts8tn82krmumdf,15855615451212m12ip23vpcm79kloro,1587005717541lov40vg61q7l1rbveon,1590458676585agbbr63t6ihighg2i1g,1602315375262ikd33ii0nii34pt861o,1582771068837vsv54a089lgp45migbg,
   1582777675954ifu05upurs465omoth7,158294809668898cklbv6c5bou8e1fpu,1584676379094iktph45fb8imhg96bql,1584695125339vo5g7iqgfn01qmrd6s2,1584699661372vhmpp9dn9foo0eob722,15848421131551gg04ie8sitsd3f7467,
@@ -217,7 +219,8 @@
       timestamp: moment().format('YYYY-MM-DD HH:mm:ss'), 
       SessionUid: _SessionUid,
       TypeCharOne: 'pc',
-      kei_id: window.btoa(window.encodeURIComponent(window.GLOB.host))
+      kei_id: window.btoa(window.encodeURIComponent(window.GLOB.host)),
+      appkey: window.GLOB.appkey || ''
     }
 
     let url = '/webapi/dologon/s_visitor_login'
@@ -251,8 +254,6 @@
     param.v_type = param.v_type || 'Y'
     param.secretkey = md5(param.LText + solt + param.timestamp)
 
-    param.appkey = window.GLOB.appkey || ''
-
     return axios({
       url: url,
       method: 'post',
@@ -275,10 +276,9 @@
       login_city: city,
       login_id_address: ipAddress,
       kei_id: window.btoa(window.encodeURIComponent(window.GLOB.host)),
-      device_id: localStorage.getItem('SessionUid')
+      device_id: localStorage.getItem('SessionUid'),
+      appkey: window.GLOB.appkey || ''
     }
-
-    param.appkey = window.GLOB.appkey || ''
 
     let url = '/webapi/dologon'
     if (isCloud) {
@@ -312,10 +312,10 @@
       login_city: city,
       login_id_address: ipAddress,
       kei_id: window.btoa(window.encodeURIComponent(window.GLOB.host)),
-      device_id: localStorage.getItem('SessionUid')
+      device_id: localStorage.getItem('SessionUid'),
+      timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
+      appkey: window.GLOB.appkey || ''
     }
-
-    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
 
     let sys_datetime = sessionStorage.getItem('sys_datetime')
     let app_datetime = sessionStorage.getItem('app_datetime')
@@ -340,20 +340,17 @@
     // Type: 'X' 鏃�
     // param.Password = Utils.formatOptions(password)
 
-    param.appkey = window.GLOB.appkey || ''
     let url = '/webapi/dologon'
 
     if (isCloud) {
       param.debug = 'Y'
       if (options.cloudServiceApi) {
-        // url = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
         param.rduri = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       }
     } else if (window.GLOB.mainSystemApi) {
       if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
         param.linkurl = window.GLOB.linkurl
       }
-      // url = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       param.rduri = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
     }
 
@@ -367,7 +364,7 @@
   /**
    * @description 鑾峰彇绯荤粺鐗堟湰淇℃伅锛屽惎鐢ㄦ垨鏇存柊websql
    */
-  getAppVersion (_resolve, _reject) {
+  getAppVersion (reload) {
     if (!window.GLOB.WebSql && !window.GLOB.IndexDB) {
       return Promise.reject()
     }
@@ -398,7 +395,7 @@
             CacheUtils.updateWebSqlversion('1.00', curTime)
             resolve()
             return
-          } else if (!msg.createDate) {
+          } else if (!msg.createDate || reload === true) {
             param.modifydate = curTime
             param.menuids = window.btoa(msg.menuids)
           } else if (msg.menuids) {
@@ -423,18 +420,25 @@
               list.push(...res.menu_del)
             }
 
+            list = list.map(mid => mid.menuid)
+
+            if (typeof(reload) === 'string' && !list.includes(reload)) {
+              list.push(reload)
+            }
+
             if (list.length > 0) {
               let clear = false
               list.forEach(mid => {
-                if (systemMenuKeys.indexOf(mid.menuid) > -1) {
+                if (systemMenuKeys.indexOf(mid) > -1) {
                   clear = true
                 }
               })
-              let keys = list.map(mid => `'${mid.menuid}'`).join(',')
               if (clear) {
-                keys = ''
+                list = ''
+              } else {
+                list = list.map(mid => `'${mid}'`).join(',')
               }
-              CacheUtils.delWebSqlConfig(keys)
+              CacheUtils.delWebSqlConfig(list)
             }
 
             CacheUtils.updateWebSqlversion(res.app_version || '1.00', curTime)
@@ -460,7 +464,7 @@
             CacheUtils.updateIndexDBversion({version: '1.00', createDate: curTime})
             resolve()
             return
-          } else if (!msg.createDate) {
+          } else if (!msg.createDate || reload === true) {
             param.modifydate = curTime
             param.menuids = window.btoa(msg.menuids)
           } else if (msg.menuids) {
@@ -485,18 +489,23 @@
               list.push(...res.menu_del)
             }
 
+            list = list.map(mid => mid.menuid)
+
+            if (typeof(reload) === 'string' && !list.includes(reload)) {
+              list.push(reload)
+            }
+
             if (list.length > 0) {
               let clear = false
               list.forEach(mid => {
-                if (systemMenuKeys.indexOf(mid.menuid) > -1) {
+                if (systemMenuKeys.indexOf(mid) > -1) {
                   clear = true
                 }
               })
-              let keys = list.map(mid => mid.menuid)
               if (clear) {
-                keys = ''
+                list = ''
               }
-              CacheUtils.delIndexDBConfig(keys)
+              CacheUtils.delIndexDBConfig(list)
             }
 
             CacheUtils.updateIndexDBversion({version: res.app_version || '1.00', createDate: curTime})
@@ -527,11 +536,6 @@
   deleteMenuStorage (menuId) {
     if (window.GLOB.IndexDB) {
       let key = menuId + (sessionStorage.getItem('UserID') || '')
-    
-      if (sessionStorage.getItem('isEditState') === 'true' && options.cloudServiceApi) {
-        key = menuId + (sessionStorage.getItem('CloudUserID') || '')
-      }
-
       return CacheUtils.delMenuIndexDBConfig(key)
     } else {
       return CacheUtils.delMenuWebSqlConfig(menuId)
@@ -820,6 +824,117 @@
     }
   }
 
+  visitOuterSystem (param, _resolve) {
+    let token = param.$token
+    delete param.$token
+
+    try {
+      token = JSON.parse(token)
+      token = token.message
+
+      const key = CryptoJS.enc.Utf8.parse(window.GLOB.appkey.slice(-16))
+      const iv = CryptoJS.enc.Utf8.parse('mksoft')
+  
+      let encryptedHexStr = CryptoJS.enc.Hex.parse(token)
+      let _srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
+      let decrypt = CryptoJS.AES.decrypt(_srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
+      let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
+      token = decryptedStr.toString()
+  
+      token = JSON.parse(window.decodeURIComponent(window.atob(token)))
+    } catch (e) {
+      token = null
+      _resolve({status: false, ErrCode: 'E', message: '鎺ュ彛淇℃伅瑙f瀽澶辫触锛�'})
+    }
+
+    if (!token) return
+
+    let userid = ''
+    let loginUid = ''
+    let dataM = ''
+
+    if (window.GLOB.OuterToken[token.interface]) {
+      let msg = window.GLOB.OuterToken[token.interface]
+
+      let seconds = Math.floor((new Date().getTime() - msg.timestamp) / 1000)
+      if (seconds >= 3600) {
+        delete window.GLOB.OuterToken[token.interface]
+      } else {
+        userid = msg.userid
+        loginUid = msg.loginUid
+        dataM = msg.dataM || ''
+      }
+    }
+
+    // param.appkey = token.appkey || ''
+
+    if (userid && loginUid) {
+      param.dataM = dataM
+      param.userid = userid
+      param.LoginUID = loginUid
+      param = this.encryptParam(param)
+
+      axios({
+        url: token.interface,
+        method: 'post',
+        data: param
+      }).then(res => {
+        _resolve(res)
+      })
+    } else {
+      let _param = {
+        UserName: token.username,
+        systemType: options.sysType,
+        login_city: sessionStorage.getItem('city') || '',
+        device_id: token.appkey || '',
+        timestamp: moment().format('YYYY-MM-DD HH:mm:ss'),
+        Type: token.publicKey,
+        appkey: token.appkey || ''
+      }
+  
+      let shaObj = new jsSHA('SHA-1', 'TEXT')
+      shaObj.update(token.password)
+      _param.Password = shaObj.getHash('HEX').toUpperCase()
+      _param.Password = md5(token.privateKey + token.username + _param.Password + _param.timestamp)
+
+      let url = token.interface.replace(/\/webapi(.*)/, '/webapi/dologon')
+  
+      if (token.ssoInterface) {
+        _param.rduri = token.ssoInterface.replace(/\/webapi(.*)/, '/webapi/dologon')
+      }
+  
+      axios({
+        url,
+        method: 'post',
+        data: _param
+      }).then(result => {
+        if (result.status) {
+          window.GLOB.OuterToken[token.interface] = {
+            userid: result.UserID,
+            loginUid: result.LoginUID,
+            timestamp: new Date().getTime(),
+            dataM: result.dataM ? 'Y' : ''
+          }
+
+          param.dataM = result.dataM ? 'Y' : ''
+          param.userid = result.UserID
+          param.LoginUID = result.LoginUID
+          param = this.encryptParam(param)
+
+          axios({
+            url: token.interface,
+            method: 'post',
+            data: param
+          }).then(res => {
+            _resolve(res)
+          })
+        } else {
+          _resolve(result)
+        }
+      })
+    }
+  }
+
   /**
    * @description 鑾峰彇涓氬姟閫氱敤鎺ュ彛
    * 璁块棶 'https://sso.mk9h.cn/webapi/dostars'鎴栦簯绔椂锛屼紶鍏serid銆丩oginUID
@@ -831,6 +946,22 @@
     param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
+    if (param.$token === '') {
+      return Promise.resolve({status: false, ErrCode: 'E', message: '鎺ュ彛鍦板潃灏氭湭璁剧疆锛�'})
+    } else if (param.$token) {
+      return new Promise(resolve => this.visitOuterSystem(param, resolve))
+    }
+
+    if (['sPC_Get_TableData', 'sPC_Get_TableData_debug', 'sPC_TableData_InUpDe', 'sPC_TableData_InUpDe_debug', 'sPC_Get_structured_data'].includes(param.func)) {
+      if (sessionStorage.getItem('isEditState') === 'true') {
+        param.username = sessionStorage.getItem('CloudUserName') || ''
+        param.fullname = sessionStorage.getItem('CloudFullName') || ''
+      } else {
+        param.username = sessionStorage.getItem('User_Name') || ''
+        param.fullname = sessionStorage.getItem('Full_Name') || ''
+      }
+    }
+
     let login = false
     let rduri = null
 
diff --git a/src/assets/css/main.scss b/src/assets/css/main.scss
index 3cb368f..3e21319 100644
--- a/src/assets/css/main.scss
+++ b/src/assets/css/main.scss
@@ -264,28 +264,6 @@
   }
 }
 
-// .ant-modal.popview-modal {
-//   top: 70px;
-//   .ant-modal-body {
-//     min-height: 200px;
-//     max-height: calc(100vh - 210px);
-//     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-modal-wrap.popview-modal {
   .ant-modal {
     top: 70px;
diff --git a/src/assets/mobimg/indent.jfif b/src/assets/mobimg/indent.jfif
new file mode 100644
index 0000000..a5a0f73
--- /dev/null
+++ b/src/assets/mobimg/indent.jfif
Binary files differ
diff --git a/src/assets/mobimg/kapmap.jfif b/src/assets/mobimg/kapmap.jfif
new file mode 100644
index 0000000..cdc60fc
--- /dev/null
+++ b/src/assets/mobimg/kapmap.jfif
Binary files differ
diff --git a/src/assets/mobimg/mindmap.png b/src/assets/mobimg/mindmap.png
new file mode 100644
index 0000000..a2e7e2b
--- /dev/null
+++ b/src/assets/mobimg/mindmap.png
Binary files differ
diff --git a/src/components/breadview/index.jsx b/src/components/breadview/index.jsx
index 62ae100..dc1e950 100644
--- a/src/components/breadview/index.jsx
+++ b/src/components/breadview/index.jsx
@@ -14,7 +14,7 @@
 const Home = asyncComponent(() => import('@/tabviews/home'))
 const CustomPage = asyncComponent(() => import('@/tabviews/custom'))
 const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
-// const BaseTable = asyncComponent(() => import('@/tabviews/basetable'))
+const BaseTable = asyncComponent(() => import('@/tabviews/basetable'))
 const CalendarPage = asyncComponent(() => import('@/tabviews/calendar'))
 const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
 const Iframe = asyncComponent(() => import('@/tabviews/iframe'))
@@ -79,16 +79,22 @@
     }
   }
 
+  changeTemp = (MenuID, Template) => {
+    this.setState({
+      tabview: {...this.state.tabview, type: Template}
+    })
+  }
+
   selectcomponent = (view) => {
     // 鏍规嵁tab椤典腑鑿滃崟淇℃伅锛岄�夋嫨鎵�闇�鐨勭粍浠�
     if (view.type === 'Home') {
       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 === 'BaseTable') {
-    //   return (<BaseTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+      return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
+    } else if (view.type === 'BaseTable') {
+      return (<BaseTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
     } else if (view.type === 'CustomPage') {
-      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
     } else if (view.type === 'TreePage') {
       return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
     } else if (view.type === 'CalendarPage') {
diff --git a/src/components/encrypts/index.jsx b/src/components/encrypts/index.jsx
index 8d6617d..7ed752e 100644
--- a/src/components/encrypts/index.jsx
+++ b/src/components/encrypts/index.jsx
@@ -13,6 +13,12 @@
     eye: false
   }
 
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (nextProps.value !== this.props.value) {
+      this.setState({eye: false})
+    }
+  }
+
   getValue = () => {
     const { value } = this.props
 
@@ -44,7 +50,7 @@
     return (
       <>
         {eye ? value : this.getValue()}
-        {eye ? <EyeOutlined className="mk-open-eye" onClick={() => this.setState({eye: false})}/> : <EyeInvisibleOutlined className="mk-close-eye" onClick={() => this.setState({eye: true})}/>}
+        {eye ? <EyeOutlined className="mk-open-eye" onClick={(e) => {e.stopPropagation();this.setState({eye: false})}}/> : <EyeInvisibleOutlined className="mk-close-eye" onClick={(e) => {e.stopPropagation();this.setState({eye: true})}}/>}
       </>
     )
   }
diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx
index 48a84e0..b5f7700 100644
--- a/src/components/header/index.jsx
+++ b/src/components/header/index.jsx
@@ -495,6 +495,13 @@
     window.addEventListener('storage', (e) => {
       if (e.key === 'getSessionStorage' && e.newValue === window.GLOB.appkey) {
         localStorage.setItem('sessionStorage', JSON.stringify(sessionStorage))
+      } else if (e.key === 'menuUpdate') {
+        let menuId = e.newValue.split(',')[1]
+        if (menuId) {
+          Api.getAppVersion(menuId).then(() => {
+            MKEmitter.emit('reloadMenuView', menuId)
+          })
+        }
       }
     })
   }
diff --git a/src/components/keyInterface/index.jsx b/src/components/keyInterface/index.jsx
new file mode 100644
index 0000000..80074b2
--- /dev/null
+++ b/src/components/keyInterface/index.jsx
@@ -0,0 +1,273 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import CryptoJS from 'crypto-js'
+import { Input, Modal, Form, notification } from 'antd'
+import { EditOutlined, DeleteOutlined } from '@ant-design/icons'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class KeyInterface extends Component {
+  static propTpyes = {
+    type: PropTypes.string,
+    onChange: PropTypes.func
+  }
+
+  state = {
+    url: '',
+    key: '',
+    visible: false,
+    setting: null
+  }
+
+  componentDidMount() {
+    const { value, type } = this.props
+
+    if (value) {
+      let content = value
+      try {
+        content = JSON.parse(content) 
+      } catch (e) {
+        content = {}
+      }
+
+      this.setState({setting: content.message || null, url: content.url || ''})
+    }
+
+    if (type === 'develop') {
+      this.setState({key: window.GLOB.appkey.slice(-16)})
+    }
+  }
+
+  editKey = () => {
+    let _setting = this.state.setting
+
+    if (this.state.key && _setting && typeof(_setting) === 'string') {
+      _setting = this.decrypt(this.state.key, _setting)
+    }
+
+    this.setState({visible: true, setting: _setting || {}})
+  }
+
+  decrypt = (token, value) => {
+    let setting = null
+    try {
+      const key = CryptoJS.enc.Utf8.parse(token)
+      const iv = CryptoJS.enc.Utf8.parse('mksoft')
+  
+      let encryptedHexStr = CryptoJS.enc.Hex.parse(value)
+      let _srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr)
+      let decrypt = CryptoJS.AES.decrypt(_srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
+      let decryptedStr = decrypt.toString(CryptoJS.enc.Utf8)
+      setting = decryptedStr.toString()
+  
+      setting = JSON.parse(window.decodeURIComponent(window.atob(setting)))
+    } catch (e) {
+      notification.warning({
+        top: 92,
+        message: '淇℃伅瑙f瀽澶辫触!',
+        duration: 5
+      })
+      setting = {}
+    }
+
+    return setting
+  }
+
+  handleConfirm = () => {
+    this.props.form.validateFieldsAndScroll((err, values) => {
+      if (err) return
+
+      if (values.apptoken) {
+        let key = values.apptoken.slice(-16)
+        let _setting = this.state.setting
+        if (_setting && typeof(_setting) === 'string') {
+          _setting = this.decrypt(key, _setting)
+        }
+        this.setState({key: key, setting: _setting})
+        return
+      }
+
+      if (!/\/dostars$/.test(values.interface)) {
+        notification.warning({
+          top: 92,
+          message: '鎺ュ彛鍦板潃璇峰~鍐檇ostars鎺ュ彛!',
+          duration: 5
+        })
+        return
+      } else if (values.ssoInterface && !/\/dostars$/.test(values.ssoInterface)) {
+        notification.warning({
+          top: 92,
+          message: 'sso鍦板潃璇峰~鍐檇ostars鎺ュ彛!',
+          duration: 5
+        })
+        return
+      }
+
+      let _values = window.btoa(window.encodeURIComponent(JSON.stringify(values)))
+
+      const key = CryptoJS.enc.Utf8.parse(this.state.key)
+      const iv = CryptoJS.enc.Utf8.parse('mksoft')
+
+      let srcs = CryptoJS.enc.Utf8.parse(_values)
+      let encrypted = CryptoJS.AES.encrypt(srcs, key, { iv: iv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 })
+      let message = encrypted.ciphertext.toString()
+      let content = JSON.stringify({message: message, url: values.interface})
+
+      this.props.onChange(content)
+
+      this.setState({setting: message, url: values.interface, visible: false})
+    })
+  }
+
+  delKey = () => {
+    const { type } = this.props
+
+    if (type === 'develop') {
+      this.setState({setting: null, url: ''})
+    } else {
+      this.setState({key: '', setting: null, url: ''})
+    }
+
+    this.props.onChange('')
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+    const { url, visible, setting, key } = this.state
+
+    return (
+      <div className="mk-key-wrap">
+        <TextArea value={url} rows={2} readOnly={true}/>
+        <div className="mk-key-edit"><EditOutlined onClick={this.editKey}/><DeleteOutlined onClick={this.delKey}/></div>
+        <Modal
+          wrapClassName='mk-key-modal'
+          visible={visible}
+          closable={false}
+          maskClosable={false}
+          width={650}
+          onOk={this.handleConfirm}
+          onCancel={() => this.setState({visible: false})}
+          destroyOnClose
+        >
+          {key && setting ?
+            <Form>
+              <Form.Item label="鎺ュ彛鍦板潃">
+                {getFieldDecorator('interface', {
+                  initialValue: setting.interface,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ユ帴鍙e湴鍧�!'
+                    },
+                    {
+                      pattern: /^[0-9a-zA-Z:_./]+$/,
+                      message: '鍙彲浣跨敤鑻辨枃銆佹暟瀛椾互鍙�:_./'
+                    }
+                  ]
+                })(<TextArea rows={2}/>)}
+              </Form.Item>
+              <Form.Item label="sso鍦板潃">
+                {getFieldDecorator('ssoInterface', {
+                  initialValue: setting.ssoInterface || '',
+                  rules: [
+                    {
+                      pattern: /^[0-9a-zA-Z:_./]+$/,
+                      message: '鍙彲浣跨敤鑻辨枃銆佹暟瀛椾互鍙�:_./'
+                    }
+                  ]
+                })(<TextArea rows={2}/>)}
+              </Form.Item>
+              <Form.Item label="appkey">
+                {getFieldDecorator('appkey', {
+                  initialValue: setting.appkey || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ppkey!'
+                    },
+                    {
+                      pattern: /^[0-9a-zA-Z]+$/,
+                      message: '鍙彲杈撳叆鑻辨枃鍙婃暟瀛椼��'
+                    },
+                    {
+                      min: 16,
+                      message: '涓嶅彲灏忎簬16浣嶏紒'
+                    }
+                  ]
+                })(<Input placeholder="璇疯緭鍏ppkey" autoComplete="off" />)}
+              </Form.Item>
+              <Form.Item label="鐢ㄦ埛鍚�">
+                {getFieldDecorator('username', {
+                  initialValue: setting.username,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ョ敤鎴峰悕!'
+                    }
+                  ]
+                })(<Input placeholder="璇疯緭鍏ョ敤鎴峰悕" autoComplete="off" />)}
+              </Form.Item>
+              <Form.Item label="瀵嗙爜">
+                {getFieldDecorator('password', {
+                  initialValue: setting.password,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ュ瘑鐮�!'
+                    }
+                  ]
+                })(<Input.Password placeholder="璇疯緭鍏ュ瘑鐮�" />)}
+              </Form.Item>
+              <Form.Item label="鍏挜">
+                {getFieldDecorator('publicKey', {
+                  initialValue: setting.publicKey,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ュ叕閽�!'
+                    }
+                  ]
+                })(<Input.Password placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+              <Form.Item label="绉侀挜">
+                {getFieldDecorator('privateKey', {
+                  initialValue: setting.privateKey,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇疯緭鍏ョ閽�!'
+                    }
+                  ]
+                })(<Input.Password placeholder={''} autoComplete="off" />)}
+              </Form.Item>
+            </Form> : null}
+          {!key ? <Form style={{marginTop: '20px', marginBottom: '50px'}}>
+            <Form.Item label="apptoken">
+              {getFieldDecorator('apptoken', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ユ寮忕郴缁焌ppkey!'
+                  },
+                  {
+                    pattern: /^[0-9a-zA-Z]+$/,
+                    message: '鍙彲杈撳叆鑻辨枃鍙婃暟瀛椼��'
+                  },
+                  {
+                    min: 16,
+                    message: '涓嶅彲灏忎簬16浣嶏紒'
+                  }
+                ]
+              })(<Input placeholder="璇疯緭鍏ユ寮忕郴缁焌ppkey" autoComplete="off" />)}
+            </Form.Item>
+          </Form> : null}
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(KeyInterface)
\ No newline at end of file
diff --git a/src/components/keyInterface/index.scss b/src/components/keyInterface/index.scss
new file mode 100644
index 0000000..7753a5e
--- /dev/null
+++ b/src/components/keyInterface/index.scss
@@ -0,0 +1,48 @@
+.mk-key-wrap {
+  position: relative;
+  margin-bottom: 5px;
+
+  .mk-key-edit {
+    position: absolute;
+    right: 0px;
+    line-height: 1;
+
+    .anticon {
+      margin-left: 10px;
+      cursor: pointer;
+      padding: 0px 5px;
+    }
+    .anticon-edit {
+      color: #1890ff;
+    }
+    .anticon-delete {
+      color: #f5222d;
+    }
+  }
+}
+.mk-key-modal {
+  .ant-modal {
+    top: 85px;
+  }
+  .ant-modal-body {
+    padding-top: 35px;
+    padding-bottom: 0px;
+  }
+  .ant-modal-footer {
+    border-top: none;
+    text-align: center;
+    padding-bottom: 30px;
+    button + button {
+      margin-left: 15px;
+    }
+  }
+  .ant-form-item {
+    display: flex;
+    .ant-form-item-label {
+      width: 20%;
+    }
+    .ant-form-item-control-wrapper {
+      width: 70%;
+    }
+  }
+}
diff --git a/src/components/normalform/modalform/index.jsx b/src/components/normalform/modalform/index.jsx
index ce8b71d..6a2636b 100644
--- a/src/components/normalform/modalform/index.jsx
+++ b/src/components/normalform/modalform/index.jsx
@@ -57,6 +57,10 @@
           required: item.required,
           message: item.label + '涓嶅彲涓虹┖!'
         }]
+
+        if (item.rules) {
+          _rules.push(...item.rules)
+        }
         
         item.rules = _rules
       } else if (item.type === 'number') {
diff --git a/src/components/normalform/modalform/styleInput/index.jsx b/src/components/normalform/modalform/styleInput/index.jsx
index 3925b2a..c6d6b45 100644
--- a/src/components/normalform/modalform/styleInput/index.jsx
+++ b/src/components/normalform/modalform/styleInput/index.jsx
@@ -23,11 +23,11 @@
   UNSAFE_componentWillMount () {
     const { config } = this.props
 
-    let val = config.initval || ''
+    let val = config.initval || config.initVal || ''
     let options = config.options || ['px', 'vh', 'vw', '%']
     let unit = options[0]
 
-    if (val) {
+    if (val && typeof(val) === 'string') {
       if (val.indexOf('px') > -1) {
         unit = 'px'
       } else if (val.indexOf('%') > -1) {
diff --git a/src/components/normalform/modalform/styleInput/index.scss b/src/components/normalform/modalform/styleInput/index.scss
index b058a37..fc0085d 100644
--- a/src/components/normalform/modalform/styleInput/index.scss
+++ b/src/components/normalform/modalform/styleInput/index.scss
@@ -8,4 +8,7 @@
     text-align: left;
     color: rgba(255, 255, 255, 0.65);
   }
+  div[title="vh"], div[title="vw"] {
+    color: #1890ff;
+  }
 }
diff --git a/src/components/qrcode/index.scss b/src/components/qrcode/index.scss
index 6b05972..d6972c1 100644
--- a/src/components/qrcode/index.scss
+++ b/src/components/qrcode/index.scss
@@ -1,5 +1,6 @@
 .qrcode-box {
   display: inline-block;
+  position: relative;
   canvas {
     vertical-align: top;
     max-width: 100%;
diff --git a/src/components/querylog/index.jsx b/src/components/querylog/index.jsx
index 16337a5..380fc19 100644
--- a/src/components/querylog/index.jsx
+++ b/src/components/querylog/index.jsx
@@ -1,4 +1,5 @@
 import { Component } from 'react'
+import { notification } from 'antd'
 import moment from 'moment'
 
 import Api from '@/api'
@@ -6,7 +7,7 @@
 import MKEmitter from '@/utils/events.js'
 
 /**
- * @description 鎿嶄綔璁板綍锛屾瘡闅斿叚鍒嗛挓鏇存柊涓�娆�
+ * @description 鎿嶄綔璁板綍锛屾瘡闅�10鍒嗛挓鏇存柊涓�娆�
  */
 class QueryLog extends Component {
   state = {
@@ -14,12 +15,10 @@
   }
 
   componentDidMount () {
-    if (window.GLOB.systemType === 'production') {
-      MKEmitter.addListener('queryTrigger', this.queryTrigger)
-      setTimeout(() => {
-        this.sendLog()
-      }, 300000)
-    }
+    MKEmitter.addListener('queryTrigger', this.queryTrigger)
+    setTimeout(() => {
+      this.sendLog()
+    }, 600000)
   }
 
   /**
@@ -35,40 +34,66 @@
   sendLog = () => {
     const { logs } = this.state
 
-    if (logs && logs.length > 0 && sessionStorage.getItem('isEditState') !== 'true') {
-      let logMap = new Map()
+    let logMap = new Map()
 
-      logs.forEach(item => {
-        if (logMap.has(item.menuId)) {
-          let _item = logMap.get(item.menuId)
-          _item.times++
-          logMap.set(item.menuId, _item)
-        } else {
-          item.times = 1
-          logMap.set(item.menuId, item)
-        }
-      })
-      let userid = sessionStorage.getItem('UserID') || ''
-      let LText = [...logMap.values()].map(item => `select '${item.menuId}','${item.times}','${item.name}','${window.GLOB.appkey}','${userid}'`)
+    logs.forEach(item => {
+      if (logMap.has(item.menuId)) {
+        let _item = logMap.get(item.menuId)
+        _item.times++
+        logMap.set(item.menuId, _item)
+      } else {
+        item.times = 1
+        logMap.set(item.menuId, item)
+      }
+    })
+
+    let userid = sessionStorage.getItem('UserID') || ''
+    let LText = [...logMap.values()].map(item => `select '${item.menuId}','${item.times}','${item.name}','${window.GLOB.appkey}','${userid}'`)
+
+    Api.genericInterface({func: 's_get_local_users_operation_log'}).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 3
+        })
+        return
+      }
+
+      if (LText.length === 0 && !res.long_param) {
+        setTimeout(() => {
+          this.sendLog()
+        }, 600000)
+        return
+      }
 
       let param = {
         func: 's_get_users_operation_log',
-        exec_type: 'y', // 鍚庡彴瑙g爜
-        LText: LText.join(' union all ')
+        exec_type: 'y',
+        LText: LText.join(' union all '),
+        long_param: res.long_param
       }
 
       param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
       param.secretkey = Utils.encrypt('', param.timestamp)
       param.LText = Utils.formatOptions(param.LText)
 
-      Api.getSystemConfig(param)
+      Api.getSystemConfig(param).then(result => {
+        if (!result.status) {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 3
+          })
+          return
+        }
 
-      this.setState({logs: []})
-    }
-
-    setTimeout(() => {
-      this.sendLog()
-    }, 300000)
+        setTimeout(() => {
+          this.sendLog()
+        }, 600000)
+        this.setState({logs: []})
+      })
+    })
   }
 
   queryTrigger = (item) => {
diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index ffb0f08..ed050db 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -17,13 +17,13 @@
 const Home = asyncComponent(() => import('@/tabviews/home'))
 const CustomPage = asyncComponent(() => import('@/tabviews/custom'))
 const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
-// const BaseTable = asyncComponent(() => import('@/tabviews/basetable'))
+const BaseTable = asyncComponent(() => import('@/tabviews/basetable'))
 const CalendarPage = asyncComponent(() => import('@/tabviews/calendar'))
 const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
-const TabManage = asyncComponent(() => import('@/tabviews/tabmanage'))
 const Iframe = asyncComponent(() => import('@/tabviews/iframe'))
 const RoleManage = asyncComponent(() => import('@/tabviews/rolemanage'))
 const FormTab = asyncComponent(() => import('@/tabviews/formtab'))
+const TabManage = asyncComponent(() => import('@/tabviews/tabmanage'))
 
 class TabViews extends Component {
   static propTpyes = {
@@ -109,7 +109,7 @@
   
       // 鑾峰彇涓昏彍鍗曞弬鏁�
       let menudefer = new Promise(resolve => {
-        Api.getAppVersion().then(() => {
+        Api.getAppVersion(menu.MenuID).then(() => {
           resolve()
         }, () => {
           resolve()
@@ -185,26 +185,39 @@
     })
   }
 
+  changeTemp = (MenuID, Template) => {
+    let tabs = fromJS(this.state.tabviews).toJS()
+
+    this.setState({
+      tabviews: tabs.map(item => {
+        if (item.MenuID === MenuID) {
+          item.type = Template
+        }
+        return item
+      })
+    })
+  }
+
   selectcomponent = (view) => {
     // 鏍规嵁tab椤典腑鑿滃崟淇℃伅锛岄�夋嫨鎵�闇�鐨勭粍浠�
     if (view.type === 'Home') {
       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 === 'BaseTable') {
-    //   return (<BaseTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+      return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
+    } else if (view.type === 'BaseTable') {
+      return (<BaseTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
     } else if (view.type === 'CustomPage') {
-      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param} changeTemp={this.changeTemp}/>)
     } else if (view.type === 'TreePage') {
       return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
     } else if (view.type === 'CalendarPage') {
       return (<CalendarPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
-    } else if (view.type === 'TabManage') {
-      return (<TabManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'RolePermission') {
       return (<RoleManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
     } else if (view.type === 'FormTab') {
       return (<FormTab MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'TabManage') {
+      return (<TabManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
     } else if (view.type === 'iframe') {
       return (<Iframe key={view.MenuID} MenuID={view.MenuID} MenuNo={view.MenuNo} title={view.MenuName} MenuName={view.MenuName} url={window.GLOB.baseurl + 'zh-CN/' + view.LinkUrl}/>)
     } else {
diff --git a/src/menu/components/card/balcony/index.jsx b/src/menu/components/card/balcony/index.jsx
index 31c5004..4c89067 100644
--- a/src/menu/components/card/balcony/index.jsx
+++ b/src/menu/components/card/balcony/index.jsx
@@ -191,7 +191,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/menu/components/card/balcony/index.scss b/src/menu/components/card/balcony/index.scss
index 97c2b34..7253e9b 100644
--- a/src/menu/components/card/balcony/index.scss
+++ b/src/menu/components/card/balcony/index.scss
@@ -57,24 +57,6 @@
     right: -30px;
     font-size: 16px;
   }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
 }
 .menu-balcony-edit-box::after {
   display: block;
diff --git a/src/menu/components/card/balcony/options.jsx b/src/menu/components/card/balcony/options.jsx
index c39fa7b..46f7bec 100644
--- a/src/menu/components/card/balcony/options.jsx
+++ b/src/menu/components/card/balcony/options.jsx
@@ -1,15 +1,14 @@
 import React from 'react'
-import { fromJS } from 'immutable'
 import MenuUtils from '@/utils/utils-custom.js'
 
 /**
  * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
  */
 export default function (wrap) {
-  let menu = fromJS(window.GLOB.customMenu).toJS()
+  let menu = window.GLOB.customMenu
 
   let modules = MenuUtils.getLinkModules(menu.components) || []
-  let supmodules = MenuUtils.getSupModules(menu.components, '') || []
+  let supmodules = MenuUtils.getSupModules(menu.components, '', menu.interfaces)
   let roleList = sessionStorage.getItem('sysRoles')
   let appType = sessionStorage.getItem('appType')
 
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
index 31328d0..95f0033 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -66,6 +66,12 @@
     btnElement = (<Button style={_style}>{card.icon ? <MkIcon type={card.icon}/> : null}{card.label}{warning}</Button>)
   }
 
+  let _style_ = null
+
+  if (card.style && card.style.clear === 'left') {
+    _style_ = {clear: 'left'}
+  }
+
   return (
     <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
       <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
@@ -76,8 +82,8 @@
         {hasProfile ? <ProfileOutlined className="profile" title="setting" onClick={() => profileCard(id)} /> : null}
       </div>
     } trigger="hover">
-      <div ref={node => drag(drop(node))} className={'ant-col card-button-cell ant-col-' + card.width + (card.hidden === 'true' ? ' mk-hidden' : '')} onDoubleClick={(e) => {e.stopPropagation(); doubleClickCard(id)}}>
-        <div style={{opacity: isDragging ? 0 : 1, ...card.wrapStyle}}>
+      <div ref={node => drag(drop(node))} style={_style_} className={'ant-col card-button-cell ant-col-' + card.width + (card.hidden === 'true' ? ' mk-hidden' : '')} onDoubleClick={(e) => {e.stopPropagation(); doubleClickCard(id)}}>
+        <div style={{opacity: isDragging ? 0.3 : 1, ...card.wrapStyle}}>
           {btnElement}
         </div>
       </div>
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
index 0410a0b..f9657cf 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -57,10 +57,10 @@
     },
   })
 
-  let _style = {opacity: isDragging ? 0 : 1}
+  let _style = {opacity: isDragging ? 0.3 : 1}
   
   if (card.style) {
-    _style = {...card.style, opacity: isDragging ? 0 : 1}
+    _style = {...card.style, opacity: isDragging ? 0.3 : 1}
     _style = resetStyle(_style)
   }
   if (card.eleType === 'picture' && card.maxWidth) {
@@ -89,7 +89,7 @@
         <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
       )
     } else if (card.eleType === 'icon') {
-      return (<MkIcon style={{height: card.innerHeight || 'auto'}} className="ant-mk-icon" type={card.icon}/>)
+      return (<MkIcon style={{height: card.innerHeight || 'auto'}} className="ant-mk-icon" type={card.icon || 'bell'}/>)
     } else if (card.eleType === 'slider') {
       let val = card.value ? (card.value / card.maxValue) * 100 : 30
       return <MkProgress value={val} config={card}/>
@@ -105,6 +105,7 @@
       let url = card.url !== '@icon@' ? card.url : sessionStorage.getItem('CloudAvatar')
 
       if (url) {
+        url = url.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
         _imagestyle = {backgroundImage: `url('${url}')`}
       } else {
         let index = card.uuid.match(/\d{1}/g)
@@ -190,6 +191,12 @@
     able = false
   }
 
+  let _style_ = null
+
+  if (card.style && card.style.clear === 'left') {
+    _style_ = {clear: 'left'}
+  }
+
   return (
     <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
       <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
@@ -200,7 +207,7 @@
         {['text', 'number', 'slider', 'sequence', 'formula'].includes(card.eleType) ? <MarkColumn field={card.field || ''} columns={fields} type={card.eleType} marks={card.marks} onSubmit={(vals) => updateMarks({...card, marks: vals})} /> : null }
       </div>
     } trigger="hover">
-      <div ref={node => drag(drop(node))} className={'ant-col card-cell ant-col-' + card.width}>
+      <div ref={node => drag(drop(node))} style={_style_} className={'ant-col card-cell ant-col-' + card.width}>
         <div style={_style} onClick={clickComponent} onDoubleClick={() => able && editCard(id)} id={card.uuid}>
           {getContent()}
         </div>
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/index.jsx b/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
index 683cd5f..d2d89ac 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -130,6 +130,14 @@
           newcard.eleType = item.value
           newcard.datatype = 'dynamic'
           newcard.height = 1
+
+          if (item.value === 'splitline') {
+            newcard.width = 24
+            newcard.color = '#EBE9E9'
+          } else if (item.value === 'slider') {
+            newcard.width = 24
+            newcard.color = '#1890ff'
+          }
         } else {
           newcard.eleType = 'button'
           newcard.label = 'button'
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index fa8a097..a85f253 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -19,7 +19,7 @@
   number: ['eleType', 'datatype', 'width', 'height', 'prefix', 'postfix', 'noValue', 'fixStyle'],
   picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'maxWidth', 'link', 'noValue'],
   video: ['eleType', 'datatype', 'width', 'aspectRatio', 'autoPlay', 'loop', 'startTime', 'noValue', 'posterType'],
-  icon: ['eleType', 'icon', 'datatype', 'width'],
+  icon: ['eleType', 'datatype', 'width', 'tooltip'],
   slider: ['eleType', 'datatype', 'width', 'color', 'maxValue', 'showInfo', 'showType', 'strokeWidth', 'strokeLinecap', 'trailColor'],
   splitline: ['eleType', 'color', 'width', 'borderWidth'],
   barcode: ['eleType', 'datatype', 'width', 'barHeight', 'displayValue', 'interval', 'noValue'],
@@ -144,7 +144,7 @@
       if (datatype === 'dynamic') {
         _options.push('field')
       } else {
-        _options.push('tooltip')
+        _options.push('icon')
       }
     }
     if (_options.includes('fixStyle') && fixStyle === 'alone') {
@@ -215,6 +215,10 @@
           item.initVal = posterType
         } else if (item.key === 'fixStyle') {
           item.initVal = fixStyle
+        } else if (item.key === 'color') {
+          if (value === 'splitline') {
+            item.initVal = '#EBE9E9'
+          }
         }
 
         return item
@@ -226,7 +230,9 @@
         showType: card.showType || 'line',
         formlist: _formlist
       }, () => {
-        if (value === 'slider') {
+        if (value === 'splitline') {
+          this.props.form.setFieldsValue({width: 24, color: '#EBE9E9'})
+        } else if (value === 'slider') {
           this.props.form.setFieldsValue({width: 24, color: '#1890ff'})
         } else if (value === 'qrcode') {
           this.props.form.setFieldsValue({color: '#000000'})
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index 02e8d29..0475028 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -1,9 +1,11 @@
+import MenuUtils from '@/utils/utils-custom.js'
+
 /**
  * @description 鑾峰彇鍏冪礌閰嶇疆淇℃伅
  * @param {*} card
  * @param {*} type
  */
-export function getCardCellForm (card, type, subtype, cardCell, anchors) {
+export function getCardCellForm (card, cards, cardCell) {
   let _options = [
     { value: 'text', text: '鏂囨湰'},
     { value: 'number', text: '鏁板��'},
@@ -18,27 +20,22 @@
     { value: 'formula', text: '鍏紡'},
   ]
 
-  if (type === 'table' || (type === 'card' && subtype === 'datacard')) {
+  let anchors = []
+  if (window.GLOB.customMenu.Template === 'BaseTable') {
+    anchors = null
+  } else {
+    anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
+  }
+
+  if (cards.type === 'table' || (cards.type === 'card' && cards.subtype === 'datacard')) {
     _options.push({value: 'sequence', text: '搴忓彿'})
   } else if (card.eleType === 'sequence') { // 鎷栨嫿娣诲姞绫诲瀷杞崲
     card.eleType = 'text'
   }
-
-  let appMenus = []
-  const isApp = sessionStorage.getItem('appType') === 'pc'
-
-  if (isApp) {
-    appMenus = sessionStorage.getItem('appMenus')
-    if (appMenus) {
-      try {
-        appMenus = JSON.parse(appMenus)
-      } catch (e) {
-        appMenus = []
-      }
-    } else {
-      appMenus = []
-    }
+  if (card.eleType === 'icon' && card.datatype === 'dynamic' && !card.field) { // 鎷栨嫿娣诲姞绫诲瀷杞崲
+    card.datatype = 'static'
   }
+  let appType = sessionStorage.getItem('appType')
 
   let tooltip = ''
   if (cardCell.$cardType === 'extendCard') {
@@ -53,13 +50,6 @@
       initVal: card.eleType,
       required: true,
       options: _options
-    },
-    {
-      type: 'icon',
-      key: 'icon',
-      label: '鍥炬爣',
-      initVal: card.icon,
-      required: true
     },
     {
       type: 'radio',
@@ -80,6 +70,13 @@
       initVal: card.field || '',
       required: true,
       options: []
+    },
+    {
+      type: 'icon',
+      key: 'icon',
+      label: '鍥炬爣',
+      initVal: card.icon,
+      required: true
     },
     {
       type: 'text',
@@ -225,7 +222,8 @@
       key: 'tooltip',
       label: '鎻愮ず淇℃伅',
       initVal: card.tooltip || '',
-      tooltip: '榧犳爣鎮诞鏃舵樉绀恒��',
+      tooltip: '榧犳爣鎮诞鏃舵樉绀恒�傛敞锛氬浣跨敤鍔ㄦ�佷俊鎭紝璇峰湪姝ゅ濉啓鐩稿簲鐨勫瓧娈靛悕銆�',
+      forbid: appType === 'mob',
       required: false
     },
     {
@@ -364,7 +362,8 @@
       initVal: card.anchors || [],
       tooltip: sessionStorage.getItem('appType') === 'mob' ? '娉細灏忕▼搴忎腑鏃犳晥' : '',
       required: false,
-      options: anchors
+      options: anchors,
+      forbid: !anchors
     },
     {
       type: 'number',
@@ -500,27 +499,6 @@
     },
     {
       type: 'select',
-      key: 'linkmenu',
-      label: '鍏宠仈鑿滃崟',
-      initVal: card.linkmenu || '',
-      required: true,
-      forbid: !isApp,
-      options: appMenus
-    },
-    // {
-    //   type: 'radio',
-    //   key: 'open',
-    //   label: '鎵撳紑鏂瑰紡',
-    //   initVal: card.open || 'blank',
-    //   required: false,
-    //   forbid: !isApp,
-    //   options: [
-    //     { value: 'blank', text: '鏂伴〉闈�' },
-    //     { value: 'self', text: '褰撳墠椤甸潰' }
-    //   ]
-    // },
-    {
-      type: 'select',
       key: 'linkurl',
       label: '閾炬帴鍦板潃',
       initVal: card.linkurl || '',
@@ -570,7 +548,7 @@
       key: 'noValue',
       label: '绌哄��',
       initVal: card.noValue || 'show',
-      tooltip: '褰撳厓绱犲唴瀹逛负绌烘椂锛屾槸鍚︽樉绀哄綋鍓嶅厓绱犮��',
+      tooltip: '褰撳厓绱犲唴瀹逛负绌烘椂锛屾槸鍚︽樉绀哄綋鍓嶅厓绱犮�傛敞锛氭暟鍊肩被鍨嬪厓绱犲寘鎷暟瀛�0锛堥潪鏂囨湰锛夈��',
       required: false,
       options: [
         { value: 'show', text: '鏄剧ず' },
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index 8ab612e..d2529af 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -5,7 +5,7 @@
 
 import asyncComponent from '@/utils/asyncComponent'
 import { getCardCellForm } from './formconfig'
-import { getActionForm } from '@/menu/components/share/actioncomponent/formconfig'
+import { getActionForm, getBaseTableActionForm } from '@/menu/components/share/actioncomponent/formconfig'
 
 import MKEmitter from '@/utils/events.js'
 import MenuUtils from '@/utils/utils-custom.js'
@@ -162,6 +162,8 @@
       options = ['padding', 'margin']
     }
 
+    options.push('clear')
+
     this.setState({
       card: element
     })
@@ -260,12 +262,10 @@
     if (card.eleType === 'button') {
       this.handleAction(card)
     } else {
-      let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
-
       this.setState({
         visible: true,
         card: card,
-        formlist: getCardCellForm(card, cards.type, cards.subtype, cardCell, anchors)
+        formlist: getCardCellForm(card, cards, cardCell)
       })
     }
   }
@@ -303,29 +303,34 @@
       }
     }
 
-    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, cards.uuid, supId) || []
-    let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, cards.uuid, supId)
 
-    this.setState({
-      actvisible: true,
-      card: card,
-      formlist: getActionForm(card, functip, cards, usefulFields, modules, anchors)
-    })
+    if (cards.subtype === 'basetable') {
+      this.setState({
+        actvisible: true,
+        card: card,
+        formlist: getBaseTableActionForm(card, functip, cards, usefulFields, modules)
+      })
+    } else {
+      let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, cards.uuid) || []
+  
+      this.setState({
+        actvisible: true,
+        card: card,
+        formlist: getActionForm(card, functip, cards, usefulFields, modules, anchors)
+      })
+    }
   }
 
   /**
    * @description 鍙栨秷淇濆瓨锛屽鏋滃厓绱犱负鏂版坊鍏冪礌锛屽垯浠庡簭鍒椾腑鍒犻櫎
    */
   editModalCancel = () => {
-    const { card, elements, appType } = this.state
+    const { card, elements } = this.state
     let _elements = null
 
     if (card.focus) {
       _elements = elements.filter(item => item.uuid !== card.uuid)
-      
-      if (card.OpenType === 'popview' && appType !== 'mob') { // 寮圭獥鏍囩鎸夐挳锛屼粠澶嶅埗鍒楄〃涓垹闄�
-        MKEmitter.emit('delButtons', [card.uuid])
-      }
     } else {
       _elements = elements
     }
@@ -408,14 +413,15 @@
    */
   handleActionSubmit = () => {
     const { elements } = this.state
-    let color = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666' }
+    let color = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666', default: 'rgba(0, 0, 0, 0.65)' }
 
     this.actionFormRef.handleConfirm().then(res => {
       let _elements = elements.map(cell => {
         if (cell.uuid === res.uuid) {
           res.eleType = cell.eleType || null
           res.style = cell.style || null
-          res.modal = cell.modal || null
+          // res.modal = cell.modal || null
+          // res.config = cell.config || null
           res.wrapStyle = cell.wrapStyle || null
           // res = {...cell, ...res}
 
@@ -431,13 +437,14 @@
               res.style = {}
             }
           } else if (res.class !== cell.class || res.show !== cell.show || !res.style) {
+            let cl = res.class.replace('border-', '')
             let style = {}
             if (res.show === 'link' || res.show === 'icon') {
-              style.color = color[res.class]
+              style.color = color[cl]
               style.backgroundColor = 'transparent'
             } else {
               style.color = '#ffffff'
-              style.backgroundColor = color[res.class]
+              style.backgroundColor = color[cl]
             }
             res.style = {...res.style, ...style}
           }
@@ -460,30 +467,19 @@
    * @description 鎸夐挳鍒犻櫎
    */
   deleteElement = (card) => {
-    const { cardCell, side } = this.props
-    const { elements, appType } = this.state
+    const { elements } = this.state
     let _this = this
 
     confirm({
       content: '纭畾鍒犻櫎鍏冪礌鍚楋紵',
       onOk() {
         let _elements = elements.filter(item => item.uuid !== card.uuid)
-        
-        if (card.OpenType === 'popview' || card.verify || card.modal) {
-          card.$parentId = cardCell.uuid
-          card.$side = side || ''
-        }
 
         _this.setState({
           elements: _elements
         }, () => {
           _this.props.updateElement(_elements)
         })
-
-        if (card.eleType !== 'button') return
-        if (appType === 'mob' || (appType === 'pc' && card.OpenType !== 'popview')) return
-        
-        MKEmitter.emit('delButtons', [card.uuid])
       },
       onCancel() {}
     })
@@ -497,7 +493,6 @@
       profVisible: true,
       card: element
     })
-    MKEmitter.emit('modalStatus', '楠岃瘉淇℃伅')
   }
 
   /**
@@ -521,8 +516,6 @@
       }, () => {
         this.props.updateElement(_elements)
       })
-
-      MKEmitter.emit('modalStatus', false)
     })
   }
 
@@ -555,18 +548,18 @@
     }
   }
 
-  handleSave = (_cards, btn, modal) => {
+  handleSave = (componentId, btnId, modal) => {
     const { cards } = this.props
     const { elements } = this.state
 
-    if (cards.uuid !== _cards.uuid) return
+    if (cards.uuid !== componentId) return
     
-    let _index = elements.findIndex(cell => cell.uuid === btn.uuid)
+    let _index = elements.findIndex(cell => cell.uuid === btnId)
 
     if (_index === -1) return
 
     let _elements = elements.map(cell => {
-      if (cell.uuid === btn.uuid) {
+      if (cell.uuid === btnId) {
         cell.modal = modal
       }
 
@@ -743,11 +736,9 @@
               if (this.verifyRef.handleCancel) {
                 this.verifyRef.handleCancel().then(() => {
                   this.setState({ profVisible: false })
-                  MKEmitter.emit('modalStatus', false)
                 })
               } else {
                 this.setState({ profVisible: false })
-                MKEmitter.emit('modalStatus', false)
               }
             }}
             destroyOnClose
diff --git a/src/menu/components/card/cardcellcomponent/index.scss b/src/menu/components/card/cardcellcomponent/index.scss
index e97d0e2..e82bfb9 100644
--- a/src/menu/components/card/cardcellcomponent/index.scss
+++ b/src/menu/components/card/cardcellcomponent/index.scss
@@ -9,6 +9,9 @@
 
   .card-button-cell {
     float: left;
+    button {
+      box-shadow: none;
+    }
     button:not(.ant-switch) {
       width: 100%;
       background-size: cover;
diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index 0ead6f4..c4bb5f3 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -239,12 +239,6 @@
     
     if (element.copyType === 'action') {
       element.eleType = 'button'
-      if (element.OpenType === 'popview') { // 寮圭獥鏍囩澶嶅埗
-        let _cell = fromJS(element).toJS()
-        _cell.$originUuid = element.uuid
-        _cell.uuid = _uuid
-        MKEmitter.emit('copyButtons', [_cell])
-      }
     }
 
     element.uuid = _uuid
diff --git a/src/menu/components/card/cardsimplecomponent/index.jsx b/src/menu/components/card/cardsimplecomponent/index.jsx
index 67e71fb..e46f414 100644
--- a/src/menu/components/card/cardsimplecomponent/index.jsx
+++ b/src/menu/components/card/cardsimplecomponent/index.jsx
@@ -16,6 +16,7 @@
 const NodesWrap = asyncComponent(() => import('./node-wrap'))
 const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteController = asyncIconComponent(() => import('@/components/paste'))
 
 class CardBoxComponent extends Component {
   static propTpyes = {
@@ -186,6 +187,24 @@
     this.props.updateElement(_card)
   }
 
+  paste = (element, resolve) => {
+    const { card } = this.state
+
+    let _uuid = Utils.getuuid()
+    
+    if (element.copyType === 'action') {
+      element.eleType = 'button'
+    }
+
+    element.uuid = _uuid
+    element.focus = true
+
+    resolve({status: true})
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
+    MKEmitter.emit('cardAddElement', card.uuid, element)
+  }
+
   render() {
     const { cards } = this.props
     const { card } = this.state
@@ -216,6 +235,7 @@
                   <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
                 </NormalForm> : <NodesWrap card={card} updateMenus={this.updateNodes}/>}
                 {cards.type !== 'timeline' ? <CopyComponent type="cardcell" card={card}/> : null}
+                <PasteController options={['action', 'customCardElement']} updateConfig={this.paste} />
                 <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
                 {control ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                   <div className="mk-popover-control">
diff --git a/src/menu/components/card/data-card/index.jsx b/src/menu/components/card/data-card/index.jsx
index 7e33108..3eb6454 100644
--- a/src/menu/components/card/data-card/index.jsx
+++ b/src/menu/components/card/data-card/index.jsx
@@ -140,10 +140,6 @@
     }
   }
 
-  componentDidMount () {
-    MKEmitter.addListener('submitModal', this.handleSave)
-  }
-
   shouldComponentUpdate (nextProps, nextState) {
     return !is(fromJS(this.state), fromJS(nextState))
   }
@@ -155,7 +151,6 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('submitModal', this.handleSave)
   }
 
   /**
@@ -281,7 +276,6 @@
    * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
    */
   deleteCard = (cell) => {
-    const { appType } = this.state
     let card = fromJS(this.state.card).toJS()
     let _this = this
 
@@ -291,25 +285,6 @@
         card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
 
         _this.updateComponent(card)
-
-        if (appType === 'mob') return
-
-        let uuids = []
-
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-        cell.backElements && cell.backElements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -318,7 +293,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'height', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'height', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -401,26 +376,6 @@
     } else if (btn.OpenType === 'popview' && appType !== 'mob') {
       MKEmitter.emit('changePopview', card, btn)
     }
-  }
-
-  handleSave = (_cards, btn, modal) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (card.uuid !== _cards.uuid) return
-
-    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
-
-    if (_index === -1) return
-
-    card.action = card.action.map(cell => {
-      if (cell.uuid === btn.uuid) {
-        cell.modal = modal
-      }
-
-      return cell
-    })
-
-    this.updateComponent(card)
   }
 
   addCard = (copy) => {
@@ -537,7 +492,6 @@
       res.$cardType = 'extendCard'
       res.setting.width = res.setting.width || 6
 
-      let copyBtns = []
       let mobtypes = ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton']
 
       let elements = []
@@ -552,16 +506,7 @@
         } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
           return
         } else {
-          let _uuid = Utils.getuuid()
-
-          if (cell.OpenType === 'popview') {
-            let _cell = fromJS(cell).toJS()
-            _cell.$originUuid = _cell.uuid
-            _cell.uuid = _uuid
-            copyBtns.push(_cell)
-          }
-
-          cell.uuid = _uuid
+          cell.uuid = Utils.getuuid()
           elements.push(cell)
         }
       })
@@ -582,26 +527,13 @@
           } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
             return
           } else {
-            let _uuid = Utils.getuuid()
-  
-            if (cell.OpenType === 'popview') {
-              let _cell = fromJS(cell).toJS()
-              _cell.$originUuid = _cell.uuid
-              _cell.uuid = _uuid
-              copyBtns.push(_cell)
-            }
-  
-            cell.uuid = _uuid
+            cell.uuid = Utils.getuuid()
             backElements.push(cell)
           }
         })
       }
 
       res.backElements = backElements
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       resolve({status: true})
 
@@ -638,19 +570,9 @@
       if (appType === 'mob' && !['pop', 'prompt', 'exec', 'innerpage'].includes(res.OpenType)) {
         resolve({status: false, message: '绉诲姩绔笉鏀寔姝ょ被鍨嬬殑鎸夐挳銆�'})
       } else {
-        let _uuid = Utils.getuuid()
-  
-        if (res.OpenType === 'popview') {
-          let _cell = fromJS(res).toJS()
-          _cell.$originUuid = _cell.uuid
-          _cell.uuid = _uuid
-  
-          MKEmitter.emit('copyButtons', [_cell])
-        }
-
         resolve({status: true})
     
-        res.uuid = _uuid
+        res.uuid = Utils.getuuid()
         this.addButton(res)
       }
     }
diff --git a/src/menu/components/card/data-card/index.scss b/src/menu/components/card/data-card/index.scss
index baf9613..0ffc607 100644
--- a/src/menu/components/card/data-card/index.scss
+++ b/src/menu/components/card/data-card/index.scss
@@ -62,16 +62,6 @@
       font-size: 16px;
     }
   }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
   .ant-pagination {
     float: right;
     margin: 10px;
diff --git a/src/menu/components/card/data-card/options.jsx b/src/menu/components/card/data-card/options.jsx
index 3733162..c750cfc 100644
--- a/src/menu/components/card/data-card/options.jsx
+++ b/src/menu/components/card/data-card/options.jsx
@@ -1,4 +1,3 @@
-import { fromJS } from 'immutable'
 import MenuUtils from '@/utils/utils-custom.js'
 
 /**
@@ -7,7 +6,7 @@
 export default function (wrap, subtype, columns = [], id = '', supNodes = [], setting) {
   let appType = sessionStorage.getItem('appType')
   let MenuType = ''
-  let menu = fromJS(window.GLOB.customMenu).toJS()
+  let menu = window.GLOB.customMenu
   let laypage = setting && setting.laypage !== 'false'
   let interfaces = []
   if (subtype === 'propcard' && menu.interfaces) {
@@ -26,7 +25,14 @@
   }
   let modules = []
   if (subtype === 'propcard' || subtype === 'datacard') {
-    modules = MenuUtils.getSupModules(menu.components, id) || []
+    modules = MenuUtils.getSupModules(menu.components, id, menu.interfaces)
+
+    if (subtype === 'propcard' && wrap.supModule && wrap.supModule.length > 0 && wrap.supModule[0] !== 'empty') {
+      let has = MenuUtils.checkSupModules(modules, wrap.supModule.slice(-1)[0])
+      if (!has) {
+        wrap.supModule = ''
+      }
+    }
   }
 
   let roleList = sessionStorage.getItem('sysRoles')
diff --git a/src/menu/components/card/prop-card/index.jsx b/src/menu/components/card/prop-card/index.jsx
index 0bec608..5e91a50 100644
--- a/src/menu/components/card/prop-card/index.jsx
+++ b/src/menu/components/card/prop-card/index.jsx
@@ -287,7 +287,6 @@
    * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
    */
   deleteCard = (cell) => {
-    const { appType } = this.state
     let card = fromJS(this.state.card).toJS()
     let _this = this
 
@@ -297,25 +296,6 @@
         card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
 
         _this.updateComponent(card)
-
-        if (appType === 'mob') return
-
-        let uuids = []
-
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-        cell.backElements && cell.backElements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -324,7 +304,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -401,7 +381,6 @@
     res.setting = res.setting || {}
     res.setting.width = res.setting.width || 6
 
-    let copyBtns = []
     let mobtypes = ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton']
 
     let elements = []
@@ -412,16 +391,7 @@
       } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
         return
       } else {
-        let _uuid = Utils.getuuid()
-
-        if (cell.OpenType === 'popview') {
-          let _cell = fromJS(cell).toJS()
-          _cell.$originUuid = _cell.uuid
-          _cell.uuid = _uuid
-          copyBtns.push(_cell)
-        }
-
-        cell.uuid = _uuid
+        cell.uuid = Utils.getuuid()
         elements.push(cell)
       }
     })
@@ -438,26 +408,13 @@
         } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
           return
         } else {
-          let _uuid = Utils.getuuid()
-
-          if (cell.OpenType === 'popview') {
-            let _cell = fromJS(cell).toJS()
-            _cell.$originUuid = _cell.uuid
-            _cell.uuid = _uuid
-            copyBtns.push(_cell)
-          }
-
-          cell.uuid = _uuid
+          cell.uuid = Utils.getuuid()
           backElements.push(cell)
         }
       })
     }
 
     res.backElements = backElements
-
-    if (copyBtns.length > 0) {
-      MKEmitter.emit('copyButtons', copyBtns)
-    }
 
     resolve({status: true})
 
diff --git a/src/menu/components/card/prop-card/index.scss b/src/menu/components/card/prop-card/index.scss
index 92e00e6..b87a34f 100644
--- a/src/menu/components/card/prop-card/index.scss
+++ b/src/menu/components/card/prop-card/index.scss
@@ -53,24 +53,6 @@
     right: -30px;
     font-size: 16px;
   }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
   .flex-layout {
     display: flex;
     width: 100%;
diff --git a/src/menu/components/card/table-card/index.jsx b/src/menu/components/card/table-card/index.jsx
index 91e11b6..5596f30 100644
--- a/src/menu/components/card/table-card/index.jsx
+++ b/src/menu/components/card/table-card/index.jsx
@@ -237,7 +237,6 @@
    * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
    */
   deleteCard = (cell) => {
-    const { appType } = this.state
     let card = fromJS(this.state.card).toJS()
     let _this = this
 
@@ -247,20 +246,6 @@
         card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
 
         _this.updateComponent(card)
-
-        if (appType === 'mob') return
-
-        let uuids = []
-
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -269,7 +254,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -374,7 +359,6 @@
       res.setting = res.setting || {}
       res.setting.width = res.setting.width || 6
 
-      let copyBtns = []
       let mobtypes = ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton']
 
       let elements = []
@@ -385,16 +369,7 @@
         } else if (appType === 'mob' && !mobtypes.includes(cell.OpenType)) {
           return
         } else {
-          let _uuid = Utils.getuuid()
-
-          if (cell.OpenType === 'popview') {
-            let _cell = fromJS(cell).toJS()
-            _cell.$originUuid = _cell.uuid
-            _cell.uuid = _uuid
-            copyBtns.push(_cell)
-          }
-
-          cell.uuid = _uuid
+          cell.uuid = Utils.getuuid()
           elements.push(cell)
         }
       })
@@ -403,10 +378,6 @@
 
       delete res.$cardType
       delete res.backElements
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       resolve({status: true})
 
diff --git a/src/menu/components/card/table-card/index.scss b/src/menu/components/card/table-card/index.scss
index c03d640..8ececec 100644
--- a/src/menu/components/card/table-card/index.scss
+++ b/src/menu/components/card/table-card/index.scss
@@ -71,16 +71,6 @@
       font-size: 16px;
     }
   }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
   .ant-pagination {
     text-align: right;
   }
diff --git a/src/menu/components/carousel/data-card/index.jsx b/src/menu/components/carousel/data-card/index.jsx
index 820ee56..dceb119 100644
--- a/src/menu/components/carousel/data-card/index.jsx
+++ b/src/menu/components/carousel/data-card/index.jsx
@@ -14,6 +14,7 @@
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
 const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const CardSimpleComponent = asyncComponent(() => import('@/menu/components/card/cardsimplecomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
@@ -29,7 +30,6 @@
   }
 
   state = {
-    appType: sessionStorage.getItem('appType'),
     card: null,
     back: false
   }
@@ -48,8 +48,8 @@
         name: card.name,
         subtype: card.subtype,
         setting: { interType: 'system' },
-        wrap: { name: card.name, width: card.width || 24, autoplay: 'false', dots: 'true' },
-        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginTop: '8px', marginBottom: '8px', height: '300px' },
+        wrap: { name: card.name, width: card.width || 24, autoplay: 'false', dots: 'true', height: '300px' },
+        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginTop: '8px', marginBottom: '8px' },
         columns: [],
         scripts: [],
         subcards: [{
@@ -85,8 +85,15 @@
 
       this.updateComponent(_card)
     } else {
+      let _card = fromJS(card).toJS()
+
+      if (!_card.wrap.height && _card.style.height) { // 鍏煎
+        _card.wrap.height = _card.style.height
+      }
+      delete _card.style.height
+
       this.setState({
-        card: fromJS(card).toJS()
+        card: _card
       })
     }
   }
@@ -179,7 +186,6 @@
    * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
    */
   deleteCard = (cell) => {
-    const { appType } = this.state
     let card = fromJS(this.state.card).toJS()
     let _this = this
 
@@ -189,20 +195,6 @@
         card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
 
         _this.updateComponent(card)
-
-        if (appType === 'mob') return
-
-        let uuids = []
-
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -211,7 +203,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -227,7 +219,13 @@
   }
 
   updateWrap = (res) => {
-    this.updateComponent({...this.state.card, wrap: res})
+    let _card = {...this.state.card, wrap: res}
+
+    if (res.title && !_card.headerStyle) {
+      _card.headerStyle = { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' }
+    }
+
+    this.updateComponent(_card)
   }
 
   clickComponent = (e) => {
@@ -249,6 +247,7 @@
 
     return (
       <div className="menu-data-carousel-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+        <NormalHeader hideSearch="true" config={card} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <NormalForm title="杞挱-鍔ㄦ�佹暟鎹�" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
@@ -264,7 +263,9 @@
         } trigger="hover">
           <ToolOutlined/>
         </Popover>
-        <CardSimpleComponent cards={card} card={card.subcards[0]} updateElement={this.updateCard} deleteElement={this.deleteCard}/>
+        <div style={{height: card.wrap.height}}>
+          <CardSimpleComponent cards={card} card={card.subcards[0]} updateElement={this.updateCard} deleteElement={this.deleteCard}/>
+        </div>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/carousel/data-card/index.scss b/src/menu/components/carousel/data-card/index.scss
index cd42494..b4a77ec 100644
--- a/src/menu/components/carousel/data-card/index.scss
+++ b/src/menu/components/carousel/data-card/index.scss
@@ -47,33 +47,9 @@
     right: -30px;
     font-size: 16px;
   }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
   .ant-pagination {
     float: right;
     margin: 10px;
-  }
-
-  .model-menu-action-list {
-    .page-card {
-      line-height: 55px;
-    }
   }
 }
 .menu-data-carousel-edit-box::after {
diff --git a/src/menu/components/carousel/data-card/options.jsx b/src/menu/components/carousel/data-card/options.jsx
index 37104ad..a29facc 100644
--- a/src/menu/components/carousel/data-card/options.jsx
+++ b/src/menu/components/carousel/data-card/options.jsx
@@ -18,6 +18,13 @@
   const cardWrapForm = [
     {
       type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: wrap.title || '',
+      required: false
+    },
+    {
+      type: 'text',
       field: 'name',
       label: '缁勪欢鍚嶇О',
       initval: wrap.name || '',
@@ -53,6 +60,62 @@
     },
     {
       type: 'radio',
+      field: 'display',
+      label: '鏄剧ず妯″紡',
+      initval: wrap.display || 'normal',
+      required: false,
+      options: [
+        {value: 'normal', label: '姝e父'},
+        {value: 'modal', label: '寮圭獥'},
+      ],
+      controlFields: [
+        {field: 'modalWidth', values: ['modal']},
+        {field: 'modalContent', values: ['modal']},
+        {field: 'code', values: ['modal']},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'modalContent',
+      label: '寮圭獥鍐呭',
+      initval: wrap.modalContent || 'message',
+      required: false,
+      options: [
+        {value: 'message', label: '娑堟伅鎻愰啋'},
+        {value: 'update', label: '绯荤粺鏇存柊'},
+      ]
+    },
+    {
+      type: 'styleInput',
+      field: 'height',
+      label: '缁勪欢楂樺害',
+      initval: wrap.height || '',
+      required: true,
+      options: ['px', 'vh']
+    },
+    {
+      type: 'styleInput',
+      field: 'modalWidth',
+      label: '寮圭獥瀹藉害',
+      initval: wrap.modalWidth || '300px',
+      required: true,
+      options: ['px', 'vw'],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'text',
+      field: 'code',
+      label: '娑堟伅缂栫爜',
+      initval: wrap.code || '',
+      tooltip: '鐢ㄤ簬璁板綍娑堟伅鏄惁宸茶鐨勬爣璇嗭紝濡傛灉涓嶅悓椤甸潰涓瓨鍦ㄧ浉鍚屾秷鎭紝鍙寚瀹氬浐瀹氬�笺��',
+      required: false,
+      rules: [{
+        pattern: /^[0-9a-zA-Z_]*$/ig,
+        message: '鍙厑璁稿寘鍚暟瀛椼�佸瓧姣嶄互鍙奯銆�'
+      }]
+    },
+    {
+      type: 'radio',
       field: 'autoplay',
       label: '鑷姩鍒囨崲',
       initval: wrap.autoplay || 'false',
diff --git a/src/menu/components/carousel/prop-card/index.jsx b/src/menu/components/carousel/prop-card/index.jsx
index 30a541e..8efdbe0 100644
--- a/src/menu/components/carousel/prop-card/index.jsx
+++ b/src/menu/components/carousel/prop-card/index.jsx
@@ -14,6 +14,7 @@
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
 const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const CardSimpleComponent = asyncComponent(() => import('@/menu/components/card/cardsimplecomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
@@ -30,7 +31,6 @@
   }
 
   state = {
-    appType: sessionStorage.getItem('appType'),
     card: null,
     back: false
   }
@@ -49,8 +49,8 @@
         name: card.name,
         subtype: card.subtype,
         setting: { },
-        wrap: { name: card.name, width: card.width || 24, datatype: 'static', autoplay: 'false', dots: 'true' },
-        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px', height: '300px' },
+        wrap: { name: card.name, width: card.width || 24, datatype: 'static', autoplay: 'false', dots: 'true', height: '300px' },
+        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
         columns: [],
         scripts: [],
         subcards: [{
@@ -84,8 +84,15 @@
  
       this.updateComponent(_card)
     } else {
+      let _card = fromJS(card).toJS()
+
+      if (!_card.wrap.height && _card.style.height) { // 鍏煎
+        _card.wrap.height = _card.style.height
+      }
+      delete _card.style.height
+
       this.setState({
-        card: fromJS(card).toJS()
+        card: _card
       })
     }
   }
@@ -199,7 +206,6 @@
    * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
    */
   deleteCard = (cell) => {
-    const { appType } = this.state
     let card = fromJS(this.state.card).toJS()
     let _this = this
 
@@ -209,20 +215,6 @@
         card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
 
         _this.updateComponent(card)
-
-        if (appType === 'mob') return
-
-        let uuids = []
-
-        cell.elements && cell.elements.forEach(c => {
-          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
-
-          uuids.push(c.uuid)
-        })
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -231,7 +223,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -292,7 +284,13 @@
   }
 
   updateWrap = (res) => {
-    this.updateComponent({...this.state.card, wrap: res})
+    let _card = {...this.state.card, wrap: res}
+
+    if (res.title && !_card.headerStyle) {
+      _card.headerStyle = { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' }
+    }
+
+    this.updateComponent(_card)
   }
 
   clickComponent = (e) => {
@@ -314,6 +312,7 @@
 
     return (
       <div className="menu-prop-carousel-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+        <NormalHeader hideSearch="true" config={card} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <PlusOutlined className="plus" title="娣诲姞鍗$墖" onClick={this.addCard}/>
@@ -332,9 +331,11 @@
         } trigger="hover">
           <ToolOutlined/>
         </Popover>
-        {card.subcards.length > 0 ? <Carousel dotPosition={card.wrap.dotPosition || 'bottom'} effect={card.wrap.effect || 'scrollx'}>
-          {card.subcards.map((subcard) => (<CardSimpleComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
-        </Carousel> : null}
+        <div style={{height: card.wrap.height}}>
+          {card.subcards.length > 0 ? <Carousel dotPosition={card.wrap.dotPosition || 'bottom'} effect={card.wrap.effect || 'scrollx'}>
+            {card.subcards.map((subcard) => (<CardSimpleComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
+          </Carousel> : null}
+        </div>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/carousel/prop-card/index.scss b/src/menu/components/carousel/prop-card/index.scss
index 3121478..3fb2fb0 100644
--- a/src/menu/components/carousel/prop-card/index.scss
+++ b/src/menu/components/carousel/prop-card/index.scss
@@ -48,6 +48,10 @@
     }
   }
   .ant-carousel {
+    height: 100%;
+    .slick-slider {
+      height: 100%;
+    }
     .slick-dots li button {
       background: #1890ff;
     }
@@ -61,24 +65,6 @@
     position: absolute;
     right: -30px;
     font-size: 16px;
-  }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
   }
 }
 .menu-prop-carousel-edit-box::after {
diff --git a/src/menu/components/chart/antv-G6/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-G6/chartcompile/formconfig.jsx
new file mode 100644
index 0000000..b6c5913
--- /dev/null
+++ b/src/menu/components/chart/antv-G6/chartcompile/formconfig.jsx
@@ -0,0 +1,239 @@
+/**
+ * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟
+ * @param {object} card       // 鍥捐〃瀵硅薄
+ */
+export function getBaseForm (card) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch (e) {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  return [
+    {
+      type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: card.title,
+      required: false
+    },
+    {
+      type: 'text',
+      field: 'name',
+      label: '缁勪欢鍚嶇О',
+      initval: card.name,
+      tooltip: '鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'width',
+      label: '瀹藉害',
+      initval: card.width,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      decimal: 0,
+      required: true
+    },
+    {
+      type: 'styleInput',
+      field: 'height',
+      label: '鍥捐〃楂樺害',
+      initval: card.height,
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
+    },
+    {
+      type: 'radio',
+      field: 'permission',
+      label: '鏉冮檺楠岃瘉',
+      initval: card.permission || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鍚敤'},
+        {value: 'false', label: '绂佺敤'},
+      ],
+      forbid: !appType
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: card.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    }
+  ]
+}
+
+/**
+ * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟
+ * @param {object} card       // 鍥捐〃瀵硅薄
+ * @param {Array}  columns    // 鏄剧ず鍒�
+ */
+export function getOptionForm (card, columns) {
+  return [
+    {
+      type: 'select',
+      field: 'subtype',
+      label: '绫诲瀷',
+      initval: card.subtype || 'mindmap',
+      required: true,
+      options: [{
+        value: 'mindmap',
+        label: '鎬濈淮瀵煎浘'
+      }, {
+        value: 'indentTree',
+        label: '缂╄繘鏂囦欢鏍�'
+      }, {
+        value: 'kapmap',
+        label: '鐭ヨ瘑鍥捐氨鏍�'
+      }],
+      controlFields: [
+        {field: 'dirField', values: ['mindmap']},
+        {field: 'nodeColor', values: ['mindmap']},
+        {field: 'collapsed', values: ['indentTree', 'kapmap']},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'rootType',
+      label: '鏍硅妭鐐瑰彇鍊�',
+      initval: card.rootType || 'fixed',
+      tooltip: '閫夋嫨涓婄骇鏃讹紝璇峰~鍐欐牴鑺傜偣鐨勬枃鏈拰鍊肩殑瀛楁鍚�',
+      required: true,
+      options: [{
+        value: 'fixed',
+        label: '鍥哄畾鍊�'
+      }, {
+        value: 'supvalue',
+        label: '涓婄骇缁勪欢'
+      }, {
+        value: 'line',
+        label: '琛�'
+      }],
+      controlFields: [
+        {field: 'rootLabel', values: ['fixed', 'supvalue']},
+        {field: 'rootValue', values: ['fixed', 'supvalue']},
+        {field: 'mark', values: ['line']},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'rootValue',
+      label: '鏍硅妭鐐瑰��',
+      initval: card.rootValue || '',
+      required: true
+    },
+    {
+      type: 'text',
+      field: 'rootLabel',
+      label: '鏍硅妭鐐规枃鏈�',
+      initval: card.rootLabel || '',
+      required: true
+    },
+    {
+      type: 'select',
+      field: 'valueField',
+      label: '鍊煎瓧娈�',
+      initval: card.valueField || '',
+      required: true,
+      options: columns
+    },
+    {
+      type: 'select',
+      field: 'labelField',
+      label: '鏂囨湰瀛楁',
+      initval: card.labelField || '',
+      required: true,
+      options: columns
+    },
+    {
+      type: 'select',
+      field: 'parentField',
+      label: '涓婄骇瀛楁',
+      initval: card.parentField || '',
+      required: true,
+      options: columns
+    },
+    {
+      type: 'text',
+      field: 'mark',
+      label: '椤剁骇鏍囪瘑',
+      initval: card.mark || '',
+      tooltip: '涓婄骇瀛楁鍊间笌椤剁骇鏍囪瘑鐩稿悓鏃讹紝瑙嗕负鏍硅妭鐐广��',
+      required: true
+    },
+    {
+      type: 'select',
+      field: 'dirField',
+      label: '鏂瑰悜鎺у埗',
+      initval: card.dirField || '',
+      required: false,
+      options: columns,
+      controlFields: [
+        {field: 'dirSign', notNull: true},
+        {field: 'leftColor', notNull: true},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'dirSign',
+      label: '宸﹀悜鏍囪',
+      initval: card.dirSign || '',
+      tooltip: '褰撹妭鐐瑰�间笌鏍囪鐩稿悓鏃惰妭鐐逛俊鎭綅浜庤妭鐐瑰乏渚э紝澶氫釜鍊艰鐢ㄩ�楀彿鍒嗛殧銆�',
+      required: false
+    },
+    {
+      type: 'color',
+      field: 'nodeColor',
+      label: '鑺傜偣棰滆壊',
+      initval: card.nodeColor || '#1890ff',
+      tooltip: '鍙充晶鑺傜偣鐨勬爣璁伴鑹层��',
+      isHex: true,
+      required: false
+    },
+    {
+      type: 'color',
+      field: 'leftColor',
+      label: '宸﹁妭鐐归鑹�',
+      initval: card.leftColor || '#26C281',
+      tooltip: '宸︿晶鑺傜偣鐨勬爣璁伴鑹层��',
+      isHex: true,
+      required: false
+    },
+    {
+      type: 'radio',
+      field: 'empty',
+      label: '绌哄�奸殣钘�',
+      initval: card.empty || 'show',
+      tooltip: '褰撴煡璇㈡暟鎹负绌烘椂锛岄殣钘忚缁勪欢銆�',
+      required: false,
+      options: [
+        {value: 'show', label: '鍚�'},
+        {value: 'hidden', label: '鏄�'},
+      ],
+    },
+    {
+      type: 'radio',
+      field: 'collapsed',
+      label: '鑺傜偣鍚堝苟',
+      initval: card.collapsed || 'false',
+      tooltip: '涓�绾ц妭鐐规槸鍚﹀悎骞躲��',
+      required: false,
+      options: [
+        {value: 'false', label: '鍚�'},
+        {value: 'true', label: '鏄�'},
+      ],
+    },
+  ]
+}
diff --git a/src/menu/components/chart/antv-G6/chartcompile/index.jsx b/src/menu/components/chart/antv-G6/chartcompile/index.jsx
new file mode 100644
index 0000000..6a24679
--- /dev/null
+++ b/src/menu/components/chart/antv-G6/chartcompile/index.jsx
@@ -0,0 +1,121 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Modal, Form, Tabs } from 'antd'
+import { EditOutlined } from '@ant-design/icons'
+
+import { getBaseForm, getOptionForm } from './formconfig'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { TabPane } = Tabs
+const NormalForm = asyncComponent(() => import('@/components/normalform/modalform'))
+
+class LineChartDrawerForm extends Component {
+  static propTpyes = {
+    plot: PropTypes.object,
+    config: PropTypes.object,
+    plotchange: PropTypes.func
+  }
+
+  state = {
+    view: 'normal',
+    visible: false,
+    plot: null,
+    formlist: null,
+    baseFormlist: null
+  }
+
+  showDrawer = () => {
+    const { config } = this.props
+
+    this.setState({
+      visible: true,
+      view: 'normal',
+      plot: fromJS(config.plot).toJS(),
+      baseFormlist: getBaseForm(config.plot),
+      formlist: getOptionForm(config.plot, config.columns)
+    })
+  }
+
+  onSubmit = () => {
+    const { config } = this.props
+    const { plot, view } = this.state
+
+    if (view === 'normal') {
+      this.norRef.handleConfirm().then(res => {
+        let _plot = {...plot, ...res}
+
+        this.setState({
+          plot: _plot,
+          visible: false
+        })
+
+        this.props.plotchange({...config, plot: _plot})
+      })
+    } else if (view === 'base') {
+      this.baseRef.handleConfirm().then(res => {
+        let _plot = {...plot, ...res}
+
+        this.setState({
+          plot: _plot,
+          visible: false
+        })
+
+        this.props.plotchange({...config, plot: _plot})
+      })
+    }
+  }
+
+  changeTab = (tab) => {
+    const { plot, view } = this.state
+
+    if (view === 'normal') {
+      this.norRef.handleConfirm().then(res => {
+        this.setState({
+          plot: {...plot, ...res},
+          view: tab
+        })
+      })
+    } else if (view === 'base') {
+      this.baseRef.handleConfirm().then(res => {
+        this.setState({
+          plot: {...plot, ...res},
+          view: tab
+        })
+      })
+    }
+  }
+
+  render() {
+    const { config } = this.props
+    const { view, visible, baseFormlist, formlist } = this.state
+
+    return (
+      <div className="line-chart-drawer-form">
+        <EditOutlined title="缂栬緫" onClick={this.showDrawer} />
+        <Modal
+          wrapClassName="mk-pop-modal"
+          visible={visible}
+          width={850}
+          maskClosable={false}
+          onOk={this.onSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          {config.name ? <div className="mk-com-name">{config.name} - 缂栬緫</div> : null}
+          <Tabs activeKey={view} onChange={this.changeTab}>
+            <TabPane tab="缁勪欢璁剧疆" key="base">
+              <NormalForm formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/>
+            </TabPane>
+            <TabPane tab="鍥捐〃璁剧疆" key="normal">
+              <NormalForm formlist={formlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.norRef = inst}/>
+            </TabPane>
+          </Tabs>
+        </Modal>
+      </div>
+    );
+  }
+}
+
+export default Form.create()(LineChartDrawerForm)
\ No newline at end of file
diff --git a/src/menu/components/chart/antv-G6/chartcompile/index.scss b/src/menu/components/chart/antv-G6/chartcompile/index.scss
new file mode 100644
index 0000000..19ce53c
--- /dev/null
+++ b/src/menu/components/chart/antv-G6/chartcompile/index.scss
@@ -0,0 +1,6 @@
+.line-chart-drawer-form {
+  display: inline-block;
+  > .anticon-edit {
+    color: #1890ff;
+  }
+}
diff --git a/src/menu/components/chart/antv-G6/index.jsx b/src/menu/components/chart/antv-G6/index.jsx
new file mode 100644
index 0000000..2900881
--- /dev/null
+++ b/src/menu/components/chart/antv-G6/index.jsx
@@ -0,0 +1,1461 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Popover } from 'antd'
+import { ToolOutlined, DeleteOutlined, FontColorsOutlined } from '@ant-design/icons'
+import G6 from '@antv/g6'
+
+import MKEmitter from '@/utils/events.js'
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
+import './index.scss'
+
+const { Util } = G6
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const ChartCompileForm = asyncIconComponent(() => import('./chartcompile'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+
+const MindData = [
+  'Modeling Methods',
+  [
+    'Classification',
+    ['Logistic regression', 'Linear discriminant analysis', 'Rules', 'Decision trees', 'Naive Bayes', 'K nearest neighbor', 'Probabilistic neural network', 'Support vector machine']
+  ],
+  [
+    'Consensus',
+    [
+      'Models diversity',
+      ['Different initializations', 'Different parameter choices', 'Different architectures', 'Different modeling methods', 'Different training sets', 'Different feature sets']
+    ],
+    [
+      'Methods',
+      ['Classifier selection', 'Classifier fusion']
+    ],
+    [
+      'Common',
+      ['Bagging', 'Boosting', 'AdaBoost']
+    ]
+  ],
+  [
+    'Regression',
+    ['Multiple linear regression', 'Partial least squares', 'Multi-layer feedforward neural network', 'General regression neural network', 'Support vector regression']
+  ]
+]
+
+const styles = {
+  blue: '#1890ff',
+  red: '#f5222d',
+  orange_red: '#fa541c',
+  orange: '#fa8c16',
+  orange_yellow: '#faad14',
+  yellow: '#fadb14',
+  yellow_green: '#a0d911',
+  green: '#52c41a',
+  cyan: '#13c2c2',
+  blue_purple: '#2f54eb',
+  purple: '#722ed1',
+  magenta: '#eb2f96',
+  grass_green: '#aeb303',
+  deep_red: '#c32539'
+}
+
+let systemColor = '#1890ff'
+if (window.GLOB.style) {
+  let type = window.GLOB.style.replace(/bg_black_style_|bg_white_style_/, '')
+  systemColor = styles[type] || '#1890ff'
+}
+const COLORS = ['#5B8FF9', '#F6BD16', '#5AD8A6', '#945FB9', '#E86452', '#6DC8EC', '#FF99C3', '#1E9493', '#FF9845', '#5D7092']
+
+// 鎬濈淮瀵煎浘
+G6.registerNode(
+  'dice-mind-map-root', {
+    jsx: (cfg) => {
+      const width = Util.getTextSize(cfg.label, 16)[0] + 24
+
+      return `
+        <group>
+          <rect style={{width: ${width}, height: 42, stroke: ${systemColor}, radius: 4}} keyshape>
+            <text style={{ fontSize: 16, marginLeft: 6, marginTop: 12 }}>${cfg.label}</text>
+          </rect>
+        </group>
+      `
+    },
+    getAnchorPoints() {
+      return [
+        [0, 0.5],
+        [1, 0.5]
+      ]
+    }
+  },
+  'single-node',
+)
+G6.registerNode(
+  'dice-mind-map-sub', {
+    jsx: (cfg) => {
+      const width = Util.getTextSize(cfg.label, 14)[0] + 24
+
+      return `
+        <group>
+          <rect style={{width: ${width}, height: 22, cursor: pointer}}>
+            <text style={{ fontSize: 14, fill: ${cfg.selected ? systemColor : '#000000'}, marginLeft: 12, marginTop: 6, cursor: pointer }}>${cfg.label}</text>
+          </rect>
+          <rect style={{ fill: ${cfg.color}, width: ${width}, height: 2, x: 0, y: 22, cursor: pointer }} />
+        </group>
+      `
+    },
+    getAnchorPoints() {
+      return [
+        [0, 0.965],
+        [1, 0.965]
+      ]
+    }
+  },
+  'single-node',
+)
+G6.registerNode(
+  'dice-mind-map-leaf', {
+    jsx: (cfg) => {
+      const width = Util.getTextSize(cfg.label, 12)[0] + 24
+
+      return `
+        <group>
+          <rect style={{width: ${width}, height: 26, fill: 'transparent', cursor: pointer }}>
+            <text style={{ fontSize: 12, fill: ${cfg.selected ? systemColor : '#000000'}, marginLeft: 12, marginTop: 6, cursor: pointer }}>${cfg.label}</text>
+          </rect>
+          <rect style={{ fill: ${cfg.color}, width: ${width}, height: 2, x: 0, y: 32, cursor: pointer }} />
+        </group>
+      `
+    },
+    getAnchorPoints() {
+      return [
+        [0, 0.965],
+        [1, 0.965]
+      ]
+    }
+  },
+  'single-node',
+)
+G6.registerBehavior('dice-mindmap', {
+  getEvents() {
+    return {
+      'node:click': 'editNode',
+      'canvas:click': 'onCanvasClick'
+    }
+  },
+  editNode(evt) {
+    const item = evt.item
+    const model = item.get('model')
+
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+
+    model.selected = true
+    this.graph.updateItem(item, model, false)
+  },
+  onCanvasClick(e) {
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+  }
+})
+G6.registerBehavior('scroll-canvas', {
+  getEvents: function getEvents() {
+    return {
+      wheel: 'onWheel'
+    }
+  },
+  onWheel: function onWheel(ev) {
+    const { graph } = this
+    if (!graph) {
+      return
+    }
+    if (ev.ctrlKey) {
+      const canvas = graph.get('canvas')
+      const point = canvas.getPointByClient(ev.clientX, ev.clientY)
+      let ratio = graph.getZoom()
+      if (ev.wheelDelta > 0) {
+        ratio += ratio * 0.05
+      } else {
+        ratio *= ratio * 0.05
+      }
+      graph.zoomTo(ratio, {
+        x: point.x,
+        y: point.y
+      })
+    } else {
+      const x = ev.deltaX || ev.movementX
+      const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3
+      graph.translate(-x, -y)
+    }
+    ev.preventDefault()
+  }
+})
+
+const dataMapTransform = (data) => {
+  const changeData = (d, level = 0, color) => {
+    const data = { ...d }
+
+    switch (level) {
+      case 0:
+        data.type = 'dice-mind-map-root'
+        break
+      case 1:
+        data.type = 'dice-mind-map-sub'
+        break
+      default:
+        data.type = 'dice-mind-map-leaf'
+        break
+    }
+
+    if (color) {
+      data.color = color
+    }
+
+    if (level === 1 && !d.direction) {
+      data.direction = 'right'
+    }
+
+    if (d.children) {
+      data.children = d.children.map((child) => changeData(child, level + 1, data.color))
+    }
+    return data
+  }
+  return changeData(data)
+}
+
+// 缂╄繘鏂囦欢鏍�
+G6.registerNode('indentedRoot', {
+  draw(model, group) {
+    const keyShape = group.addShape('rect', {
+      attrs: {
+        x: -46,
+        y: -16,
+        width: 92,
+        height: 32,
+        fill: systemColor,
+        radius: 2,
+        stroke: '#5B8FF9',
+        lineWidth: 0
+      },
+      name: 'key-shape'
+    })
+
+    const text = group.addShape('text', {
+      attrs: {
+        text: model.label || 'root',
+        fill: "#fff",
+        fontSize: 12,
+        x: 0,
+        y: 0,
+        textAlign: 'center',
+        textBaseline: 'middle'
+      },
+      name: 'root-text-shape'
+    })
+    const textBBox = text.getBBox()
+    const width = textBBox.width + 24
+    const height = textBBox.height + 12
+    keyShape.attr({
+      x: -width / 2,
+      y: -height / 2,
+      width,
+      height
+    })
+
+    return keyShape
+  },
+  getAnchorPoints() {
+    return [
+      [0.5, 1]
+    ]
+  },
+  update: undefined
+})
+
+G6.registerNode('indentedNode', {
+  addChildCount(group, tag, props) {
+    const { collapsed, branchColor, count } = props
+    let clickCircleY = 10
+    // 瀛愮被鏁伴噺 icon锛岀粯鍒跺渾鐐瑰湪鑺傜偣姝d笅鏂�
+    if (tag) {
+      const childCountGroup = group.addGroup({
+        name: 'child-count-group'
+      })
+      childCountGroup.setMatrix([1, 0, 0, 0, 1, 0, 0, clickCircleY, 1])
+      const countBackWidth = collapsed ? 26 : 12
+      childCountGroup.addShape('rect', {
+        attrs: {
+          width: countBackWidth,
+          height: 12,
+          radius: 6,
+          stroke: branchColor,
+          lineWidth: 2,
+          fill: collapsed ? branchColor : '#fff',
+          x: -countBackWidth / 2,
+          y: -6,
+          cursor: 'pointer',
+        },
+        name: 'child-count-rect-shape'
+      })
+      const childCountText = childCountGroup.addShape('text', {
+        attrs: {
+          text: count,
+          fill: '#fff',
+          x: 0,
+          y: 0,
+          fontSize: 10,
+          textAlign: 'center',
+          textBaseline: 'middle',
+          cursor: 'pointer',
+        },
+        name: 'child-count-text-shape'
+      })
+      const childHoverIcon = childCountGroup.addShape('path', {
+        attrs: {
+          stroke: '#fff',
+          lineWidth: 1,
+          cursor: 'pointer',
+          path: [['M', -3, 2], ['L', 0, -2], ['L', 3, 2]]
+        },
+        name: 'child-count-expand-icon',
+        capture: false
+      })
+      childHoverIcon.hide()
+
+      // 杩炴帴 count 鐨勭嚎娈�
+      const countLink = group.addShape('path', {
+        attrs: {
+          path: [['M', 0, 0], ['L', 0, 11]],
+          stroke: branchColor,
+          lineWidth: 2,
+        },
+        name: 'count-link'
+      })
+      countLink.toBack()
+
+      if (collapsed) {
+        childCountGroup.show()
+        childCountText.show()
+        countLink.show()
+      }
+      else {
+        childCountGroup.hide()
+        childCountText.hide()
+        countLink.hide()
+      }
+
+      clickCircleY += 16
+    }
+  },
+  addBottomLine(group, props) {
+    const { x, width, stroke, lineWidth } = props
+    return group.addShape('path', {
+      attrs: {
+        path: [
+          ['M', x - 1, 0],
+          ['L', width, 0],
+        ],
+        stroke,
+        lineWidth,
+      },
+      name: 'node-path-shape'
+    })
+  },
+  addName(group, props) {
+    const { label, x = 0, y, fill } = props
+    return group.addShape('text', {
+      attrs: {
+        text: label,
+        x,
+        y,
+        textAlign: 'start',
+        textBaseline: 'top',
+        fill,
+        fontSize: 14,
+        fontFamily: 'PingFangSC-Regular',
+        cursor: 'pointer',
+      },
+      name: 'not-root-text-shape'
+    })
+  },
+  draw(model, group) {
+    const { collapsed, depth, label, children, selected } = model
+    // 鏄惁涓烘牴鑺傜偣
+    const rootNode = depth === 0
+    // 瀛愯妭鐐规暟閲�
+    const childCount = children ? children.length : 0
+
+    let width = 0
+    const height = 24
+    const x = 0
+    const y = -height / 2
+    const borderRadius = 4
+    // 鍚嶇О鏂囨湰
+    const text = this.addName(group, { label, x, y })
+
+    let textWidth = text.getBBox().width
+    width = textWidth + 20
+
+    const keyShapeAttrs = {
+      x,
+      y,
+      width,
+      height,
+      radius: borderRadius,
+      fill: undefined,
+      stroke: undefined,
+    }
+
+    const keyShape = group.addShape('rect', {
+      attrs: keyShapeAttrs,
+      name: 'root-key-shape-rect-shape'
+    })
+
+    // 搴曢儴妯嚎
+    const bottomLine = this.addBottomLine(group, {
+      stroke: model.branchColor || '#AAB7C4',
+      lineWidth: 3,
+      x,
+      width
+    })
+
+    let nameColor = 'rgba(0, 0, 0, 0.85)'
+
+    if (selected) {
+      nameColor = systemColor
+    }
+
+    // 鍚嶇О
+    text.attr({
+      y: y - 12,
+      fill: nameColor
+    })
+    text.toFront()
+    textWidth = text.getBBox().width
+
+    if (bottomLine) bottomLine.toFront()
+
+    this.addChildCount(group, childCount && !rootNode, {
+      collapsed,
+      branchColor: model.branchColor,
+      count: childCount ? `${childCount}` : undefined
+    })
+
+    const bbox = group.getBBox()
+    const backContainer = group.addShape('path', {
+      attrs: {
+        path: childCount ? [
+          ['M', bbox.minX, bbox.minY],
+          ['L', bbox.maxX, bbox.minY],
+          ['L', bbox.maxX, bbox.maxY],
+          ['L', bbox.minX + 20, bbox.maxY],
+          ['L', bbox.minX + 20, bbox.maxY + 20],
+          ['L', bbox.minX, bbox.maxY + 20],
+          ['Z']
+        ] : [
+          ['M', bbox.minX, bbox.minY],
+          ['L', bbox.maxX, bbox.minY],
+          ['L', bbox.maxX, bbox.maxY],
+          ['L', bbox.minX, bbox.maxY],
+          ['Z']
+        ],
+        fill: '#fff',
+        opacity: 0
+      }
+    })
+    backContainer.toBack()
+    return keyShape
+  }
+})
+
+G6.registerEdge('indentedEdge', {
+  afterDraw: (cfg, group) => {
+    const sourceNode = cfg.sourceNode && cfg.sourceNode.getModel()
+    const targetNode = cfg.targetNode && cfg.targetNode.getModel()
+    const color = sourceNode.branchColor || targetNode.branchColor || cfg.color || '#000'
+
+    const keyShape = group.get('children')[0]
+    keyShape.attr({
+      stroke: color,
+      lineWidth: 3 // branchThick
+    })
+    group.toBack()
+  },
+  getControlPoints: (cfg) => {
+    const startPoint = cfg.startPoint
+    const endPoint = cfg.endPoint
+    return [
+      startPoint,
+      {
+        x: startPoint.x,
+        y: endPoint.y,
+      },
+      endPoint
+    ]
+  },
+  update: undefined
+}, 'polyline')
+
+G6.registerBehavior('wheel-scroll', {
+  getDefaultCfg() {
+    return {
+      direction: 'y',
+      zoomKey: 'ctrl',
+      sensitivity: 3,
+      scalableRange: -64
+    }
+  },
+  getEvents() {
+    return {
+      wheel: 'onWheel'
+    }
+  },
+  onWheel(ev) {
+    const graph = this.graph
+    let keyDown = ev[`${this.zoomKey}Key`]
+    if (this.zoomKey === 'control') keyDown = ev.ctrlKey
+    if (keyDown) {
+      const sensitivity = this.get('sensitivity')
+      const canvas = graph.get('canvas')
+      const point = canvas.getPointByClient(ev.clientX, ev.clientY)
+      let ratio = graph.getZoom()
+      if (ev.wheelDelta > 0) {
+        ratio *= (1 + 0.01 * sensitivity)
+      } else {
+        ratio *= (1 - 0.01 * sensitivity)
+      }
+      graph.zoomTo(ratio, {
+        x: point.x,
+        y: point.y
+      })
+      graph.emit('wheelzoom', ev)
+    } else {
+      let dx = ev.deltaX || ev.movementX
+      let dy = ev.deltaY || ev.movementY
+      if (!dy && navigator.userAgent.indexOf('Firefox') > -1) dy = (-ev.wheelDelta * 125) / 3
+
+      const width = this.graph.get('width')
+      const height = this.graph.get('height')
+      const graphCanvasBBox = this.graph.get('group').getCanvasBBox()
+
+      let expandWidth = this.scalableRange
+      let expandHeight = this.scalableRange
+      // 鑻� scalableRange 鏄� 0~1 鐨勫皬鏁帮紝鍒欎綔涓烘瘮渚嬭�冭檻
+      if (expandWidth < 1 && expandWidth > -1) {
+        expandWidth = width * expandWidth
+        expandHeight = height * expandHeight
+      }
+
+      const { minX, maxX, minY, maxY } = graphCanvasBBox
+
+      if (dx > 0) {
+        if (maxX < -expandWidth) {
+          dx = 0
+        } else if (maxX - dx < -expandWidth) {
+          dx = maxX + expandWidth
+        }
+      } else if (dx < 0) {
+        if (minX > width + expandWidth) {
+          dx = 0
+        } else if (minX - dx > width + expandWidth) {
+          dx = minX - (width + expandWidth)
+        }
+      }
+
+      if (dy > 0) {
+        if (maxY < -expandHeight) {
+          dy = 0
+        } else if (maxY - dy < -expandHeight) {
+          dy = maxY + expandHeight
+        }
+      } else if (dy < 0) {
+        if (minY > height + expandHeight) {
+          dy = 0
+        } else if (minY - dy > height + expandHeight) {
+          dy = minY - (height + expandHeight)
+        }
+      }
+
+      if (this.get('direction') === 'x') {
+        dy = 0
+      } else if (this.get('direction') === 'y') {
+        dx = 0
+      }
+
+      graph.translate(-dx, -dy)
+    }
+    ev.preventDefault()
+  }
+})
+G6.registerBehavior('hover-node', {
+  getEvents() {
+    return {
+      'node:mouseover': 'onNodeMouseOver',
+      'node:mouseleave': 'onNodeMouseLeave',
+      'node:mouseenter': 'onNodeMouseEnter'
+    }
+  },
+  onNodeMouseEnter(ev) {
+    const { item } = ev
+    if (!item || item.get('destroyed')) return
+    item.toFront()
+    const model = item.getModel()
+    const { collapsed, depth } = model
+    const rootNode = depth === 0 || model.isRoot
+    const group = item.getContainer()
+
+    if (rootNode) return
+
+    // 鎺у埗瀛愯妭鐐逛釜鏁版爣璁�
+    if (!collapsed) {
+      const childCountGroup = group.find(e => e.get('name') === 'child-count-group')
+      if (childCountGroup) {
+        childCountGroup.show()
+      }
+    }
+  },
+  onNodeMouseOver(ev) {
+    const shape = ev.target
+
+    // tooltip鏄剧ず銆侀殣钘�
+    this.graph.emit('tooltip: show', ev)
+
+    // expand 鐘舵�佷笅锛岃嫢 hover 鍒板瓙鑺傜偣涓暟鏍囪锛屽~鍏呰儗鏅�+鏄剧ず鏀惰捣 icon
+    const { item } = ev
+    const group = item.getContainer()
+    const model = item.getModel()
+    if (!model.collapsed) {
+      const childCountGroup = group.find(e => e.get('name') === 'child-count-group')
+      if (childCountGroup) {
+        childCountGroup.show()
+        const back = childCountGroup.find(e => e.get('name') === 'child-count-rect-shape')
+        const expandIcon = childCountGroup.find(e => e.get('name') === 'child-count-expand-icon')
+        const rootNode = model.depth === 0 || model.isRoot
+        const branchColor = rootNode ? '#576286' : model.branchColor
+        if (shape.get('parent').get('name') === 'child-count-group') {
+          if (back) {
+            back.attr('fill', branchColor || '#fff')
+          }
+          if (expandIcon) {
+            expandIcon.show()
+          }
+        } else {
+          if (back) {
+            back.attr('fill', '#fff')
+          }
+          if (expandIcon) {
+            expandIcon.hide()
+          }
+        }
+      }
+    }
+  },
+  onNodeMouseLeave(ev) {
+    const { item } = ev
+    const model = item.getModel()
+    const group = item.getContainer()
+    const { collapsed } = model
+
+    if (!collapsed) {
+      const childCountGroup = group.find(e => e.get('name') === 'child-count-group')
+      if (childCountGroup) {
+        childCountGroup.hide()
+      }
+
+      const iconsLinkPath = group.find(e => e.get('name') === 'icons-link-path')
+      if (iconsLinkPath) {
+        iconsLinkPath.hide()
+      }
+    }
+
+    this.graph.emit('tooltip: hide', ev)
+  }
+})
+G6.registerBehavior('click-node', {
+  getEvents() {
+    return {
+      'node:click': 'onNodeClick',
+      'canvas:click': 'onCanvasClick'
+    }
+  },
+  onNodeClick(e) {
+    const { item, target } = e
+    const shape = target
+    const shapeName = shape.cfg.name
+    let model = item.getModel()
+
+    // 鐐瑰嚮鏀惰捣/灞曞紑 icon
+    if (shapeName === 'child-count-rect-shape' || shapeName === 'child-count-text-shape') {
+      const updatedCollapsed = !model.collapsed
+      this.graph.updateItem(item, { collapsed: updatedCollapsed })
+      this.graph.layout()
+      return
+    }
+
+    // 閫変腑鑺傜偣
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+
+    model.selected = true
+    this.graph.updateItem(item, model, false)
+
+    return
+  },
+  onCanvasClick(e) {
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+  }
+})
+const dataIndTransform = (data) => {
+  const changeData = (d) => {
+    let data = { ...d }
+
+    data.type = data.isRoot ? 'indentedRoot' : 'indentedNode'
+
+    if (d.children) {
+      data.children = d.children.map((child) => changeData(child))
+    }
+    // 缁欏畾 branchColor 鍜� 0-2 灞傝妭鐐� depth
+    if (data.children && data.children.length) {
+      data.depth = 0
+      data.children.forEach((subtree, i) => {
+        subtree.branchColor = COLORS[i % COLORS.length]
+        // dfs
+        let currentDepth = 1
+        subtree.depth = currentDepth
+        Util.traverseTree(subtree, child => {
+          child.branchColor = COLORS[i % COLORS.length]
+
+          if (!child.depth) {
+            child.depth = currentDepth + 1
+          }
+          else currentDepth = subtree.depth
+          if (child.children) {
+            child.children.forEach(subChild => {
+              subChild.depth = child.depth + 1
+            })
+          }
+
+          if (!data.isRoot) {
+            child.collapsed = data.collapsed || false
+          }
+          return true
+        })
+      })
+    }
+
+    return data
+  }
+  return changeData(data)
+}
+
+// 鐭ヨ瘑鍥捐氨鏍�
+G6.registerNode('treeNode', {
+  draw: (cfg, group) => {
+    const { label, selected, children, isRoot } = cfg
+    const rootNode = !!isRoot
+    const hasChildren = children && children.length !== 0
+
+    let width = 0
+    const height = 28
+    const x = 0
+    const y = -height / 2
+
+    // 鍚嶇О鏂囨湰
+    const text = group.addShape('text', {
+      attrs: {
+        text: label,
+        x: x * 2,
+        y,
+        textAlign: 'left',
+        textBaseline: 'top',
+        fontFamily: 'PingFangSC-Regular',
+      },
+      cursor: 'pointer',
+      name: 'name-text-shape',
+    })
+    const textWidth = text.getBBox().width
+    width = textWidth + 20
+
+    width = width < 60 ? 60 : width
+
+    if (!rootNode && hasChildren) {
+      width += 22
+    }
+
+    const keyShapeAttrs = {
+      x,
+      y,
+      width,
+      height,
+      radius: 4
+    }
+
+    const keyShape = group.addShape('rect', {
+      attrs: keyShapeAttrs,
+      name: 'root-key-shape-rect-shape'
+    })
+
+    if (!rootNode) {
+      // 搴曢儴妯嚎
+      group.addShape('path', {
+        attrs: {
+          path: [
+            ['M', x - 1, 0],
+            ['L', width, 0],
+          ],
+          stroke: '#AAB7C4',
+          lineWidth: 1,
+        },
+        name: 'node-path-shape'
+      })
+    }
+
+    const mainX = x - 10
+    const mainY = -height + 15
+
+    if (rootNode) {
+      group.addShape('rect', {
+        attrs: {
+          x: mainX,
+          y: mainY,
+          width: width + 12,
+          height,
+          radius: 14,
+          fill: systemColor,
+          cursor: 'pointer',
+        },
+        name: 'main-shape'
+      })
+    }
+
+    let nameColor = 'rgba(0, 0, 0, 0.85)'
+    if (selected) {
+      nameColor = systemColor
+    }
+
+    // 鍚嶇О
+    if (rootNode) {
+      group.addShape('text', {
+        attrs: {
+          text: label,
+          x: mainX + 18,
+          y: 1,
+          textAlign: 'left',
+          textBaseline: 'middle',
+          fill: '#ffffff',
+          fontSize: 12,
+          fontFamily: 'PingFangSC-Regular',
+          cursor: 'pointer',
+        },
+        name: 'root-text-shape'
+      })
+    } else {
+      group.addShape('text', {
+        attrs: {
+          text: label,
+          x: mainX + 6,
+          y: y - 5,
+          textAlign: 'start',
+          textBaseline: 'top',
+          fill: nameColor,
+          fontSize: 12,
+          fontFamily: 'PingFangSC-Regular',
+          cursor: 'pointer',
+        },
+        name: 'not-root-text-shape'
+      })
+    }
+
+    // 瀛愮被鏁伴噺
+    if (hasChildren && !rootNode) {
+      const childCountHeight = 12
+      const childCountX = width - 22
+      const childCountY = -childCountHeight / 2
+
+      group.addShape('rect', {
+        attrs: {
+          width: 22,
+          height: 12,
+          stroke: systemColor,
+          fill: '#fff',
+          x: childCountX,
+          y: childCountY,
+          radius: 6,
+          cursor: 'pointer',
+        },
+        name: 'child-count-rect-shape',
+      })
+      group.addShape('text', {
+        attrs: {
+          text: `${children.length}`,
+          fill: 'rgba(0, 0, 0, .65)',
+          x: childCountX + 11,
+          y: childCountY + 12,
+          fontSize: 10,
+          width: 22,
+          textAlign: 'center',
+          cursor: 'pointer',
+        },
+        name: 'child-count-text-shape'
+      })
+    }
+
+    return keyShape
+  }
+})
+
+G6.registerEdge('smooth', {
+  draw(cfg, group) {
+    const { startPoint, endPoint } = cfg
+    const hgap = Math.abs(endPoint.x - startPoint.x)
+
+    const path = [
+      ['M', startPoint.x, startPoint.y],
+      [
+        'C',
+        startPoint.x + hgap / 4,
+        startPoint.y,
+        endPoint.x - hgap / 2,
+        endPoint.y,
+        endPoint.x,
+        endPoint.y,
+      ],
+    ]
+
+    const shape = group.addShape('path', {
+      attrs: {
+        stroke: '#AAB7C4',
+        path,
+      },
+      name: 'smooth-path-shape',
+    })
+    return shape
+  },
+})
+
+G6.registerBehavior('click-item', {
+  getEvents() {
+    return {
+      'node:click': 'onNodeClick',
+      'canvas:click': 'onCanvasClick'
+    }
+  },
+  onNodeClick(e) {
+    const { item } = e
+    let model = item.getModel()
+
+    if (model.children) return
+    // 閫変腑鑺傜偣
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+
+    model.selected = true
+    this.graph.updateItem(item, model, false)
+
+    return
+  },
+  onCanvasClick(e) {
+    this.graph.getNodes().forEach(node => {
+      let _model = node.get('model')
+      if (_model.selected) {
+        _model.selected = false
+        this.graph.updateItem(node, _model, false)
+      }
+    })
+  }
+})
+
+class antvG6Chart extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    updateConfig: PropTypes.func,
+    deletecomponent: PropTypes.func,
+  }
+
+  state = {
+    card: null,
+    eventListener: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _plot = {
+        width: card.width || 24,
+        height: 400,
+        subtype: card.subtype,
+        name: card.name
+      }
+
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        format: 'array',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,   // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: true,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: _plot.width,
+        name: _plot.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        style: {
+          borderWidth: '1px', borderColor: 'rgb(217, 217, 217)',
+          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
+        },
+        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
+        columns: [],
+        scripts: [],
+        search: [],
+        action: [],
+        plot: _plot,
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.plot = config.plot
+        _card.plot.name = card.name
+        _card.style = config.style
+        _card.headerStyle = config.headerStyle
+
+        _card.setting = config.setting
+        _card.columns = config.columns
+        _card.scripts = config.scripts
+      }
+
+      this.updateComponent(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('tabsChange', this.handleTabsChange)
+    setTimeout(() => {
+      this.viewrender()
+    }, 1000)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
+  }
+
+  handleTabsChange = (parentId) => {
+    const { card } = this.state
+
+    if (parentId.indexOf(card.uuid) > -1 || parentId === 'all') {
+      let _element = document.getElementById(card.uuid + 'canvas')
+      if (_element) {
+        _element.innerHTML = ''
+      }
+
+      this.$timer && clearTimeout(this.$timer)
+      this.$timer = setTimeout(this.viewrender, 100)
+    }
+  }
+
+  getdata = () => {
+    const { card } = this.state
+
+    const setData = (list) => {
+      let item = {
+        label: list[0],
+        id: list[0],
+        children: []
+      }
+      if (!list[1]) {
+        delete item.children
+
+        return item
+      } else if (!Array.isArray(list[1])) {
+        return list.map(m => ({label: m, id: m}))
+      }
+
+      for (let i = 1; i < list.length; i++) {
+        let cell = setData(list[i])
+
+        if (Array.isArray(cell)) {
+          item.children.push(...cell)
+        } else {
+          item.children.push(cell)
+        }
+      }
+  
+      return item
+    }
+
+    let data = setData(MindData)
+
+    if (card.plot.subtype === 'mindmap') {
+      if (card.plot.dirField) {
+        data.children[0].direction = 'left'
+        data.children[2].direction = 'left'
+      }
+  
+      data.children.forEach(item => {
+        if (item.direction === 'left') {
+          item.color = card.plot.leftColor || '#26C281'
+        } else {
+          item.color = card.plot.nodeColor || '#1890ff'
+        }
+      })
+    } else if (card.plot.subtype === 'indentTree') {
+      data.isRoot = true
+      data.collapsed = false
+
+      data.children.forEach(item => {
+        item.collapsed = card.plot.collapsed === 'true'
+      })
+    } else if (card.plot.subtype === 'kapmap') {
+      data.isRoot = true
+      data.collapsed = false
+
+      if (card.plot.collapsed === 'true') {
+        const collapse = (item) => {
+          if (!item.children) return
+
+          item.children.forEach(cell => {
+            cell.collapsed = true
+            collapse(cell)
+          })
+        }
+
+        collapse(data)
+      }
+    }
+    
+    return data
+  }
+
+  viewrender = () => {
+    const { card } = this.state
+
+    if (card.plot.subtype === 'mindmap') {
+      this.ponitrender()
+    } else if (card.plot.subtype === 'indentTree') {
+      this.indentrender()
+    } else if (card.plot.subtype === 'kapmap') {
+      this.kapmaprender()
+    }
+  }
+
+  kapmaprender = () => {
+    const { card } = this.state
+    const plot = card.plot
+    const data = this.getdata()
+
+    const graph = new G6.TreeGraph({
+      container: card.uuid + 'canvas',
+      width: this.wrap.scrollWidth - 30,
+      height: getHeight(plot.height),
+      modes: {
+        default: [
+          {
+            type: 'collapse-expand',
+          },
+          'drag-canvas',
+          'zoom-canvas',
+          'click-item'
+        ],
+      },
+      defaultNode: {
+        type: 'treeNode',
+        anchorPoints: [
+          [0, 0.5],
+          [1, 0.5],
+        ],
+      },
+      defaultEdge: {
+        type: 'smooth',
+      },
+      layout: {
+        type: 'compactBox',
+        direction: 'LR',
+        getId: function getId(d) {
+          return d.id
+        },
+        getHeight: function getHeight() {
+          return 16
+        },
+        getWidth: function getWidth(d) {
+          const labelWidth = G6.Util.getTextSize(d.label, 12)[0]
+          const width = 60 + labelWidth
+          return width
+        },
+        getVGap: function getVGap() {
+          return 15
+        },
+        getHGap: function getHGap() {
+          return 30
+        }
+      }
+    })
+
+    graph.data(data)
+    graph.render()
+    graph.fitView()
+  }
+
+  indentrender = () => {
+    const { card } = this.state
+    const plot = card.plot
+    const data = this.getdata()
+
+    const tree = new G6.TreeGraph({
+      container: card.uuid + 'canvas',
+      width: this.wrap.scrollWidth - 30,
+      height: getHeight(plot.height),
+      layout: {
+        type: 'indented',
+        direction: 'LR',
+        isHorizontal: true,
+        indent: 40,
+        getHeight: (d) => {
+          if (d.isRoot) {
+            return 30
+          }
+          if (d.collapsed && d.children && d.children.length) {
+            return 36
+          }
+          return 22
+        },
+        getVGap: () => {
+          return 10
+        },
+      },
+      defaultEdge: {
+        type: 'indentedEdge',
+        style: {
+          lineWidth: 2,
+          radius: 16
+        }
+      },
+      minZoom: 0.5,
+      modes: {
+        default: [
+          'drag-canvas',
+          'wheel-scroll',
+          'hover-node',
+          'click-node'
+        ]
+      }
+    })
+    
+    tree.on('afterrender', e => {
+      tree.getEdges().forEach(edge => {
+        const targetNode = edge.getTarget().getModel()
+        const color = targetNode.branchColor
+        tree.updateItem(edge, { color })
+      })
+      setTimeout(() => {
+        tree.moveTo(32, 32)
+        tree.zoomTo(0.7)
+      }, 16)
+    })
+    
+    tree.data(dataIndTransform(data))
+    
+    tree.render()
+  }
+
+  /**
+   * @description 鏁g偣鍥�
+   */
+  ponitrender = () => {
+    const { card } = this.state
+    const plot = card.plot
+    const data = this.getdata()
+
+    const tree = new G6.TreeGraph({
+      container: card.uuid + 'canvas',
+      width: this.wrap.scrollWidth - 30,
+      height: getHeight(plot.height),
+      fitView: true,
+      layout: {
+        type: 'mindmap',
+        direction: 'H',
+        getHeight: () => {
+          return 16
+        },
+        getWidth: (node) => {
+          return node.level === 0 ?
+            Util.getTextSize(node.label, 16)[0] + 12 :
+            Util.getTextSize(node.label, 12)[0]
+        },
+        getVGap: () => {
+          return 10
+        },
+        getHGap: () => {
+          return 60
+        },
+        getSide: (node) => {
+          return node.data.direction
+        }
+      },
+      defaultEdge: {
+        type: 'cubic-horizontal',
+        style: {
+          lineWidth: 2
+        }
+      },
+      minZoom: 0.5,
+      modes: {
+        default: ['drag-canvas', 'zoom-canvas', 'dice-mindmap']
+      }
+    })
+
+    tree.data(dataMapTransform(data))
+
+    tree.render()
+  }
+
+  updateComponent = (card) => {
+    if (this.state.card && (!is(fromJS(card.plot), fromJS(this.state.card.plot)) || !is(fromJS(card.style), fromJS(this.state.card.style)))) {
+      let _element = document.getElementById(card.uuid + 'canvas')
+      if (_element) {
+        _element.innerHTML = ''
+      }
+      this.$timer && clearTimeout(this.$timer)
+      this.$timer = setTimeout(() => {
+        this.viewrender()
+      }, 150)
+    }
+
+    card.width = card.plot.width
+    card.name = card.plot.name
+    card.subtype = card.plot.subtype
+    card.errors = []
+
+    let columns = card.columns.map(c => c.field)
+    
+    if (card.setting.interType === 'system' && card.setting.execute !== 'false' && !card.setting.dataresource) {
+      card.errors.push({ level: 0, detail: '鏈缃暟鎹簮锛�'})
+    } else if (card.setting.interType === 'system' && card.setting.execute === 'false' && card.scripts.filter(script => script.status !== 'false').length === 0) {
+      card.errors.push({ level: 0, detail: '鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+    } else if (!card.setting.primaryKey) {
+      card.errors.push({ level: 0, detail: '鏈缃富閿紒'})
+    } else if (!columns.includes(card.setting.primaryKey)) {
+      card.errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
+    } else if (!card.setting.supModule) {
+      card.errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+    }
+
+    if (card.errors.length === 0) {
+      card.$tables = getTables(card)
+    }
+    
+    if (!card.plot.valueField) {
+      card.errors.push({ level: 0, detail: '鍥捐〃淇℃伅灏氭湭璁剧疆锛�'})
+    }
+
+    this.setState({
+      card: card
+    })
+    this.props.updateConfig(card)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
+  }
+
+  getStyle = (style) => {
+    let _card = {...this.state.card, style}
+
+    this.updateComponent(_card)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card.uuid, null, (style) => {
+        let _card = {...this.state.card}
+        _card.style = {..._card.style, ...style}
+
+        this.updateComponent(_card)
+      })
+    }
+  }
+
+  render() {
+    const { card } = this.state
+    let _style = resetStyle(card.style)
+
+    return (
+      <div className="menu-scatter-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <ChartCompileForm config={card} plotchange={this.updateComponent}/>
+            <CopyComponent type="antvG6" card={card}/>
+            <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
+            <UserComponent config={card}/>
+            <DeleteOutlined className="close" title="delete" onClick={() => this.props.deletecomponent(card.uuid)}/>
+            <SettingComponent config={card} updateConfig={this.updateComponent}/>
+          </div>
+        } trigger="hover">
+          <ToolOutlined/>
+        </Popover>
+        <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
+        <div className="component-name">
+          <div className="center">
+            <div className="title">{card.name}</div>
+            <div className="content">
+              {card.errors && card.errors.map((err, index) => {
+                if (err.level === 0) {
+                  return <span key={index} className="error">{err.detail}</span>
+                } else {
+                  return <span key={index} className="waring">{err.detail}锛�</span>
+                }
+              })}
+            </div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default antvG6Chart
\ No newline at end of file
diff --git a/src/menu/components/chart/antv-G6/index.scss b/src/menu/components/chart/antv-G6/index.scss
new file mode 100644
index 0000000..5d8ba6f
--- /dev/null
+++ b/src/menu/components/chart/antv-G6/index.scss
@@ -0,0 +1,68 @@
+.menu-scatter-chart-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  
+  .canvas {
+    margin: 0px;
+    padding: 15px;
+    letter-spacing: 0px;
+  }
+
+  .chart-header {
+    position: relative;
+    height: 45px;
+    border-bottom: 1px solid #e8e8e8;
+    overflow: hidden;
+    padding-right: 35px;
+
+    .chart-title {
+      text-decoration: inherit;
+      font-weight: inherit;
+      font-style: inherit;
+      float: left;
+      line-height: 45px;
+      margin-left: 10px;
+      position: relative;
+      z-index: 1;
+    }
+  }
+
+  >.anticon-tool {
+    position: absolute;
+    right: 1px;
+    top: 1px;
+    z-index: 3;
+    font-size: 16px;
+    padding: 5px;
+    cursor: pointer;
+    color: rgba(0, 0, 0, 0.85);
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .model-menu-action-list {
+    position: absolute;
+    right: 0px;
+    top: 30px;
+    z-index: 4;
+    font-size: 16px;
+  
+    .ant-row .anticon-plus {
+      float: right;
+    }
+  
+    .page-card {
+      float: right;
+    }
+  }
+  .normal-header + .canvas + .model-menu-action-list {
+    top: 45px;
+  }
+}
+.menu-scatter-chart-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
index 432e43f..492d58a 100644
--- a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -69,14 +69,13 @@
       required: true
     },
     {
-      type: 'number',
+      type: 'styleInput',
       key: 'height',
-      label: '楂樺害',
+      label: '鍥捐〃楂樺害',
       initVal: card.height,
-      min: 100,
-      max: 1000,
-      decimal: 0,
-      required: true
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
     },
     {
       type: 'radio',
@@ -105,11 +104,10 @@
       key: 'click',
       label: '鐐瑰嚮浜嬩欢',
       initVal: card.click || '',
-      tooltip: '褰撶粦瀹氱偣鍑讳簨浠舵椂锛屾煴褰㈠浘鐨勫垏鎹㈠姛鑳藉皢澶辨晥銆�',
       required: false,
       forbid: appType === 'mob' || card.chartType !== 'bar',
       options: [
-        {value: '', label: '鏃�'},
+        {value: '', label: '鏁版嵁鍒囨崲'},
         {value: 'menu', label: '鑿滃崟'},
         {value: 'menus', label: '鑿滃崟缁�'}
       ]
diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx
index 319cd3f..082b5fa 100644
--- a/src/menu/components/chart/antv-bar/index.jsx
+++ b/src/menu/components/chart/antv-bar/index.jsx
@@ -9,7 +9,7 @@
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import { chartColors } from '@/utils/option.js'
 import './index.scss'
@@ -228,7 +228,7 @@
       const chart = new Chart({
         container: card.uuid + 'canvas',
         autoFit: true,
-        height: this.wrap.offsetHeight - 25
+        height: getHeight(plot.height)
       })
   
       chart.data(dv.rows)
@@ -554,7 +554,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: this.wrap.offsetHeight - 25,
+      height: getHeight(plot.height)
     })
 
     // chart.axis(plot.Xaxis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } })
@@ -948,7 +948,7 @@
       const chart = new Chart({
         container: card.uuid + 'canvas',
         autoFit: true,
-        height: this.wrap.offsetHeight - 25
+        height: getHeight(plot.height)
       })
 
       chart.data(dv.rows)
@@ -1331,7 +1331,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -1354,10 +1354,12 @@
 
   render() {
     const { card, appType } = this.state
+
     let _style = resetStyle(card.style)
+    _style.height = 'auto'
 
     return (
-      <div className="menu-line-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-line-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             {appType !== 'mob' ? <PlusCircleOutlined className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch}/> : null}
@@ -1375,7 +1377,7 @@
           <ToolOutlined/>
         </Popover>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
-        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
+        <div className="canvas" id={card.uuid + 'canvas'}></div>
         {appType !== 'mob' ? <ActionComponent
           config={card}
           updateaction={this.updateComponent}
diff --git a/src/menu/components/chart/antv-bar/index.scss b/src/menu/components/chart/antv-bar/index.scss
index cc5f914..c978810 100644
--- a/src/menu/components/chart/antv-bar/index.scss
+++ b/src/menu/components/chart/antv-bar/index.scss
@@ -5,14 +5,11 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  display: flex;
-  flex-flow: column;
 
   .canvas {
     margin: 0px;
-    padding: 15px 10px 10px;
+    padding: 15px;
     letter-spacing: 0px;
-    flex: 1;
   }
 
   .chart-header {
diff --git a/src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx
index 453f22e..61a1508 100644
--- a/src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx
@@ -43,14 +43,13 @@
       required: true
     },
     {
-      type: 'number',
+      type: 'styleInput',
       key: 'height',
-      label: '楂樺害',
+      label: '鍥捐〃楂樺害',
       initVal: card.height,
-      min: 100,
-      max: 1000,
-      decimal: 0,
-      required: true
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
     },
     {
       type: 'radio',
diff --git a/src/menu/components/chart/antv-dashboard/index.jsx b/src/menu/components/chart/antv-dashboard/index.jsx
index 22f6b8e..33c7c2e 100644
--- a/src/menu/components/chart/antv-dashboard/index.jsx
+++ b/src/menu/components/chart/antv-dashboard/index.jsx
@@ -8,7 +8,7 @@
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
@@ -227,7 +227,7 @@
     const chart = new Chart({
       container: card.uuid + 'dashboard',
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: getHeight(plot.height)
     })
     
     chart.data(data)
@@ -326,7 +326,7 @@
     const chart = new Chart({
       container: card.uuid + 'dashboard',
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: getHeight(plot.height),
       padding: [0, 0, 0, 0],
     })
     chart.data(data)
@@ -508,7 +508,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -532,9 +532,10 @@
   render() {
     const { card } = this.state
     let _style = resetStyle(card.style)
+    _style.height = 'auto'
 
     return (
-      <div className="menu-dashboard-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-dashboard-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <ChartCompileForm config={card} plotchange={this.updateComponent}/>
@@ -549,7 +550,7 @@
           <ToolOutlined/>
         </Popover>
         <NormalHeader hideSearch="true" config={card} updateComponent={this.updateComponent}/>
-        <div className="canvas" id={card.uuid + 'dashboard'} ref={ref => this.wrap = ref}></div>
+        <div className="canvas" id={card.uuid + 'dashboard'}></div>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/chart/antv-dashboard/index.scss b/src/menu/components/chart/antv-dashboard/index.scss
index b017bc1..b55876e 100644
--- a/src/menu/components/chart/antv-dashboard/index.scss
+++ b/src/menu/components/chart/antv-dashboard/index.scss
@@ -5,14 +5,11 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  display: flex;
-  flex-flow: column;
   
   .canvas {
     margin: 0px;
     padding: 15px;
     letter-spacing: 0px;
-    flex: 1;
   }
 
   >.anticon-tool {
@@ -25,22 +22,6 @@
     cursor: pointer;
     color: rgba(0, 0, 0, 0.85);
     background: rgba(255, 255, 255, 0.55);
-  }
-
-  .model-menu-action-list {
-    position: absolute;
-    right: 0px;
-    z-index: 4;
-    padding-top: 10px;
-    font-size: 16px;
-  
-    .ant-row .anticon-plus {
-      float: right;
-    }
-  
-    .page-card {
-      float: right;
-    }
   }
 }
 .menu-dashboard-edit-box:hover {
diff --git a/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
index fbdeeee..7c34a71 100644
--- a/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
@@ -69,14 +69,13 @@
       required: true
     },
     {
-      type: 'number',
+      type: 'styleInput',
       key: 'height',
-      label: '楂樺害',
+      label: '鍥捐〃楂樺害',
       initVal: card.height,
-      min: 100,
-      max: 1000,
-      decimal: 0,
-      required: true
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
     },
     {
       type: 'cascader',
diff --git a/src/menu/components/chart/antv-pie/index.jsx b/src/menu/components/chart/antv-pie/index.jsx
index 9009ffd..a7e3f8a 100644
--- a/src/menu/components/chart/antv-pie/index.jsx
+++ b/src/menu/components/chart/antv-pie/index.jsx
@@ -9,7 +9,7 @@
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
@@ -143,6 +143,22 @@
     }
   }
 
+  getHeight = (val) => {
+    if (typeof(val) === 'string') {
+      if (val.indexOf('px') > -1) {
+        val = parseFloat(val)
+      } else if (val.indexOf('vw') > -1) {
+        val = parseFloat(val)
+        val = document.body.clientWidth * val / 100
+      } else if (val.indexOf('vh') > -1) {
+        val = parseFloat(val)
+        val = document.body.clientHeight * val / 100
+      }
+    }
+
+    return parseInt(val || 400)
+  }
+
   viewrender = () => {
     const { card } = this.state
 
@@ -248,7 +264,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: getHeight(plot.height),
       padding: 0,
     })
 
@@ -419,7 +435,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: this.wrap.offsetHeight - 30
+      height: getHeight(plot.height)
     })
 
     if (plot.shape !== 'nightingale' && plot.show !== 'value') {
@@ -665,7 +681,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -689,9 +705,10 @@
   render() {
     const { card, appType } = this.state
     let _style = resetStyle(card.style)
+    _style.height = 'auto'
 
     return (
-      <div className="menu-pie-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-pie-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             {appType !== 'mob' ? <PlusCircleOutlined className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch}/> : null}
@@ -707,7 +724,7 @@
           <ToolOutlined />
         </Popover>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
-        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
+        <div className="canvas" id={card.uuid + 'canvas'}></div>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/chart/antv-pie/index.scss b/src/menu/components/chart/antv-pie/index.scss
index 13d75d2..184b89a 100644
--- a/src/menu/components/chart/antv-pie/index.scss
+++ b/src/menu/components/chart/antv-pie/index.scss
@@ -5,14 +5,11 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  display: flex;
-  flex-flow: column;
   
   .canvas {
     margin: 0px;
     padding: 15px;
     letter-spacing: 0px;
-    flex: 1;
   }
 
   >.anticon-tool {
@@ -25,22 +22,6 @@
     cursor: pointer;
     color: rgba(0, 0, 0, 0.85);
     background: rgba(255, 255, 255, 0.55);
-  }
-
-  .model-menu-action-list {
-    position: absolute;
-    right: 0px;
-    z-index: 4;
-    padding-top: 10px;
-    font-size: 16px;
-  
-    .ant-row .anticon-plus {
-      float: right;
-    }
-  
-    .page-card {
-      float: right;
-    }
   }
 }
 .menu-pie-chart-edit-box:hover {
diff --git a/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx
index 79b4f14..45f9942 100644
--- a/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx
@@ -43,14 +43,13 @@
       required: true
     },
     {
-      type: 'number',
+      type: 'styleInput',
       key: 'height',
-      label: '楂樺害',
+      label: '鍥捐〃楂樺害',
       initVal: card.height,
-      min: 100,
-      max: 1000,
-      decimal: 0,
-      required: true
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
     },
     {
       type: 'radio',
diff --git a/src/menu/components/chart/antv-scatter/index.jsx b/src/menu/components/chart/antv-scatter/index.jsx
index 2180026..2534961 100644
--- a/src/menu/components/chart/antv-scatter/index.jsx
+++ b/src/menu/components/chart/antv-scatter/index.jsx
@@ -8,7 +8,7 @@
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
@@ -185,7 +185,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: this.wrap.offsetHeight - 25
+      height: getHeight(plot.height)
     })
 
     chart.data(data);
@@ -350,7 +350,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -374,9 +374,10 @@
   render() {
     const { card, appType } = this.state
     let _style = resetStyle(card.style)
+    _style.height = 'auto'
 
     return (
-      <div className="menu-scatter-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-scatter-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             {appType !== 'mob' ? <PlusCircleOutlined className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch}/> : null}
@@ -394,7 +395,7 @@
           <ToolOutlined/>
         </Popover>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
-        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
+        <div className="canvas" id={card.uuid + 'canvas'}></div>
         {appType !== 'mob' ? <ActionComponent config={card} updateaction={this.updateComponent}/> : null}
         <div className="component-name">
           <div className="center">
diff --git a/src/menu/components/chart/antv-scatter/index.scss b/src/menu/components/chart/antv-scatter/index.scss
index 4c27973..5d8ba6f 100644
--- a/src/menu/components/chart/antv-scatter/index.scss
+++ b/src/menu/components/chart/antv-scatter/index.scss
@@ -5,14 +5,11 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  display: flex;
-  flex-flow: column;
   
   .canvas {
     margin: 0px;
-    padding: 15px 10px 10px;
+    padding: 15px;
     letter-spacing: 0px;
-    flex: 1;
   }
 
   .chart-header {
diff --git a/src/menu/components/chart/chart-custom/chartcompile/formconfig.jsx b/src/menu/components/chart/chart-custom/chartcompile/formconfig.jsx
index 26955ae..11c362b 100644
--- a/src/menu/components/chart/chart-custom/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/chart-custom/chartcompile/formconfig.jsx
@@ -54,14 +54,13 @@
       required: true
     },
     {
-      type: 'number',
+      type: 'styleInput',
       key: 'height',
-      label: '楂樺害',
+      label: '鍥捐〃楂樺害',
       initVal: card.height,
-      min: 100,
-      max: 1000,
-      decimal: 0,
-      required: true
+      tooltip: '鍥捐〃缁樺浘鍖哄煙鐨勯珮搴︼紝涓嶅寘鎷爣棰樺強鍐呭杈硅窛銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
     },
     {
       type: 'radio',
diff --git a/src/menu/components/chart/chart-custom/index.jsx b/src/menu/components/chart/chart-custom/index.jsx
index d5ea198..d601ba4 100644
--- a/src/menu/components/chart/chart-custom/index.jsx
+++ b/src/menu/components/chart/chart-custom/index.jsx
@@ -10,7 +10,7 @@
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import { resetStyle, getTables, getHeight } from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
@@ -231,7 +231,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -255,9 +255,10 @@
   render() {
     const { card, appType } = this.state
     let _style = resetStyle(card.style)
+    _style.height = 'auto'
 
     return (
-      <div className="menu-custom-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
+      <div className="menu-custom-chart-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             {appType !== 'mob' ? <PlusCircleOutlined className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch}/> : null}
@@ -272,7 +273,9 @@
           <ToolOutlined />
         </Popover>
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
-        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
+        <div className="canvas-wrap" style={{height: card.plot.height}}>
+          <div className="canvas" id={card.uuid + 'canvas'} style={{height: getHeight(card.plot.height)}} ref={ref => this.wrap = ref}></div>
+        </div>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/chart/chart-custom/index.scss b/src/menu/components/chart/chart-custom/index.scss
index 8bd0443..c946907 100644
--- a/src/menu/components/chart/chart-custom/index.scss
+++ b/src/menu/components/chart/chart-custom/index.scss
@@ -5,14 +5,14 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  display: flex;
-  flex-flow: column;
 
+  .canvas-wrap {
+    padding: 15px;
+  }
   .canvas {
     margin: 0px;
     padding: 0px;
     letter-spacing: 0px;
-    flex: 1;
   }
 
   .chart-header {
@@ -44,25 +44,6 @@
     cursor: pointer;
     color: rgba(0, 0, 0, 0.85);
     background: rgba(255, 255, 255, 0.55);
-  }
-
-  .model-menu-action-list {
-    position: absolute;
-    right: 0px;
-    top: 30px;
-    z-index: 4;
-    font-size: 16px;
-  
-    .ant-row .anticon-plus {
-      float: right;
-    }
-  
-    .page-card {
-      float: right;
-    }
-  }
-  .normal-header + .canvas + .model-menu-action-list {
-    top: 45px;
   }
 }
 .menu-custom-chart-edit-box:hover {
diff --git a/src/menu/components/code/sandbox/codecontent/index.jsx b/src/menu/components/code/sandbox/codecontent/index.jsx
index 306fd59..42ff7c2 100644
--- a/src/menu/components/code/sandbox/codecontent/index.jsx
+++ b/src/menu/components/code/sandbox/codecontent/index.jsx
@@ -4,85 +4,77 @@
 
 import './index.scss'
 
-class BraftContent extends Component {
+class CodeContent extends Component {
   static propTpyes = {
-    name: PropTypes.string,
-    html: PropTypes.any,
-    css: PropTypes.any,
-    js: PropTypes.any,
+    config: PropTypes.object,
   }
 
-  state = {
-    csselement: null
-  }
+  state = {}
 
   UNSAFE_componentWillMount () {
-    const { css } = this.props
+    const { config } = this.props
 
-    if (css) {
-      // let style = css.replace(/^[^}{]*{|}[^}{]*{/ig, (word) => {
-      //   return word.replace(/}\n*/ig, `}\n#${mark}`).replace(/,/ig, `,#${mark} `)
-      // })
-      // style = `\n/* 鑷畾涔� */\n#${mark} ${style}\n`
-
+    if (config.css) {
+      let node = document.getElementById(config.uuid + 'style')
+      node && node.remove()
+      
       let ele = document.createElement('style')
-      ele.innerHTML = css
+      ele.id = config.uuid + 'style'
+      ele.innerHTML = config.css
       document.getElementsByTagName('head')[0].appendChild(ele)
-      // document.getElementsByTagName('head')[0].prepend(ele)
-
-      this.setState({csselement: ele})
     }
   }
 
   componentDidMount () {
-    const { js, name } = this.props
-    if (js) {
+    const { config } = this.props
+    if (config.js && config.wrap.compileMode !== 'custom') {
       try {
         // eslint-disable-next-line no-eval
-        eval(js)
+        eval(config.js)
       } catch (e) {
-        message.warning(name + 'JS 鎵ц澶辫触锛�')
+        message.warning(config.name + 'JS 鎵ц澶辫触锛�')
+        console.warn(config.name + e)
       }
     }
   }
 
   UNSAFE_componentWillReceiveProps(nextProps) {
-    if (this.props.css !== nextProps.css) {
-      const { csselement } = this.state
-      
-      if (csselement && csselement.remove) {
-        csselement.remove()
-      }
-      if (nextProps.css) {
-        let ele = document.createElement('style')
-        ele.innerHTML = nextProps.css
-        document.getElementsByTagName('head')[0].appendChild(ele)
+    const { config } = this.props
 
-        this.setState({csselement: ele})
+    if (config.css !== nextProps.config.css) {
+      let node = document.getElementById(config.uuid + 'style')
+      node && node.remove()
+
+      if (nextProps.config.css) {
+        let ele = document.createElement('style')
+        ele.id = config.uuid + 'style'
+        ele.innerHTML = nextProps.config.css
+        document.getElementsByTagName('head')[0].appendChild(ele)
       }
     }
-    if (this.props.html !== nextProps.html || this.props.js !== nextProps.js) {
-      if (nextProps.js) {
+    if (config.html !== nextProps.config.html || config.js !== nextProps.config.js) {
+      if (nextProps.config.js && nextProps.config.wrap.compileMode !== 'custom') {
         try {
           // eslint-disable-next-line no-eval
-          eval(nextProps.js)
+          eval(nextProps.config.js)
         } catch (e) {
-          message.warning(nextProps.name + 'JS 鎵ц澶辫触锛�')
+          message.warning(config.name + 'JS 鎵ц澶辫触锛�')
+          console.warn(config.name + e)
         }
       }
     }
   }
 
   render() {
-    const { html } = this.props
+    const { config } = this.props
 
-    if (!html) return <Empty style={{padding: '10px 0px'}} description={null}/>
+    if (!config.html) return <Empty style={{padding: '10px 0px'}} description={null}/>
 
     return (
-      <div dangerouslySetInnerHTML={{ __html: html }}></div>
+      <div dangerouslySetInnerHTML={{ __html: config.html }}></div>
     )
   }
 }
 
 
-export default BraftContent
\ No newline at end of file
+export default CodeContent
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/index.jsx b/src/menu/components/code/sandbox/index.jsx
index 635e948..d633dd7 100644
--- a/src/menu/components/code/sandbox/index.jsx
+++ b/src/menu/components/code/sandbox/index.jsx
@@ -38,7 +38,7 @@
       let _card = {
         uuid: card.uuid,
         type: card.type,
-        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        format: 'array',    // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
         pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
         switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
         width: card.width || 24,
@@ -71,8 +71,11 @@
       
       this.updateComponent(_card)
     } else {
+      let _card = fromJS(card).toJS()
+      _card.format = 'array'
+
       this.setState({
-        card: fromJS(card).toJS()
+        card: _card
       })
     }
   }
@@ -125,7 +128,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -168,7 +171,7 @@
             <NormalForm title="鑷畾涔夌粍浠惰缃�" width={700} update={this.updateWrap} getForms={this.getWrapForms}>
               <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
             </NormalForm>
-            <CopyComponent type="normaltable" card={card}/>
+            <CopyComponent type="sandbox" card={card}/>
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
             <UserComponent config={card}/>
             <DeleteOutlined className="close" title="鍒犻櫎缁勪欢" onClick={() => this.props.deletecomponent(card.uuid)} />
@@ -179,7 +182,7 @@
         } trigger="hover">
           <ToolOutlined />
         </Popover>
-        <CodeContent name={card.name} html={card.html} css={card.css} js={card.js}/>
+        <CodeContent config={card}/>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/code/sandbox/options.jsx b/src/menu/components/code/sandbox/options.jsx
index 6aab039..de03409 100644
--- a/src/menu/components/code/sandbox/options.jsx
+++ b/src/menu/components/code/sandbox/options.jsx
@@ -45,6 +45,20 @@
       options: [
         {value: 'dynamic', label: '鍔ㄦ��'},
         {value: 'static', label: '闈欐��'},
+      ],
+      controlFields: [
+        {field: 'compileMode', values: ['dynamic']}
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'compileMode',
+      label: '缂栬瘧鏂瑰紡',
+      initval: wrap.compileMode || 'replace',
+      required: false,
+      options: [
+        {value: 'replace', label: '瀛楁鏇挎崲'},
+        {value: 'custom', label: '鑷畾涔�'},
       ]
     },
     {
diff --git a/src/menu/components/editor/braft-editor/index.jsx b/src/menu/components/editor/braft-editor/index.jsx
index a8d23f8..b032aec 100644
--- a/src/menu/components/editor/braft-editor/index.jsx
+++ b/src/menu/components/editor/braft-editor/index.jsx
@@ -128,7 +128,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/menu/components/form/dragtitle/options.jsx b/src/menu/components/form/dragtitle/options.jsx
index b367eab..8f424aa 100644
--- a/src/menu/components/form/dragtitle/options.jsx
+++ b/src/menu/components/form/dragtitle/options.jsx
@@ -87,7 +87,7 @@
       field: 'prevEnable',
       label: '涓婁竴姝�',
       initval: group.prevButton ? group.prevButton.enable || 'false' : 'false',
-      tooltip: '绗竴缁勪笉鏄剧ず銆�',
+      tooltip: '绗竴缁勪笉鏄剧ず銆傛敞锛氶櫎鍏抽棴鍔熻兘澶栥��',
       required: false,
       options: [
         {value: 'true', label: '鏄剧ず'},
@@ -111,7 +111,7 @@
       field: 'nextEnable',
       label: '璺宠繃',
       initval: group.nextButton ? group.nextButton.enable || 'false' : 'false',
-      tooltip: '鏈�鍚庝竴缁勪笉鏄剧ず銆�',
+      tooltip: '鏈�鍚庝竴缁勪笉鏄剧ず銆傛敞锛氶櫎鍏抽棴鍔熻兘澶栥��',
       required: false,
       options: [
         {value: 'true', label: '鏄剧ず'},
diff --git a/src/menu/components/form/formaction/actionform/index.jsx b/src/menu/components/form/formaction/actionform/index.jsx
index b67cad8..7f5eae5 100644
--- a/src/menu/components/form/formaction/actionform/index.jsx
+++ b/src/menu/components/form/formaction/actionform/index.jsx
@@ -54,10 +54,13 @@
     const { card } = this.props
 
     if (card.type === 'prev') {
-      return ['type', 'label', 'enable']
+      return ['type', 'label', 'enable', 'actionType']
     } else if (card.type === 'next') {
+      return ['type', 'label', 'enable', 'actionType']
+    } else if (card.type === 'close') {
       return ['type', 'label', 'enable']
     }
+    
     let _options = ['type', 'label', 'intertype', 'Ot', 'execSuccess', 'syncComponent', 'anchors', 'linkmenu', 'enable', 'output', 'reload'] // 閫夐」鍒楄〃
     
     if (_intertype === 'custom') {
diff --git a/src/menu/components/form/formaction/formconfig.jsx b/src/menu/components/form/formaction/formconfig.jsx
index 5295788..163739f 100644
--- a/src/menu/components/form/formaction/formconfig.jsx
+++ b/src/menu/components/form/formaction/formconfig.jsx
@@ -11,6 +11,8 @@
     _type = '涓婁竴姝�'
   } else if (card.type === 'next') {
     _type = '涓嬩竴姝�'
+  } else if (card.type === 'close') {
+    _type = '鍏抽棴'
   }
 
   let menulist = []
@@ -54,6 +56,21 @@
       initVal: card.label,
       required: true,
       readonly: false
+    },
+    {
+      type: 'radio',
+      key: 'actionType',
+      label: '鎵ц鎿嶄綔',
+      initVal: card.actionType || 'default',
+      tooltip: '鍏抽棴鍔熻兘锛氱鐞嗙郴缁熶腑浼氬叧闂綋鍓嶆爣绛撅紝瀛愬簲鐢ㄤ腑涓鸿繑鍥炰笂涓�椤点��',
+      required: true,
+      options: [{
+        value: 'default',
+        text: '榛樿'
+      }, {
+        value: 'close',
+        text: '鍏抽棴'
+      }]
     },
     {
       type: 'radio',
@@ -147,6 +164,7 @@
       key: 'sysInterface',
       label: '绯荤粺鎺ュ彛',
       initVal: card.sysInterface || 'false',
+      tooltip: '鍗曠偣鐧诲綍绯荤粺',
       required: true,
       options: [{
         value: 'true',
diff --git a/src/menu/components/form/formaction/index.jsx b/src/menu/components/form/formaction/index.jsx
index f0e70b2..57d9865 100644
--- a/src/menu/components/form/formaction/index.jsx
+++ b/src/menu/components/form/formaction/index.jsx
@@ -96,7 +96,7 @@
       supId = config.wrap.supModule[config.wrap.supModule.length - 1]
     }
 
-    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid, supId) || []
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid, supId)
     let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, config.uuid) || []
 
     if (card.type === 'submit' && !card.Ot) {
@@ -136,11 +136,16 @@
       let group = fromJS(this.props.group).toJS()
 
       if (res.type === 'prev') {
+        res.enable = group.prevButton.enable || 'true'
         group.prevButton = res
       } else if (res.type === 'submit') {
         group.subButton = res
       } else if (res.type === 'next') {
+        res.enable = group.nextButton.enable || 'true'
         group.nextButton = res
+      } else if (res.type === 'close') {
+        res.enable = group.closeButton.enable || 'true'
+        group.closeButton = res
       }
 
       this.setState({
@@ -157,7 +162,6 @@
     this.setState({
       profVisible: true
     })
-    MKEmitter.emit('modalStatus', '楠岃瘉淇℃伅')
   }
 
   /**
@@ -173,7 +177,6 @@
         profVisible: false
       })
       this.props.updateconfig(group)
-      MKEmitter.emit('modalStatus', false)
     })
   }
 
@@ -198,7 +201,7 @@
 
     return (
       <div className="mk-form-action">
-        {group.prevButton && group.prevButton.enable !== 'false' && group.sort !== 1 ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+        {group.prevButton && group.prevButton.enable === 'true' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <EditOutlined className="edit" title="缂栬緫" onClick={() => this.handleAction(group.prevButton)} />
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.prevButton)}/>
@@ -215,7 +218,15 @@
         } trigger="hover">
           <Button type="link" className="submit mk-primary" onDoubleClick={this.changeMenu} style={resetStyle(group.subButton.style)}>{group.subButton.label}</Button>
         </Popover>
-        {group.nextButton && group.nextButton.enable !== 'false' && group.sort !== config.subcards.length ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+        {group.closeButton && group.closeButton.enable === 'true' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <EditOutlined className="edit" title="缂栬緫" onClick={() => this.handleAction(group.closeButton)} />
+            <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.closeButton)}/>
+          </div>
+        } trigger="hover">
+          <Button type="link" style={resetStyle(group.closeButton.style)}>{group.closeButton.label}</Button>
+        </Popover> : null}
+        {group.nextButton && group.nextButton.enable === 'true' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <EditOutlined className="edit" title="缂栬緫" onClick={() => this.handleAction(group.nextButton)} />
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.nextButton)}/>
@@ -256,11 +267,9 @@
             if (this.verifyRef.handleCancel) {
               this.verifyRef.handleCancel().then(() => {
                 this.setState({ profVisible: false })
-                MKEmitter.emit('modalStatus', false)
               })
             } else {
               this.setState({ profVisible: false })
-              MKEmitter.emit('modalStatus', false)
             }
           }}
           destroyOnClose
diff --git a/src/menu/components/form/formaction/index.scss b/src/menu/components/form/formaction/index.scss
index d58f09a..79055ca 100644
--- a/src/menu/components/form/formaction/index.scss
+++ b/src/menu/components/form/formaction/index.scss
@@ -13,7 +13,8 @@
   }
   .skip {
     color: rgba(0, 0, 0, 0.85);
-    float: right;
+    position: absolute;
     height: auto;
+    right: 0px;
   }
 }
diff --git a/src/menu/components/form/simple-form/index.jsx b/src/menu/components/form/simple-form/index.jsx
index 3b87f6c..30ea2af 100644
--- a/src/menu/components/form/simple-form/index.jsx
+++ b/src/menu/components/form/simple-form/index.jsx
@@ -194,7 +194,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -429,7 +429,7 @@
       standardform,
       visible: true,
       editform: _form,
-      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields)
+      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields, card.columns)
     })
   }
 
@@ -481,6 +481,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
@@ -583,7 +584,8 @@
   }
 
   updateWrap = (res) => {
-    let _card = {...this.state.card, wrap: res}
+    let _card = fromJS(this.state.card).toJS()
+    _card.wrap = res
 
     if (res.datatype === 'static') {
       if (res.supModule && res.supModule.length > 0) {
@@ -599,6 +601,12 @@
     _card.subcards[0].setting.enable = _card.wrap.enable
     _card.subcards[0].setting.verticalSpace = _card.wrap.verticalSpace
 
+    if (_card.wrap.closeEnable === 'true' && !_card.subcards[0].closeButton) {
+      _card.subcards[0].closeButton = {label: '鍏抽棴', enable: 'true', type: 'close', style: {backgroundColor: '#ffffff', color: 'rgba(0,0,0,0.65)', borderColor: '#d9d9d9', borderWidth: '1px', paddingLeft: '25px', paddingRight: '25px', paddingTop: '5px', paddingBottom: '5px', marginLeft: '10px'}}
+    } else if (_card.subcards[0].closeButton) {
+      _card.subcards[0].closeButton.enable = _card.wrap.closeEnable
+    }
+
     this.updateComponent(_card)
   }
 
diff --git a/src/menu/components/form/simple-form/options.jsx b/src/menu/components/form/simple-form/options.jsx
index 4e25a6c..e0936b9 100644
--- a/src/menu/components/form/simple-form/options.jsx
+++ b/src/menu/components/form/simple-form/options.jsx
@@ -1,4 +1,3 @@
-import { fromJS } from 'immutable'
 import MenuUtils from '@/utils/utils-custom.js'
 
 /**
@@ -36,8 +35,15 @@
   }
 
   let modules = []
-  let menu = fromJS(window.GLOB.customMenu).toJS()
-  modules = MenuUtils.getSupModules(menu.components, config.uuid) || []
+  let menu = window.GLOB.customMenu
+  modules = MenuUtils.getSupModules(menu.components, config.uuid, menu.interfaces)
+
+  if (wrap.supModule && wrap.supModule.length > 0 && wrap.supModule[0] !== 'empty') {
+    let has = MenuUtils.checkSupModules(modules, wrap.supModule.slice(-1)[0])
+    if (!has) {
+      wrap.supModule = ''
+    }
+  }
 
   const wrapForm = [
     {
@@ -129,7 +135,7 @@
     },
     {
       type: 'radio',
-      field: 'subEnable',
+      field: 'enable',
       label: '鎻愪氦',
       initval: wrap.enable || 'true',
       required: false,
@@ -140,6 +146,31 @@
     },
     {
       type: 'radio',
+      field: 'closeEnable',
+      label: '鍏抽棴',
+      initval: wrap.closeEnable || 'false',
+      tooltip: '绠$悊绯荤粺涓細鍏抽棴褰撳墠鏍囩锛屽瓙搴旂敤涓负杩斿洖涓婁竴椤点��',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'goback',
+      label: '绌哄�艰繑鍥�',
+      initval: wrap.goback || 'false',
+      tooltip: '褰撴煡璇㈡暟鎹负绌烘椂锛岃繑鍥炰笂涓�鐣岄潰銆�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+      forbid: appType !== 'mob'
+    },
+    {
+      type: 'radio',
       field: 'permission',
       label: '鏉冮檺楠岃瘉',
       initval: wrap.permission || 'false',
diff --git a/src/menu/components/form/step-form/index.jsx b/src/menu/components/form/step-form/index.jsx
index a28b55f..fec8336 100644
--- a/src/menu/components/form/step-form/index.jsx
+++ b/src/menu/components/form/step-form/index.jsx
@@ -201,7 +201,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -530,7 +530,7 @@
       standardform,
       visible: true,
       editform: _form,
-      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields)
+      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields, card.columns)
     })
   }
 
@@ -582,6 +582,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/menu/components/form/step-form/options.jsx b/src/menu/components/form/step-form/options.jsx
index fa3eb97..4011d4e 100644
--- a/src/menu/components/form/step-form/options.jsx
+++ b/src/menu/components/form/step-form/options.jsx
@@ -1,4 +1,3 @@
-import { fromJS } from 'immutable'
 import MenuUtils from '@/utils/utils-custom.js'
 
 /**
@@ -20,8 +19,15 @@
   }
 
   let modules = []
-  let menu = fromJS(window.GLOB.customMenu).toJS()
-  modules = MenuUtils.getSupModules(menu.components, config.uuid) || []
+  let menu = window.GLOB.customMenu
+  modules = MenuUtils.getSupModules(menu.components, config.uuid, menu.interfaces)
+
+  if (wrap.supModule && wrap.supModule.length > 0 && wrap.supModule[0] !== 'empty') {
+    let has = MenuUtils.checkSupModules(modules, wrap.supModule.slice(-1)[0])
+    if (!has) {
+      wrap.supModule = ''
+    }
+  }
 
   const wrapForm = [
     {
@@ -103,6 +109,19 @@
     },
     {
       type: 'radio',
+      field: 'goback',
+      label: '绌哄�艰繑鍥�',
+      initval: wrap.goback || 'false',
+      tooltip: '褰撴煡璇㈡暟鎹负绌烘椂锛岃繑鍥炰笂涓�鐣岄潰銆�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+      forbid: appType !== 'mob'
+    },
+    {
+      type: 'radio',
       field: 'permission',
       label: '鏉冮檺楠岃瘉',
       initval: wrap.permission || 'false',
diff --git a/src/menu/components/form/tab-form/index.jsx b/src/menu/components/form/tab-form/index.jsx
index b5ccead..a572893 100644
--- a/src/menu/components/form/tab-form/index.jsx
+++ b/src/menu/components/form/tab-form/index.jsx
@@ -213,7 +213,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -536,7 +536,7 @@
       standardform,
       visible: true,
       editform: _form,
-      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields)
+      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields, card.columns)
     })
   }
 
@@ -588,6 +588,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/menu/components/group/groupcomponents/card.jsx b/src/menu/components/group/groupcomponents/card.jsx
index c01643a..f1c77da 100644
--- a/src/menu/components/group/groupcomponents/card.jsx
+++ b/src/menu/components/group/groupcomponents/card.jsx
@@ -58,6 +58,10 @@
     style = { opacity: 0.3}
   }
 
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
+  }
+
   const getCardComponent = () => {
     if (card.type === 'bar' || card.type === 'line') {
       return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
@@ -79,7 +83,7 @@
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
       return (<PropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
diff --git a/src/menu/components/group/groupcomponents/index.jsx b/src/menu/components/group/groupcomponents/index.jsx
index b126069..49b9341 100644
--- a/src/menu/components/group/groupcomponents/index.jsx
+++ b/src/menu/components/group/groupcomponents/index.jsx
@@ -5,8 +5,6 @@
 import { Empty, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -39,16 +37,10 @@
   const deleteCard = (id) => {
     const { card } = findCard(id)
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
       onOk() {
         handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
-
-        if (uuids.length === 0) return
-
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
diff --git a/src/menu/components/group/normal-group/index.jsx b/src/menu/components/group/normal-group/index.jsx
index 3d5ec2f..0544f9d 100644
--- a/src/menu/components/group/normal-group/index.jsx
+++ b/src/menu/components/group/normal-group/index.jsx
@@ -13,6 +13,7 @@
 
 const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const PasteComponent = asyncIconComponent(() => import('../paste'))
 const GroupComponents = asyncComponent(() => import('../groupcomponents'))
 
@@ -69,7 +70,7 @@
   changeStyle = () => {
     const { group } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], group.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], group.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -119,7 +120,13 @@
   }
 
   updateWrap = (res) => {
-    this.updateComponent({...this.state.group, setting: res})
+    let group = {...this.state.group, setting: res}
+
+    if (res.title && !group.headerStyle) {
+      group.headerStyle = { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' }
+    }
+
+    this.updateComponent(group)
   }
 
   render() {
@@ -133,6 +140,7 @@
 
     return (
       <div className={'menu-group-edit-box ' + (paddingTop ? 'padding ' : '') + (group.setting.layout || '')} style={_style} id={group.uuid}>
+        <NormalHeader hideSearch="true" config={group} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <NormalForm title="鍒嗙粍璁剧疆" width={700} update={this.updateWrap} getForms={this.getWrapForms}>
diff --git a/src/menu/components/group/normal-group/options.jsx b/src/menu/components/group/normal-group/options.jsx
index 9c6f2e6..168bc3b 100644
--- a/src/menu/components/group/normal-group/options.jsx
+++ b/src/menu/components/group/normal-group/options.jsx
@@ -18,6 +18,13 @@
   const settingForm = [
     {
       type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: setting.title || '',
+      required: false
+    },
+    {
+      type: 'text',
       field: 'name',
       label: '缁勪欢鍚嶇О',
       initval: setting.name || '',
diff --git a/src/menu/components/group/paste/index.jsx b/src/menu/components/group/paste/index.jsx
index 2932f39..df04404 100644
--- a/src/menu/components/group/paste/index.jsx
+++ b/src/menu/components/group/paste/index.jsx
@@ -4,7 +4,6 @@
 import { SnippetsOutlined } from '@ant-design/icons'
 
 import MenuUtils from '@/utils/utils-custom.js'
-import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 // import './index.scss'
 
@@ -24,7 +23,8 @@
   }
 
   pasteSubmit = () => {
-    let options = ['datacard', 'propcard', 'balcony', 'timeline', 'simpleform', 'stepform', 'tabform', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter', 'chart']
+    let appType = sessionStorage.getItem('appType')
+    let options = ['datacard', 'propcard', 'balcony', 'timeline', 'simpleform', 'stepform', 'tabform', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter', 'chart', 'sandbox']
     let types = {
       login: '鐧诲綍',
       navbar: '瀵艰埅鏍�',
@@ -34,6 +34,10 @@
       mainsearch: '鎼滅储',
       group: '鍒嗙粍',
       menubar: '鑿滃崟'
+    }
+
+    if (appType !== 'mob') {
+      options.push('editable')
     }
 
     this.pasteFormRef.handleConfirm().then(res => {
@@ -53,19 +57,11 @@
         return
       }
 
-      let copyBtns = new Map()
-
-      res = MenuUtils.resetComponentConfig(res, copyBtns)
+      res = MenuUtils.resetComponentConfig(res)
 
       delete res.copyType
       
       this.props.insert(res)
-
-      copyBtns = [...copyBtns.values()]
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       this.setState({visible: false})
 
diff --git a/src/menu/components/iframe/index.jsx b/src/menu/components/iframe/index.jsx
new file mode 100644
index 0000000..53645e2
--- /dev/null
+++ b/src/menu/components/iframe/index.jsx
@@ -0,0 +1,193 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Popover, Input } from 'antd'
+import { EditOutlined, ToolOutlined, DeleteOutlined, FontColorsOutlined, SettingOutlined } from '@ant-design/icons'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { resetStyle, getTables } from '@/utils/utils-custom.js'
+import MKEmitter from '@/utils/events.js'
+import getWrapForm from './options'
+import './index.scss'
+
+const { Search } = Input
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
+
+class NormalIframe extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    card: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        wrap: { title: '', name: card.name, datatype: 'static', width: card.width || 24, height: '300px', linkType: 'fixed' },
+        style: { marginLeft: '0px', marginRight: '0px', marginTop: '0px', marginBottom: '0px' },
+        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
+        columns: [],
+        scripts: [],
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+
+        _card.setting = config.setting
+        _card.columns = config.columns
+        _card.scripts = config.scripts
+      }
+
+      this.updateComponent(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (card) => {
+    card.width = card.wrap.width
+    card.name = card.wrap.name
+    if (!window.GLOB.styling || !card.errors) { // 鏍峰紡淇敼鏃朵笉鍋氱瓫鏌�
+      card.errors = []
+
+      if (card.wrap.datatype === 'dynamic') {
+        let columns = card.columns.map(c => c.field)
+    
+        if (card.setting.interType === 'system' && card.setting.execute !== 'false' && !card.setting.dataresource) {
+          card.errors.push({ level: 0, detail: '鏈缃暟鎹簮锛�'})
+        } else if (card.setting.interType === 'system' && card.setting.execute === 'false' && card.scripts.filter(script => script.status !== 'false').length === 0) {
+          card.errors.push({ level: 0, detail: '鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+        } else if (!card.setting.primaryKey) {
+          card.errors.push({ level: 0, detail: '鏈缃富閿紒'})
+        } else if (!columns.includes(card.setting.primaryKey)) {
+          card.errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
+        } else if (!card.setting.supModule) {
+          card.errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+        }
+      }
+
+      if (card.errors.length === 0) {
+        card.$tables = getTables(card)
+      }
+    }
+    
+    this.setState({
+      card: card
+    })
+
+    this.props.updateConfig(card)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', ['border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
+  }
+
+  getStyle = (style) => {
+    let _card = {...this.state.card, style}
+    
+    this.updateComponent(_card)
+  }
+
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.columns)
+  }
+
+  updateWrap = (res) => {
+    let _card = {...this.state.card, wrap: res}
+    
+    this.updateComponent(_card)
+  }
+
+  render() {
+    const { card } = this.state
+
+    let _style = resetStyle(card.style)
+
+    return (
+      <div className="menu-iframe-edit-box" style={_style} id={card.uuid}>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <NormalForm title="iframe璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
+            <CopyComponent type="iframe" card={card}/>
+            <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
+            <DeleteOutlined className="close" title="鍒犻櫎缁勪欢" onClick={() => this.props.deletecomponent(card.uuid)} />
+            {card.wrap.datatype === 'dynamic' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : <SettingOutlined style={{color: '#eeeeee', cursor: 'not-allowed'}}/>}
+          </div>
+        } trigger="hover">
+          <ToolOutlined />
+        </Popover>
+        <NormalHeader hideSearch="true" config={card} updateComponent={this.updateComponent}/>
+        {card.wrap.linkType === 'input' ? <div className="input-box">
+          <Search placeholder="璇疯緭鍏ュ湴鍧�" enterButton="纭畾"/>
+        </div> : null}
+        <div className="iframe-wrap" style={{height: card.wrap.height}}>
+          <iframe title="mk" className="iframe" src={card.wrap.linkUrl || 'http://www.minkesoft.com/index.html'} frameBorder="0"></iframe>
+        </div>
+        <div className="component-name">
+          <div className="center">
+            <div className="title">{card.name}</div>
+            <div className="content">
+              {card.errors && card.errors.map((err, index) => {
+                if (err.level === 0) {
+                  return <span key={index} className="error">{err.detail}</span>
+                } else {
+                  return <span key={index} className="waring">{err.detail}锛�</span>
+                }
+              })}
+            </div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default NormalIframe
\ No newline at end of file
diff --git a/src/menu/components/iframe/index.scss b/src/menu/components/iframe/index.scss
new file mode 100644
index 0000000..f1a9bf5
--- /dev/null
+++ b/src/menu/components/iframe/index.scss
@@ -0,0 +1,53 @@
+.menu-iframe-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 50px;
+
+  .anticon-tool {
+    position: absolute;
+    z-index: 2;
+    font-size: 16px;
+    right: 1px;
+    top: 1px;
+    cursor: pointer;
+    padding: 5px;
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .input-box {
+    position: absolute;
+    top: 0px;
+    width: 100%;
+    height: 45px;
+    padding-top: 5px;
+    .ant-input-group-wrapper {
+      width: 40%;
+      max-width: 400px;
+      float: right;
+    }
+  }
+  .normal-header.hidden + .input-box {
+    position: relative;
+  }
+
+  .iframe-wrap {
+    .iframe {
+      width: 100%;
+      height: 100%;
+      vertical-align: top;
+    }
+  }
+}
+.menu-iframe-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-iframe-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/iframe/options.jsx b/src/menu/components/iframe/options.jsx
new file mode 100644
index 0000000..73bee16
--- /dev/null
+++ b/src/menu/components/iframe/options.jsx
@@ -0,0 +1,124 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap, columns) {
+  let roleList = sessionStorage.getItem('sysRoles')
+  let appType = sessionStorage.getItem('appType')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch (e) {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const balconyWrapForm = [
+    {
+      type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: wrap.title || '',
+      required: false
+    },
+    {
+      type: 'text',
+      field: 'name',
+      label: '缁勪欢鍚嶇О',
+      initval: wrap.name || '',
+      tooltip: '鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'width',
+      label: '瀹藉害',
+      initval: wrap.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'radio',
+      field: 'datatype',
+      label: '鏁版嵁鏉ユ簮',
+      initval: wrap.datatype || 'static',
+      tooltip: '閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��',
+      required: false,
+      options: [
+        {value: 'dynamic', label: '鍔ㄦ��'},
+        {value: 'static', label: '闈欐��'},
+      ],
+      controlFields: [
+        {field: 'linkField', values: ['dynamic']},
+        {field: 'linkType', values: ['static']},
+      ]
+    },
+    {
+      type: 'styleInput',
+      field: 'height',
+      label: '楂樺害',
+      initval: wrap.height || '',
+      tooltip: 'ifram楂樺害銆�',
+      required: true,
+      options: ['px', 'vh', 'vw']
+    },
+    {
+      type: 'radio',
+      field: 'linkType',
+      label: '閾炬帴绫诲瀷',
+      initval: wrap.linkType || 'fixed',
+      required: false,
+      options: [
+        {value: 'fixed', label: '鍥哄畾閾炬帴'},
+        {value: 'input', label: '杈撳叆閾炬帴'},
+      ],
+      controlFields: [
+        {field: 'linkUrl', values: ['fixed']},
+      ]
+    },
+    {
+      type: 'textarea',
+      field: 'linkUrl',
+      label: '鍦板潃閾炬帴',
+      initval: wrap.linkUrl || '',
+      required: true,
+      span: 24
+    },
+    {
+      type: 'select',
+      field: 'linkField',
+      label: '鍦板潃閾炬帴',
+      initval: wrap.linkField || '',
+      required: columns.length > 0,
+      options: columns
+    },
+    {
+      type: 'radio',
+      field: 'permission',
+      label: '鏉冮檺楠岃瘉',
+      initval: wrap.permission || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鍚敤'},
+        {value: 'false', label: '绂佺敤'},
+      ],
+      forbid: !appType
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return balconyWrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/module/voucher/index.jsx b/src/menu/components/module/voucher/index.jsx
index 329d64d..5f13cd1 100644
--- a/src/menu/components/module/voucher/index.jsx
+++ b/src/menu/components/module/voucher/index.jsx
@@ -85,7 +85,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/menu/components/module/voucher/options.jsx b/src/menu/components/module/voucher/options.jsx
index df667c8..a837e93 100644
--- a/src/menu/components/module/voucher/options.jsx
+++ b/src/menu/components/module/voucher/options.jsx
@@ -1,4 +1,3 @@
-import { fromJS } from 'immutable'
 import MenuUtils from '@/utils/utils-custom.js'
 
 /**
@@ -8,8 +7,8 @@
   let roleList = sessionStorage.getItem('sysRoles')
   let appType = sessionStorage.getItem('appType')
 
-  let menu = fromJS(window.GLOB.customMenu).toJS()
-  let modules = MenuUtils.getSupModules(menu.components, id) || []
+  let menu = window.GLOB.customMenu
+  let modules = MenuUtils.getSupModules(menu.components, id, menu.interfaces)
 
   if (roleList) {
     try {
diff --git a/src/menu/components/search/main-search/index.jsx b/src/menu/components/search/main-search/index.jsx
index 3595d12..8741554 100644
--- a/src/menu/components/search/main-search/index.jsx
+++ b/src/menu/components/search/main-search/index.jsx
@@ -9,6 +9,7 @@
 import Utils from '@/utils/utils.js'
 import { getSearchForm } from '@/templates/zshare/formconfig'
 import { resetStyle } from '@/utils/utils-custom.js'
+import { getTables } from '@/utils/utils-custom.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import DragElement from './dragsearch'
@@ -95,25 +96,27 @@
       card: _card
     })
     
-    this.props.updateConfig(_card)
+    this.updateComponent(_card)
   }
 
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   /**
    * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
    */
   updateComponent = (component) => {
-    this.setState({
-      card: component
-    })
+    component.$tables = getTables(component)
 
     component.width = component.wrap.width
     component.name = component.wrap.name
+
+    this.setState({
+      card: component
+    })
 
     this.props.updateConfig(component)
   }
@@ -143,9 +146,7 @@
       this.setState({card: _card})
       this.handleSearch(newcell)
     } else {
-      this.setState({card: _card}, ()=> {  
-        this.props.updateConfig(_card)
-      })
+      this.updateComponent(_card)
     }
   }
 
@@ -273,6 +274,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
@@ -284,11 +286,10 @@
         Api.genericInterface(param).then(result => {
           if (result.status) {
             this.setState({
-              card: card,
               sqlVerifing: false,
               visible: false
             }, ()=> {
-              this.props.updateConfig(card)
+              this.updateComponent(card)
             })
           } else {
             this.setState({sqlVerifing: false})
@@ -300,10 +301,9 @@
         })
       } else {
         this.setState({
-          card: card,
           visible: false
         }, ()=> { 
-          this.props.updateConfig(card)
+          this.updateComponent(card)
         })
       }
     })
@@ -321,11 +321,7 @@
         let _card = fromJS(_this.state.card).toJS()
         _card.search = _card.search.filter(item => item.uuid !== cell.uuid)
 
-        _this.setState({
-          card: _card
-        }, () => {
-          _this.props.updateConfig(_card)
-        })
+        _this.updateComponent(_card)
       },
       onCancel() {}
     })
@@ -367,18 +363,12 @@
 
     if (type === 'simple') {
       _card.search.push(item)
-      this.setState({
-        card: _card,
-      }, () => {
-        this.props.updateConfig(_card)
-      })
+
+      this.updateComponent(_card)
     } else if (type === 'multil') {
       _card.search.push(...item)
-      this.setState({
-        card: _card,
-      }, () => {
-        this.props.updateConfig(_card)
-      })
+
+      this.updateComponent(_card)
     }
   }
 
@@ -399,8 +389,7 @@
         let _card = {...this.state.card}
         _card.style = {..._card.style, ...style}
 
-        this.setState({ card: _card })
-        this.props.updateConfig(_card)
+        this.updateComponent(_card)
       })
     }
   }
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index 7ca57d9..b281c2a 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -6,6 +6,7 @@
 import { formRule } from '@/utils/option.js'
 
 import asyncComponent from '@/utils/asyncComponent'
+import KeyInterface from '@/components/keyInterface'
 import './index.scss'
 
 const { TextArea } = Input
@@ -177,7 +178,7 @@
         reReadonly.interface = false
         reRequired.interface = true
       } else if (intertype === 'outer') {
-        shows.push('procMode', 'sysInterface', 'interface', 'outerFunc', 'callbackType', 'output')
+        shows.push('procMode', 'sysInterface', 'outerFunc', 'callbackType', 'output')
         if (this.record.procMode === 'system') {
           shows.push('sql', 'sqlType')
         } else if (this.record.procMode === 'inner') {
@@ -194,10 +195,14 @@
           reReadonly.interface = false
           reRequired.interface = true
 
-          shows.push('proInterface')
-        } else {
+          shows.push('interface', 'proInterface')
+        } else if (this.record.sysInterface === 'true') {
           reReadonly.interface = true
           reRequired.interface = false
+
+          shows.push('interface')
+        } else if (this.record.sysInterface === 'external') {
+          shows.push('exInterface', 'exProInterface')
         }
       } else if (intertype === 'inner') {
         shows.push('innerFunc', 'output')
@@ -244,7 +249,7 @@
       reOptions.intertype = this.state.interTypeOptions.filter(item => item.value !== 'custom')
 
       if (intertype === 'outer') {
-        shows.push('procMode', 'sysInterface', 'interface', 'outerFunc', 'callbackType')
+        shows.push('procMode', 'sysInterface', 'outerFunc', 'callbackType')
         if (this.record.procMode === 'system') {
           shows.push('sql', 'sqlType')
         } else if (this.record.procMode === 'inner') {
@@ -261,10 +266,14 @@
           reReadonly.interface = false
           reRequired.interface = true
 
-          shows.push('proInterface')
-        } else {
+          shows.push('interface', 'proInterface')
+        } else if (this.record.sysInterface === 'true') {
           reReadonly.interface = true
           reRequired.interface = false
+
+          shows.push('interface')
+        } else if (this.record.sysInterface === 'external') {
+          shows.push('exInterface', 'exProInterface')
         }
       } else if (intertype === 'inner') {
         shows.push('innerFunc')
@@ -293,7 +302,7 @@
       reOptions.Ot = requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
 
       if (this.record.intertype === 'outer') {
-        shows.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+        shows.push('innerFunc', 'sysInterface', 'outerFunc', 'callbackFunc')
         reRequired.innerFunc = false
         reRequired.callbackFunc = false
 
@@ -301,10 +310,14 @@
           reReadonly.interface = false
           reRequired.interface = true
 
-          shows.push('proInterface')
-        } else {
+          shows.push('interface', 'proInterface')
+        } else if (this.record.sysInterface === 'true') {
           reReadonly.interface = true
           reRequired.interface = false
+
+          shows.push('interface')
+        } else if (this.record.sysInterface === 'external') {
+          shows.push('exInterface', 'exProInterface')
         }
       } else if (this.record.intertype === 'inner') {
         shows.push('innerFunc')
@@ -317,17 +330,21 @@
       reOptions.intertype = this.state.interTypeOptions.filter(op => op.value !== 'custom')
 
       if (this.record.intertype === 'outer') {
-        shows.push('innerFunc', 'sysInterface', 'interface', 'outerFunc')
+        shows.push('innerFunc', 'sysInterface', 'outerFunc')
         reRequired.innerFunc = false
 
         if (this.record.sysInterface === 'false') {
           reReadonly.interface = false
           reRequired.interface = true
 
-          shows.push('proInterface')
-        } else {
+          shows.push('interface', 'proInterface')
+        } else if (this.record.sysInterface === 'true') {
           reReadonly.interface = true
           reRequired.interface = false
+
+          shows.push('interface')
+        } else if (this.record.sysInterface === 'external') {
+          shows.push('exInterface', 'exProInterface')
         }
       } else if (this.record.intertype === 'inner') {
         shows.push('innerFunc')
@@ -379,18 +396,22 @@
       if (_funcType === 'print') {
         shows.push('execMode', 'intertype', 'Ot', 'execSuccess', 'execError')
         if (this.record.intertype === 'outer') {
-          shows.push('innerFunc', 'sysInterface', 'interface', 'outerFunc', 'callbackFunc')
+          shows.push('innerFunc', 'sysInterface', 'outerFunc', 'callbackFunc')
           reRequired.innerFunc = false
           reRequired.callbackFunc = false
 
           if (this.record.sysInterface === 'false') {
             reReadonly.interface = false
             reRequired.interface = true
-
-            shows.push('proInterface')
-          } else {
+  
+            shows.push('interface', 'proInterface')
+          } else if (this.record.sysInterface === 'true') {
             reReadonly.interface = true
             reRequired.interface = false
+  
+            shows.push('interface')
+          } else if (this.record.sysInterface === 'external') {
+            shows.push('exInterface', 'exProInterface')
           }
         } else if (this.record.intertype === 'inner') {
           shows.push('innerFunc')
@@ -843,6 +864,14 @@
         ]
 
         content = <TextArea rows={2} readOnly={item.readonly}/>
+      } else if (item.type === 'keyinterface') {
+        span = 24
+        className = 'textarea'
+        rules = [
+          { required: item.required, message: '璇疯緭鍏�' + item.label + '!' }
+        ]
+
+        content = <KeyInterface type={item.key === 'exInterface' ? 'develop' : 'product'}/>
       }
 
       fields.push(
@@ -884,6 +913,7 @@
           values.uuid = card.uuid
           values.verify = card.verify || null
           values.modal = card.modal || null
+          values.config = card.config || null
 
           if (values.OpenType === 'form') {
             if (values.formType !== 'scan') {
diff --git a/src/menu/components/share/actioncomponent/dragaction/card.jsx b/src/menu/components/share/actioncomponent/dragaction/card.jsx
index 78a372c..0059c8d 100644
--- a/src/menu/components/share/actioncomponent/dragaction/card.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -1,7 +1,7 @@
 import React from 'react'
 import { useDrag, useDrop } from 'react-dnd'
 import { Button, Popover } from 'antd'
-import { CopyOutlined, EditOutlined, FontColorsOutlined, CloseOutlined, ProfileOutlined, WarningOutlined } from '@ant-design/icons'
+import { CopyOutlined, EditOutlined, FontColorsOutlined, CloseOutlined, ProfileOutlined, WarningOutlined, DisconnectOutlined } from '@ant-design/icons'
 
 import MkIcon from '@/components/mk-icon'
 import { resetStyle } from '@/utils/utils-custom.js'
@@ -31,10 +31,17 @@
   const opacity = isDragging ? 0.5 : 1
 
   let hasProfile = false
+  let forbidSql = false
   if (['pop', 'prompt', 'exec'].includes(card.OpenType)) {
     hasProfile = true
+    if (card.verify && card.verify.default === 'false') {
+      forbidSql = true
+    }
   } else if (card.OpenType === 'excelIn' || card.OpenType === 'excelOut') {
     hasProfile = true
+    if (card.verify && card.verify.default === 'false') {
+      forbidSql = true
+    }
   } else if (card.funcType === 'print') {
     hasProfile = true
   } else if (card.funcType === 'megvii') {
@@ -56,6 +63,8 @@
   let warning = null
   if (card.OpenType === 'innerpage' && !card.pageTemplate) {
     warning = <WarningOutlined style={{color: 'orange', marginLeft: '5px'}}/>
+  } else if (forbidSql) {
+    warning = <DisconnectOutlined className="mk-disconnect"/>
   }
 
   if (show === 'icon') {
diff --git a/src/menu/components/share/actioncomponent/dragaction/index.jsx b/src/menu/components/share/actioncomponent/dragaction/index.jsx
index b4f4a65..b59ac72 100644
--- a/src/menu/components/share/actioncomponent/dragaction/index.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/index.jsx
@@ -122,7 +122,7 @@
 
         if (newcard.OpenType === 'excelIn') {
           newcard.label = item.text
-          newcard.class = 'dgreen'
+          newcard.class = item.class || 'dgreen'
           newcard.Ot = 'notRequired'
         } else if (newcard.OpenType === 'excelOut') {
           newcard.label = item.text
diff --git a/src/menu/components/share/actioncomponent/formconfig.jsx b/src/menu/components/share/actioncomponent/formconfig.jsx
index 22eae11..196f05c 100644
--- a/src/menu/components/share/actioncomponent/formconfig.jsx
+++ b/src/menu/components/share/actioncomponent/formconfig.jsx
@@ -126,7 +126,7 @@
     }
   }
   if (appType === 'mob') {
-    opentypes = opentypes.filter(item => ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton'].includes(item.value))
+    opentypes = opentypes.filter(item => ['pop', 'prompt', 'exec', 'excelOut', 'innerpage', 'funcbutton'].includes(item.value))
     funTypes = [
       { value: 'scan', text: '鎵爜' },
       { value: 'pay', text: '鏀粯' },
@@ -201,7 +201,7 @@
   }
 
   let closetab = []
-  if (!appType) {
+  if (!appType && viewType !== 'popview') {
     closetab = [{
       value: 'closetab',
       text: '鍏抽棴鏍囩'
@@ -391,15 +391,19 @@
     {
       type: 'radio',
       key: 'sysInterface',
-      label: '绯荤粺鎺ュ彛',
+      label: '绯荤粺绫诲瀷',
       initVal: card.sysInterface || 'false',
+      tooltip: '涓氬姟绯荤粺鎸囧悓涓�sso涓嬬殑鍏朵粬涓氬姟绯荤粺锛屽閮ㄧ郴缁熸寚涓嶅悓sso涓嬬殑鍏朵粬涓氬姟绯荤粺銆�',
       required: true,
       options: [{
-        value: 'true',
-        text: '鏄�'
-      }, {
         value: 'false',
-        text: '鍚�'
+        text: '涓氬姟'
+      }, {
+        value: 'true',
+        text: '鍗曠偣'
+      }, {
+        value: 'external',
+        text: '澶栭儴'
       }]
     },
     {
@@ -422,6 +426,21 @@
       key: 'proInterface',
       label: '姝e紡鍦板潃',
       initVal: card.proInterface || '',
+      tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
+      required: false
+    },
+    {
+      type: 'keyinterface',
+      key: 'exInterface',
+      label: '娴嬭瘯鍦板潃',
+      initVal: card.exInterface || '',
+      required: true
+    },
+    {
+      type: 'keyinterface',
+      key: 'exProInterface',
+      label: '姝e紡鍦板潃',
+      initVal: card.exProInterface || '',
       tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
       required: false
     },
@@ -686,10 +705,20 @@
       required: true
     },
     {
+      type: 'cascader',
+      key: 'syncComponent',
+      label: '鍒锋柊缁勪欢',
+      initVal: card.syncComponent || [],
+      tooltip: '鎵ц鎴愬姛鍚庯紙鎴栧脊绐楁爣绛惧叧闂椂锛夛紝闇�瑕佸悓姝ュ埛鏂扮殑缁勪欢銆傛敞锛氶�夋嫨褰撳墠缁勪欢鐨勪笂绾х粍浠舵棤鏁堬紝鍒锋柊涓婄骇缁勪欢璇烽�夋嫨鎴愬姛鍚庘�滃埛鏂颁笂绾х粍浠� - 琛屸�濄��',
+      required: false,
+      options: modules
+    },
+    {
       type: 'radio',
       key: 'pagination',
       label: '鍒嗛〉',
       initVal: card.pagination || 'false',
+      tooltip: '瀵煎嚭鏁版嵁閲忚緝澶ф椂锛�1000+锛夛紝鍙缃垎椤佃姹傛暟鎹��',
       required: false,
       options: [{
         value: 'true',
@@ -697,7 +726,8 @@
       }, {
         value: 'false',
         text: '鍚�'
-      }]
+      }],
+      forbid: appType === 'mob'
     },
     {
       type: 'radio',
@@ -711,16 +741,8 @@
       }, {
         value: 'false',
         text: '闈炲繀濉�'
-      }]
-    },
-    {
-      type: 'cascader',
-      key: 'syncComponent',
-      label: '鍒锋柊缁勪欢',
-      initVal: card.syncComponent || [],
-      tooltip: '鎵ц鎴愬姛鍚庯紙鎴栧脊绐楁爣绛惧叧闂椂锛夛紝闇�瑕佸悓姝ュ埛鏂扮殑缁勪欢銆傛敞锛氶�夋嫨褰撳墠缁勪欢鐨勪笂绾х粍浠舵棤鏁堬紝鍒锋柊涓婄骇缁勪欢璇烽�夋嫨鎴愬姛鍚庘�滃埛鏂颁笂绾х粍浠� - 琛屸�濄��',
-      required: false,
-      options: modules
+      }],
+      forbid: appType === 'mob'
     },
     {
       type: 'cascader',
@@ -1029,13 +1051,12 @@
  * @param {*} setting        缁勪欢閰嶇疆
  * @param {*} usefulFields   瀛樺偍杩囩▼鍙敤鐨勫紑濮嬪瓧娈�
  */
-export function getBaseTableActionForm (card, functip, config, usefulFields) {
+export function getBaseTableActionForm (card, functip, config, usefulFields, modules) {
   let viewType = sessionStorage.getItem('editMenuType') // 寮圭獥 popview
   let printTemps = sessionStorage.getItem('printTemps')
   printTemps = printTemps ? JSON.parse(printTemps) : []
   let setting = config.setting || {}
   let columns = config.columns || []
-  let modules = []
 
   let opentypes = [
     {
@@ -1069,6 +1090,8 @@
   ]
 
   let pageTemps = [
+    // { value: 'print', text: '鏍囩鎵撳嵃妯℃澘' },
+    // { value: 'billprintTemp', text: '鍗曟嵁鎵撳嵃妯℃澘' },
     { value: 'billprint', text: '鍗曟嵁鎵撳嵃' },
     { value: 'pay', text: '鏀粯' },
     { value: 'custom', text: '鑷畾涔�' }
@@ -1265,15 +1288,19 @@
     {
       type: 'radio',
       key: 'sysInterface',
-      label: '绯荤粺鎺ュ彛',
+      label: '绯荤粺绫诲瀷',
       initVal: card.sysInterface || 'false',
+      tooltip: '涓氬姟绯荤粺鎸囧悓涓�sso涓嬬殑鍏朵粬涓氬姟绯荤粺锛屽閮ㄧ郴缁熸寚涓嶅悓sso涓嬬殑鍏朵粬涓氬姟绯荤粺銆�',
       required: true,
       options: [{
-        value: 'true',
-        text: '鏄�'
-      }, {
         value: 'false',
-        text: '鍚�'
+        text: '涓氬姟'
+      }, {
+        value: 'true',
+        text: '鍗曠偣'
+      }, {
+        value: 'external',
+        text: '澶栭儴'
       }]
     },
     {
@@ -1296,6 +1323,21 @@
       key: 'proInterface',
       label: '姝e紡鍦板潃',
       initVal: card.proInterface || '',
+      tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
+      required: false
+    },
+    {
+      type: 'keyinterface',
+      key: 'exInterface',
+      label: '娴嬭瘯鍦板潃',
+      initVal: card.exInterface || '',
+      required: true
+    },
+    {
+      type: 'keyinterface',
+      key: 'exProInterface',
+      label: '姝e紡鍦板潃',
+      initVal: card.exProInterface || '',
       tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
       required: false
     },
@@ -1447,6 +1489,35 @@
       }]
     },
     {
+      type: 'number',
+      key: 'width',
+      min: 1,
+      max: 24,
+      precision: 0,
+      label: '瀹藉害',
+      initVal: card.width || 12,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      forbid: card.eleType !== 'button',
+      required: true
+    },
+    {
+      type: 'radio',
+      key: 'show',
+      label: '鏄剧ず涓�',
+      initVal: card.show || 'button',
+      required: true,
+      options: [{
+        value: 'icon',
+        text: '鍥炬爣'
+      }, {
+        value: 'button',
+        text: '鍥炬爣+鏂囧瓧'
+      }, {
+        value: 'link',
+        text: '鏂囧瓧+鍥炬爣'
+      }]
+    },
+    {
       type: 'icon',
       key: 'icon',
       label: '鍥炬爣',
@@ -1484,10 +1555,20 @@
       required: true
     },
     {
+      type: 'cascader',
+      key: 'syncComponent',
+      label: '鍒锋柊缁勪欢',
+      initVal: card.syncComponent || [],
+      tooltip: '鎵ц鎴愬姛鍚庯紙鎴栧脊绐楁爣绛惧叧闂椂锛夛紝闇�瑕佸悓姝ュ埛鏂扮殑缁勪欢銆傛敞锛氶�夋嫨褰撳墠缁勪欢鐨勪笂绾х粍浠舵棤鏁堬紝鍒锋柊涓婄骇缁勪欢璇烽�夋嫨鎴愬姛鍚庘�滃埛鏂颁笂绾х粍浠� - 琛屸�濄��',
+      required: false,
+      options: modules
+    },
+    {
       type: 'radio',
       key: 'pagination',
       label: '鍒嗛〉',
       initVal: card.pagination || 'false',
+      tooltip: '瀵煎嚭鏁版嵁閲忚緝澶ф椂锛�1000+锛夛紝鍙缃垎椤佃姹傛暟鎹��',
       required: false,
       options: [{
         value: 'true',
@@ -1510,15 +1591,6 @@
         value: 'false',
         text: '闈炲繀濉�'
       }]
-    },
-    {
-      type: 'cascader',
-      key: 'syncComponent',
-      label: '鍒锋柊缁勪欢',
-      initVal: card.syncComponent || [],
-      tooltip: '鎵ц鎴愬姛鍚庯紙鎴栧脊绐楁爣绛惧叧闂椂锛夛紝闇�瑕佸悓姝ュ埛鏂扮殑缁勪欢銆傛敞锛氶�夋嫨褰撳墠缁勪欢鐨勪笂绾х粍浠舵棤鏁堬紝鍒锋柊涓婄骇缁勪欢璇烽�夋嫨鎴愬姛鍚庘�滃埛鏂颁笂绾х粍浠� - 琛屸�濄��',
-      required: false,
-      options: modules
     },
     {
       type: 'cascader',
diff --git a/src/menu/components/share/actioncomponent/index.jsx b/src/menu/components/share/actioncomponent/index.jsx
index 4a6ebd9..1234964 100644
--- a/src/menu/components/share/actioncomponent/index.jsx
+++ b/src/menu/components/share/actioncomponent/index.jsx
@@ -60,17 +60,9 @@
   }
 
   componentDidMount () {
+    MKEmitter.addListener('revert', this.revert)
     MKEmitter.addListener('addButton', this.addButton)
-  }
-
-  /**
-   * @description 鐩戝惉鍒版寜閽鍒舵椂锛岃Е鍙戞寜閽紪杈�
-   */
-  UNSAFE_componentWillReceiveProps (nextProps) {
-    const { actionlist } = this.state
-    if (!is(fromJS(nextProps.config.action), fromJS(this.props.config.action)) && !is(fromJS(nextProps.config.action), fromJS(actionlist))) {
-      this.setState({actionlist: fromJS(nextProps.config.action).toJS()})
-    }
+    MKEmitter.addListener('submitModal', this.handleSave)
   }
 
   shouldComponentUpdate (nextProps, nextState) {
@@ -84,7 +76,43 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('revert', this.revert)
     MKEmitter.removeListener('addButton', this.addButton)
+    MKEmitter.removeListener('submitModal', this.handleSave)
+  }
+
+  revert = (id) => {
+    if (id && id !== this.props.config.uuid) return
+
+    this.setState({
+      actionlist: fromJS(this.props.config.action).toJS()
+    })
+  }
+
+  handleSave = (componentId, btnId, modal) => {
+    const { config } = this.props
+
+    if (config.uuid !== componentId) return
+
+    const { actionlist } = this.state
+
+    let _index = actionlist.findIndex(cell => cell.uuid === btnId)
+
+    if (_index === -1) return
+
+    let _actionlist = actionlist.map(cell => {
+      if (cell.uuid === btnId) {
+        cell.modal = modal
+      }
+
+      return cell
+    })
+
+    this.setState({
+      actionlist: _actionlist
+    }, () => {
+      this.props.updateaction({...config, action: _actionlist})
+    })
   }
 
   getStyle = (style) => {
@@ -169,15 +197,6 @@
       functip = ''
     }
 
-    if (config.subtype === 'basetable') {
-      this.setState({
-        visible: true,
-        card: card,
-        formlist: getBaseTableActionForm(card, functip, config, usefulFields)
-      })
-      return
-    }
-
     let supId = ''
     if (config.setting && config.setting.supModule) {
       let pid = config.setting.supModule[config.setting.supModule.length - 1]
@@ -188,14 +207,25 @@
       }
     }
     
-    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid, supId) || []
-    let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, config.uuid) || []
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid, supId)
 
-    this.setState({
-      visible: true,
-      card: card,
-      formlist: getActionForm(card, functip, config, usefulFields, modules, anchors)
-    })
+    if (config.subtype === 'basetable') {
+      delete card.eleType // 鍖哄垎鎸夐挳浣嶇疆
+
+      this.setState({
+        visible: true,
+        card: card,
+        formlist: getBaseTableActionForm(card, functip, config, usefulFields, modules)
+      })
+    } else {
+      let anchors = MenuUtils.getAnchors(window.GLOB.customMenu.components, config.uuid) || []
+  
+      this.setState({
+        visible: true,
+        card: card,
+        formlist: getActionForm(card, functip, config, usefulFields, modules, anchors)
+      })
+    }
   }
 
   /**
@@ -227,7 +257,7 @@
    */
   handleSubmit = () => {
     const { config } = this.props
-    let color = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666', default: '#333333' }
+    let color = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666', default: 'rgba(0, 0, 0, 0.65)' }
     let _actionlist = fromJS(this.state.actionlist).toJS()
     
     this.actionFormRef.handleConfirm().then(btn => {
@@ -242,10 +272,12 @@
         if (item.uuid === btn.uuid) {
           if (config.subtype === 'basetable') {
             let _c = btn.class.replace('border-', '')
-            if (btn.class.indexOf('border') > -1 || btn.class === 'default') {
-              btn.style = {color: color[_c], backgroundColor: '#fff', borderColor: color[_c]}
+            if (btn.class === 'default') {
+              btn.style = {color: 'rgba(0, 0, 0, 0.65)', backgroundColor: '#fff', borderColor: '#d9d9d9', marginRight: '15px'}
+            } else if (btn.class.indexOf('border') > -1) {
+              btn.style = {color: color[_c], backgroundColor: '#fff', borderColor: color[_c], marginRight: '15px'}
             } else {
-              btn.style = {color: '#fff', backgroundColor: color[_c], borderColor: color[_c]}
+              btn.style = {color: '#fff', backgroundColor: color[_c], borderColor: color[_c], marginRight: '15px'}
             }
           } else {
             btn.style = item.style || {}
@@ -288,7 +320,6 @@
    */
   deleteElement = (card) => {
     const { config } = this.props
-    const { appType } = this.state
     let _this = this
 
     confirm({
@@ -303,11 +334,6 @@
         }, () => {
           _this.props.updateaction({...config, action: _actionlist})
         })
-
-        if (card.origin || appType === 'mob') return
-        if (appType === 'pc' && card.OpenType !== 'popview') return
-
-        MKEmitter.emit('delButtons', [card.uuid])
       },
       onCancel() {}
     })
@@ -321,7 +347,6 @@
       profVisible: true,
       card: element
     })
-    MKEmitter.emit('modalStatus', '楠岃瘉淇℃伅')
   }
 
   /**
@@ -349,7 +374,6 @@
       }, () => {
         this.props.updateaction({...config, action: _actionlist})
       })
-      MKEmitter.emit('modalStatus', false)
     })
   }
 
@@ -435,7 +459,7 @@
 
     let btn = null
     let _col = null
-    if (config.subtype === 'normaltable' || config.subtype === 'editable') {
+    if (config.type === 'table') {
       config.cols.forEach(col => {
         if (col.type !== 'action') return
 
@@ -553,11 +577,9 @@
             if (this.verifyRef.handleCancel) {
               this.verifyRef.handleCancel().then(() => {
                 this.setState({ profVisible: false })
-                MKEmitter.emit('modalStatus', false)
               })
             } else {
               this.setState({ profVisible: false })
-              MKEmitter.emit('modalStatus', false)
             }
           }}
           destroyOnClose
diff --git a/src/menu/components/share/actioncomponent/index.scss b/src/menu/components/share/actioncomponent/index.scss
index 4224780..63c17eb 100644
--- a/src/menu/components/share/actioncomponent/index.scss
+++ b/src/menu/components/share/actioncomponent/index.scss
@@ -29,6 +29,12 @@
         text-decoration: inherit;
         font-weight: inherit;
       }
+      .mk-disconnect {
+        font-size: 10px;
+        position: absolute;
+        right: 1px;
+        top: 0px;
+      }
     }
     .swiper {
       margin-left: 10px;
diff --git a/src/menu/components/share/copycomponent/index.jsx b/src/menu/components/share/copycomponent/index.jsx
index 53404dc..af234aa 100644
--- a/src/menu/components/share/copycomponent/index.jsx
+++ b/src/menu/components/share/copycomponent/index.jsx
@@ -39,10 +39,16 @@
 
           _val.subMenus.push(cell)
         }
+      } else if (['normaltable', 'editable', 'basetable'].includes(type)) {
+        _val.action = _val.action.filter(item => !item.origin)
+        _val.cols = _val.cols.filter(item => !item.origin)
+        _val.search = _val.search.filter(item => !item.origin)
+        delete _val.isNew
       }
 
       _val = window.btoa(window.encodeURIComponent(JSON.stringify(_val)))
     } catch (e) {
+      console.warn(e)
       message.warning('澶嶅埗澶辫触锛岃閲嶈瘯锛�')
       _val = ''
     }
diff --git a/src/menu/components/share/markcomponent/index.jsx b/src/menu/components/share/markcomponent/index.jsx
index 84c2b73..50c608f 100644
--- a/src/menu/components/share/markcomponent/index.jsx
+++ b/src/menu/components/share/markcomponent/index.jsx
@@ -61,7 +61,15 @@
         width: '16%',
         editable: true,
         required: false,
-        inputType: 'input'
+        inputType: 'input',
+        render: (text, record) => {
+          if (!text) return ''
+          if (record.field && record.field[1] === 'dynamic') {
+            return <span title="瀵规瘮鍔ㄦ�佸�兼椂锛屽姣斿�兼棤鏁堛��" style={{textDecoration: 'line-through'}}>{text}</span>
+          } else {
+            return text
+          }
+        }
       },
       {
         title: '棰滆壊',
@@ -136,9 +144,13 @@
     _columns.unshift({field: '$Index', label: '搴忓彿'})
 
     let options = _columns.map(col => {
+      let label = col.label
+      if (col.label.toLowerCase() !== col.field.toLowerCase()) {
+        label += `锛�${col.field}锛塦
+      }
       return {
         value: col.field,
-        label: col.label,
+        label: label,
         isLeaf: false,
         children: [
           {
@@ -149,9 +161,13 @@
             value: 'dynamic',
             label: '鍔ㄦ�佸��',
             children: _columns.map(cell => {
+              let _label = cell.label
+              if (cell.label.toLowerCase() !== cell.field.toLowerCase()) {
+                _label += `锛�${cell.field}锛塦
+              }
               return {
                 value: cell.field,
-                label: cell.label
+                label: _label
               }
             })
           }
@@ -330,7 +346,7 @@
         <AntDesignOutlined className="profile" title="鏍囪" onClick={this.resetMark} />
         <Modal
           wrapClassName="model-table-column-mark-modal"
-          title={'鏍囪璁剧疆'}
+          title="鏍囪璁剧疆"
           visible={visible}
           width={'75vw'}
           maskClosable={false}
diff --git a/src/menu/components/share/normalform/index.jsx b/src/menu/components/share/normalform/index.jsx
index c8244d9..56c3f9a 100644
--- a/src/menu/components/share/normalform/index.jsx
+++ b/src/menu/components/share/normalform/index.jsx
@@ -8,6 +8,7 @@
 import './index.scss'
 
 const MKTable = asyncComponent(() => import('@/components/normalform/modalform/mkTable'))
+const StyleInput = asyncComponent(() => import('@/components/normalform/modalform/styleInput'))
 
 class SettingForm extends Component {
   static propTpyes = {
@@ -80,6 +81,8 @@
         content = (<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)
       } else if (item.type === 'number') {
         content = (<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.handleSubmit}/>)
+      } else if (item.type === 'styleInput') {
+        content = (<StyleInput config={item}/>)
       } else if (item.type === 'select') {
         content = (<Select mode={item.multi ? 'multiple' : ''}>
           {item.options.map((option, index) =>
diff --git a/src/menu/components/share/normalheader/index.jsx b/src/menu/components/share/normalheader/index.jsx
index 2f552fd..acc4e22 100644
--- a/src/menu/components/share/normalheader/index.jsx
+++ b/src/menu/components/share/normalheader/index.jsx
@@ -48,24 +48,24 @@
   changeStyle = () => {
     const { config } = this.props
 
-    let options = ['font', 'border', 'background']
-    if (config.type === 'menubar') {
-      options.push('padding')
-    }
+    let options = ['font', 'border', 'background', 'padding']
 
     MKEmitter.emit('changeStyle', options, config.headerStyle, this.getStyle)
   }
 
   render() {
     const { config, hideSearch } = this.props
-    // const { appType } = this.state
 
-    let title = config.plot ? config.plot.title : config.wrap.title
+    let title = ''
     let show = true
 
-    // if (!title && appType === 'mob' && config.type === 'card' && config.subtype === 'datacard' && config.action && config.action.length) {
-    //   title = ' '
-    // }
+    if (config.plot) {
+      title = config.plot.title
+    } else if (config.type === 'group') {
+      title = config.setting.title || ''
+    } else if (config.wrap) {
+      title = config.wrap.title || ''
+    }
 
     if (!title && (!config.search || config.search.length === 0 || hideSearch === 'true')) {
       show = false
diff --git a/src/menu/components/share/normalheader/index.scss b/src/menu/components/share/normalheader/index.scss
index 9d23a2a..0d9f65f 100644
--- a/src/menu/components/share/normalheader/index.scss
+++ b/src/menu/components/share/normalheader/index.scss
@@ -1,9 +1,12 @@
 .normal-header {
   position: relative;
   height: 45px;
+  padding-right: 8px;
   border-bottom: 1px solid #e8e8e8;
   overflow: hidden;
+  letter-spacing: 0px;
   line-height: 45px;
+  box-sizing: content-box;
 
   .title {
     text-decoration: inherit;
@@ -16,6 +19,9 @@
     z-index: 1;
     min-height: 45px;
     min-width: 30px;
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
   }
   .model-custom-header-search-list {
     flex: 1;
diff --git a/src/menu/components/share/pastebasetable/index.jsx b/src/menu/components/share/pastebasetable/index.jsx
new file mode 100644
index 0000000..ac88559
--- /dev/null
+++ b/src/menu/components/share/pastebasetable/index.jsx
@@ -0,0 +1,135 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Modal, notification, Button } from 'antd'
+import { SnippetsOutlined } from '@ant-design/icons'
+
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+// import './index.scss'
+
+const PasteForm = asyncComponent(() => import('@/templates/zshare/pasteform'))
+
+class PasteBaseTable extends Component {
+  static propTpyes = {
+    type: PropTypes.any,
+    insert: PropTypes.func
+  }
+
+  state = {
+    visible: false
+  }
+
+  handleMenuClick = () => {
+    this.setState({visible: true})
+  }
+
+  pasteSubmit = () => {
+    this.pasteFormRef.handleConfirm().then(res => {
+      if (res.copyType !== 'basetable') {
+        notification.warning({
+          top: 92,
+          message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�',
+          duration: 5
+        })
+        return
+      }
+
+      delete res.copyType
+
+      res.uuid = Utils.getuuid()
+
+      res.search = res.search.map(cell => {
+        cell.uuid = Utils.getuuid()
+        return cell
+      })
+
+      let loopCol = (col) => {
+        col.subcols = col.subcols.map(c => {
+          c.uuid = Utils.getuuid()
+
+          if (c.type === 'colspan' && c.subcols) {
+            c = loopCol(c)
+          } else if (c.type === 'custom' && c.elements) {
+            c.elements = c.elements.map(cell => {
+              cell.uuid = Utils.getuuid()
+              return cell
+            })
+          }
+          return c
+        })
+
+        return col
+      }
+
+      res.cols = res.cols.map(col => {
+        col.uuid = Utils.getuuid()
+
+        if (col.type === 'colspan' && col.subcols) {
+          col = loopCol(col)
+        } else if (col.type === 'custom' && col.elements) {
+          col.elements = col.elements.map(cell => {
+            cell.uuid = Utils.getuuid()
+            return cell
+          })
+        } else if (col.type === 'action' && col.elements) {
+          col.elements = col.elements.map(cell => {
+            cell.uuid = Utils.getuuid()
+            return cell
+          })
+        }
+        return col
+      })
+
+      let oriUids = {}
+      res.action = res.action.map(cell => {
+        let _uuid = Utils.getuuid()
+
+        oriUids[cell.uuid] = _uuid
+
+        cell.uuid = _uuid
+
+        return cell
+      })
+
+      res.setting.supModule = ''
+
+      if (res.wrap && res.wrap.doubleClick) {
+        res.wrap.doubleClick = oriUids[res.wrap.doubleClick] || ''
+      }
+      
+      this.props.insert(res)
+
+      this.setState({visible: false})
+
+      notification.success({
+        top: 92,
+        message: '绮樿创鎴愬姛锛�',
+        duration: 2
+      })
+    })
+  }
+
+  render() {
+    const { type } = this.props
+    const { visible } = this.state
+
+    return (
+      <div style={{display: 'inline-block'}}>
+        {type === 'page' ? <Button icon="snippets" style={{color: '#1890ff', borderColor: '#1890ff'}} onClick={() => {this.setState({visible: true})}} >绮樿创</Button> : <SnippetsOutlined style={{color: 'purple'}} onClick={() => {this.setState({visible: true})}} />}
+        <Modal
+          title="绮樿创"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.pasteSubmit}
+          onCancel={() => {this.setState({visible: false})}}
+          destroyOnClose
+        >
+          <PasteForm wrappedComponentRef={(inst) => this.pasteFormRef = inst} inputSubmit={this.pasteSubmit}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default PasteBaseTable
\ No newline at end of file
diff --git a/src/views/popdesign/menuform/index.scss b/src/menu/components/share/pastebasetable/index.scss
similarity index 100%
copy from src/views/popdesign/menuform/index.scss
copy to src/menu/components/share/pastebasetable/index.scss
diff --git a/src/menu/components/share/pastecomponent/index.jsx b/src/menu/components/share/pastecomponent/index.jsx
index b0c79bb..cb1bb7b 100644
--- a/src/menu/components/share/pastecomponent/index.jsx
+++ b/src/menu/components/share/pastecomponent/index.jsx
@@ -25,15 +25,8 @@
     this.setState({visible: true})
   }
 
-  resetconfig = (item, copyBtns, config) => {
+  resetconfig = (item, config) => {
     let _uuid = Utils.getuuid()
-
-    if (item.OpenType === 'popview') {
-      let _cell = fromJS(item).toJS()
-      _cell.$originUuid = _cell.uuid
-      _cell.uuid = _uuid
-      copyBtns.set(_uuid, _cell)
-    }
 
     if (item.uuid) {
       item.uuid = _uuid
@@ -109,14 +102,7 @@
           })
         } else if (_item.type === 'action' && _item.elements) {
           _item.elements = _item.elements.map(cell => {
-            let _uuid = Utils.getuuid()
-            if (cell.OpenType === 'popview') {
-              let _cell = fromJS(cell).toJS()
-              _cell.$originUuid = _cell.uuid
-              _cell.uuid = _uuid
-              copyBtns.set(_uuid, _cell)
-            }
-            cell.uuid = _uuid
+            cell.uuid = Utils.getuuid()
             return cell
           })
         }
@@ -138,25 +124,15 @@
 
       let type = res.copyType
       let config = fromJS(this.props.config).toJS()
-      let copyBtns = new Map()
 
-      res = this.resetconfig(res, copyBtns, config)
+      res = this.resetconfig(res, config)
       delete res.copyType
-
-      copyBtns = [...copyBtns.values()]
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       if (config.type === 'form') {
         this.props.updateConfig(res)
         this.setState({visible: false})
         return
       } else if (type === 'action') {
-        config.action = config.action || []
-        config.action = config.action.filter(item => !item.origin)
-
         if (['line', 'bar', 'scatter'].includes(config.type) && !['excelOut', 'excelIn'].includes(res.OpenType)) {
           notification.warning({ top: 92, message: '鍥捐〃涓笉鏀寔姝ょ被鎸夐挳锛�', duration: 5 })
           return
diff --git a/src/menu/components/share/searchcomponent/index.jsx b/src/menu/components/share/searchcomponent/index.jsx
index 3136358..80730a4 100644
--- a/src/menu/components/share/searchcomponent/index.jsx
+++ b/src/menu/components/share/searchcomponent/index.jsx
@@ -222,6 +222,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/menu/components/share/sourcecomponent/index.jsx b/src/menu/components/share/sourcecomponent/index.jsx
index ae5a6eb..2d197f7 100644
--- a/src/menu/components/share/sourcecomponent/index.jsx
+++ b/src/menu/components/share/sourcecomponent/index.jsx
@@ -78,7 +78,7 @@
           <DeleteOutlined title="鍒犻櫎鏂囦欢" onClick={this.deleteUrl}/>
         </div> : null}
         {url && type !== 'video' && url !== '@icon@' ? <div className="mk-source-item-info picture">
-          <img src={url} alt="" />
+          <img src={url.replace(/@mywebsite@\//ig, window.GLOB.baseurl)} alt="" />
           <a target="_blank" rel="noopener noreferrer" href={url}>{name}</a>
           <DeleteOutlined title="鍒犻櫎鏂囦欢" onClick={this.deleteUrl}/>
         </div> : null}
diff --git a/src/menu/components/table/base-table/columns/editColumn/formconfig.jsx b/src/menu/components/table/base-table/columns/editColumn/formconfig.jsx
index 8938741..b195aa8 100644
--- a/src/menu/components/table/base-table/columns/editColumn/formconfig.jsx
+++ b/src/menu/components/table/base-table/columns/editColumn/formconfig.jsx
@@ -36,6 +36,9 @@
     value: 'picture',
     text: '鍥剧墖'
   }, {
+    value: 'video',
+    text: '瑙嗛'
+  }, {
     value: 'link',
     text: '閾炬帴'
   }, {
@@ -163,6 +166,26 @@
       }]
     },
     {
+      type: 'number',
+      key: 'startTime',
+      precision: 0,
+      label: '寮�濮嬫椂闂�',
+      initVal: card.startTime || 0,
+      tooltip: '瑙嗛寮�濮嬫挱鏀剧殑鏃堕棿锛岀敤浜庤皟鏁磋棰戝垵濮嬪寲灞曠ず鐨勭晫闈€��',
+      required: false
+    },
+    {
+      type: 'select',
+      key: 'aspectRatio',
+      label: '闀垮姣�',
+      initVal: card.aspectRatio || '16:9',
+      required: true,
+      options: [
+        { value: '4:3', text: '4:3' },
+        { value: '16:9', text: '16:9' }
+      ]
+    },
+    {
       type: 'radio',
       key: 'rowspan',
       label: '琛屽悎骞�',
@@ -190,7 +213,16 @@
       }, {
         value: 'false',
         text: '鍚�'
-      }]
+      }],
+      forbid: card.isSub
+    },
+    {
+      type: 'number',
+      key: 'fieldlength',
+      label: '瀛楁闀垮害',
+      initVal: card.fieldlength || 50,
+      required: true,
+      forbid: card.isSub
     },
     {
       type: 'number',
@@ -199,8 +231,8 @@
       max: 18,
       decimal: 0,
       label: '灏忔暟浣�',
-      initVal: card.decimal || 0,
-      required: true
+      initVal: card.decimal === undefined ? 0 : card.decimal,
+      required: !card.isSub
     },
     {
       type: 'select',
diff --git a/src/menu/components/table/base-table/columns/editColumn/index.jsx b/src/menu/components/table/base-table/columns/editColumn/index.jsx
index cbb8ac4..67cb0aa 100644
--- a/src/menu/components/table/base-table/columns/editColumn/index.jsx
+++ b/src/menu/components/table/base-table/columns/editColumn/index.jsx
@@ -10,11 +10,12 @@
 
 const { TextArea } = Input
 const columnTypeOptions = {
-  text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'blacklist', 'perspective', 'rowspan'],
+  text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'fieldlength', 'blacklist', 'perspective', 'rowspan'],
   number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
-  link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'blacklist', 'nameField'],
-  textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'blacklist'],
-  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'span'],
+  link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'fieldlength', 'blacklist', 'nameField'],
+  textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'fieldlength', 'prefix', 'postfix', 'blacklist'],
+  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'fieldlength', 'blacklist', 'scale', 'lenWidRadio', 'span'],
+  video: ['label', 'field', 'type', 'Align', 'Hide', 'startTime', 'Width', 'fieldlength', 'blacklist', 'aspectRatio'],
   colspan: ['label', 'type', 'Align', 'Hide', 'blacklist'],
   custom: ['label', 'type', 'Align', 'Hide', 'Width', 'blacklist'],
   action: ['label', 'type', 'Align', 'Width'],
@@ -164,7 +165,7 @@
     if (!formlist) return null
 
     formlist.forEach((item, index) => {
-      if (item.hidden || item.forbidden) return
+      if (item.hidden || item.forbid) return
 
       if (item.type === 'text') {
         let rules = []
@@ -349,8 +350,9 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
-        this.setState({visible: false, formlist: null})
-        this.props.submitCol(values)
+        this.props.submitCol(values, () => {
+          this.setState({visible: false, formlist: null})
+        })
       }
     })
   }
diff --git a/src/menu/components/table/base-table/columns/index.jsx b/src/menu/components/table/base-table/columns/index.jsx
index 5292b18..a5f8102 100644
--- a/src/menu/components/table/base-table/columns/index.jsx
+++ b/src/menu/components/table/base-table/columns/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { DndProvider, DragSource, DropTarget } from 'react-dnd'
-import { Table, Popover, Modal, message } from 'antd'
+import { Table, Popover, Modal, message, notification } from 'antd'
 import { PlusOutlined, EditOutlined, CopyOutlined, DeleteOutlined, FontColorsOutlined, CloseCircleOutlined, AntDesignOutlined } from '@ant-design/icons'
 
 import asyncComponent from '@/utils/asyncComponent'
@@ -12,10 +12,11 @@
 import './index.scss'
 
 const { confirm } = Modal
-const EditColumn = asyncIconComponent(() => import('./editColumn'))
+const EditColumn = asyncComponent(() => import('./editColumn'))
 const MarkColumn = asyncIconComponent(() => import('@/menu/components/share/markcomponent'))
 const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
+const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 
 class HeaderCol extends Component {
   deleteCol = () => {
@@ -49,11 +50,17 @@
   }
 
   render() {
-    const { connectDragSource, connectDropTarget, moveCol, addElement, updateCol, editColumn, pasteCell, changeStyle, deleteCol, index, column, align, fields, children, ...restProps } = this.props
+    const { connectDragSource, connectDropTarget, moveCol, addElement, dropCol, updateCol, editColumn, pasteCell, changeStyle, deleteCol, index, column, align, fields, children, ...restProps } = this.props
 
     if (index !== undefined) {
+      let style = {cursor: 'move', textAlign: align}
+      if (column.Width) {
+        style.width = column.Width
+        style.minWidth = column.Width
+      }
+
       return connectDragSource(
-        connectDropTarget(<th {...restProps} index={index} style={{ cursor: 'move', textAlign: align }} onDoubleClick={() => column && this.props.editColumn(column)}>
+        connectDropTarget(<th {...restProps} index={index} style={style} onDoubleClick={() => column && this.props.editColumn(column)}>
           <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
             <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
               {column && ['custom', 'colspan', 'action'].includes(column.type) ?
@@ -71,8 +78,13 @@
         </th>),
       )
     } else if (column) {
+      let style = {textAlign: align}
+      if (column.Width) {
+        style.width = column.Width
+        style.minWidth = column.Width
+      }
       return (
-        <th {...restProps} key={column.uuid} onDoubleClick={() => this.props.editColumn(column)}>
+        <th {...restProps} style={style} key={column.uuid} onDoubleClick={() => this.props.editColumn(column)}>
           <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
             <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
               {column && ['custom', 'colspan'].includes(column.type) ?
@@ -104,10 +116,14 @@
 
 const ColTarget = {
   drop(props, monitor) {
-    const dragIndex = monitor.getItem().index
+    const item = monitor.getItem()
+    const dragIndex = item.index
     const hoverIndex = props.index
 
-    if (dragIndex === undefined || hoverIndex === undefined || dragIndex === hoverIndex) {
+    if (item.$init) {
+      props.dropCol(item, hoverIndex)
+      return
+    } else if (dragIndex === undefined || hoverIndex === undefined || dragIndex === hoverIndex) {
       return
     }
 
@@ -145,13 +161,13 @@
 
     if (column && column.type === 'custom') {
       return (
-        <td style={{padding: 0, minWidth: column.Width || 100, ...(column.style || {})}} className={className}>
+        <td style={{padding: 0, ...(column.style || {})}} className={className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
     } else if (column && column.type === 'action') {
       return (
-        <td style={{padding: '0 5px', textAlign: column.Align, minWidth: column.Width || 100}} className={'action-column ' + className}>
+        <td style={{padding: '0 5px', textAlign: column.Align}} className={'action-column ' + className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
@@ -167,7 +183,7 @@
         }
       }
       return (
-        <td style={{...style, minWidth: column.Width || 100}} className={className}>
+        <td style={{...style}} className={className}>
           {val}
           {column.Hide === 'true' ? <CloseCircleOutlined style={{marginLeft: '5px', color: 'orange', fontSize: '12px'}}/> : null}
           {column.marks && column.marks.length ? <AntDesignOutlined className="profile"/> : null}
@@ -265,10 +281,31 @@
     })
   }
 
+  dropCol = (item, hoverIndex) => {
+    let _columns = fromJS(this.state.columns).toJS()
+
+    let col = { focus: true, uuid: Utils.getuuid(), label: 'label', field: '', type: item.subType, elements: [] }
+    if (col.type === 'colspan') {
+      col.subcols = []
+    } else if (col.type === 'action') {
+      col.label = '鎿嶄綔'
+    } else if (col.type === 'index') {
+      col.label = '搴忓彿'
+    }
+
+    _columns.splice(hoverIndex, 0, col)
+
+    this.setState({
+      columns: _columns
+    }, () => {
+      this.editColumn(col)
+    })
+  }
+
   loopCol = (columns, col) => {
     return columns.map(column => {
       if (column.type === 'colspan') {
-        column.subcols = this.loopCol(column.subcols, col)
+        column.subcols = this.loopCol(column.subcols || [], col)
       }
       if (column.uuid === col.uuid) {
         return col
@@ -287,6 +324,10 @@
       let config = {...this.props.config, cols: _columns}
       if (btn) {
         config.action = config.action.filter(item => item.uuid !== btn.uuid)
+
+        setTimeout(() => {
+          MKEmitter.emit('revert', config.uuid)
+        }, 200)
       }
 
       this.props.updatecolumn(config)
@@ -345,8 +386,8 @@
     }
   }
 
-  submitCol = (col) => {
-    const { card } = this.state
+  submitCol = (col, resolve) => {
+    const { card, columns } = this.state
 
     col.uuid = card.uuid
     col.isSub = card.isSub === true
@@ -358,6 +399,28 @@
       col.elements = card.type === 'custom' ? (card.elements || []) : []
     } else if (col.type === 'action') {
       col.elements = card.type === 'action' ? (card.elements || []) : []
+    }
+
+    if (!col.field || col.isSub) {
+      resolve()
+    } else {
+      let _cols = []
+      columns.forEach(c => {
+        if (c.field && col.uuid !== c.uuid) {
+          _cols.push(c.field.toLowerCase())
+        }
+      })
+
+      if (_cols.includes(col.field.toLowerCase())) {
+        notification.warning({
+          top: 92,
+          message: '瀛楁宸插瓨鍦紒',
+          duration: 5
+        })
+        return
+      } else {
+        resolve()
+      }
     }
 
     this.setState({card: null})
@@ -409,19 +472,6 @@
     }, () => {
       this.props.updatecolumn({...this.props.config, cols: _columns})
     })
-
-    if (col.type !== 'action') return
-
-    let uuids = []
-    col.elements && col.elements.forEach(c => {
-      if (c.OpenType !== 'popview') return
-
-      uuids.push(c.uuid)
-    })
-
-    if (uuids.length === 0) return
-    
-    MKEmitter.emit('delButtons', uuids)
   }
 
   updateLineMarks = (vals) => {
@@ -467,7 +517,6 @@
         title: col.label,
         dataIndex: col.uuid,
         align: col.Align,
-        // sorter: !isSub && col.IsSort === 'true',
         sorter: col.IsSort === 'true',
         onCell: () => ({
           column: col,
@@ -482,6 +531,7 @@
           align: col.Align,
           moveCol: this.moveCol,
           updateCol: this.updateCol,
+          dropCol: this.dropCol,
           addElement: this.addElement,
           editColumn: this.editColumn,
           pasteCell: this.pasteCell,
@@ -493,6 +543,27 @@
     })
   }
 
+  plusColumns = (id, items) => {
+    const { config } = this.props
+
+    if (id !== config.uuid) return
+
+    let _columns = fromJS(this.state.columns).toJS()
+    _columns = _columns.filter(a => !a.origin)
+
+    _columns.push(...items)
+
+    this.setState({
+      columns: _columns
+    }, () => {
+      this.props.updatecolumn({...config, cols: _columns})
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('plusColumns', this.plusColumns)
+  }
+
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
    */
@@ -500,6 +571,7 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('plusColumns', this.plusColumns)
   }
 
   render() {
@@ -515,18 +587,48 @@
     }
 
     const columns = this.handlecolumns(this.state.columns, fields, config)
+    let groups = null
+    if (columns.length > 12) {
+      let g = Math.ceil(columns.length / 8)
+      let n = Math.ceil(columns.length / g)
+      groups = []
+
+      for (let i = 0, len = columns.length; i < len; i += n) {
+        groups.push(columns.slice(i, i + n))
+      }
+    }
 
     return (
       <div className={`normal-table-columns ${config.setting.laypage} ${config.wrap.tableType} ${config.wrap.mode || ''}`} id={tableId}>
         <div className="col-control">
+          <FieldsComponent config={config} type="columns" />
           <CopyOutlined title="澶嶅埗鏄剧ず鍒�" onClick={this.copycolumn} />
           <MarkColumn columns={fields} type="line" marks={lineMarks} onSubmit={this.updateLineMarks} />
         </div>
         <DndProvider>
+          {groups ? groups.map((group, i) => {
+            return <Table
+              key={i}
+              rowKey="uuid"
+              size={config.wrap.size || 'middle'}
+              bordered={config.wrap.bordered !== 'false'}
+              components={components}
+              dataSource={this.state.data}
+              rowSelection={config.wrap.tableType && i === 0 ? { type: 'radio' } : null}
+              columns={group}
+              pagination={i === groups.length - 1 ? {
+                current: 1,
+                pageSize: 10,
+                pageSizeOptions: ['10', '25', '50', '100', '500', '1000'],
+                showSizeChanger: true,
+                total: 58,
+                showTotal: (total, range) => `${range[0]}-${range[1]} 鍏� ${total} 鏉
+              } : false}
+            />
+          }) :
           <Table
             rowKey="uuid"
             size={config.wrap.size || 'middle'}
-            rowClassName="editable-row"
             bordered={config.wrap.bordered !== 'false'}
             components={components}
             dataSource={this.state.data}
@@ -540,7 +642,7 @@
               total: 58,
               showTotal: (total, range) => `${range[0]}-${range[1]} 鍏� ${total} 鏉
             }}
-          />
+          />}
         </DndProvider>
         <EditColumn column={card} fields={fields} submitCol={this.submitCol} cancelCol={this.cancelCol}/>
       </div>
diff --git a/src/menu/components/table/base-table/columns/index.scss b/src/menu/components/table/base-table/columns/index.scss
index 3d4835c..8f8cf70 100644
--- a/src/menu/components/table/base-table/columns/index.scss
+++ b/src/menu/components/table/base-table/columns/index.scss
@@ -5,10 +5,18 @@
   --mk-table-font-size: 14px;
   --mk-table-font-weight: normal;
 
+  .ant-table-wrapper + .ant-table-wrapper {
+    margin-top: 10px;
+  }
   .ant-table {
     color: inherit;
     font-size: inherit;
     font-weight: inherit;
+  }
+  .ant-table-selection-column {
+    width: 60px;
+    min-width: 60px;
+    max-width: 60px;
   }
   .ant-table-body {
     overflow-x: auto;
@@ -72,15 +80,21 @@
     >.anticon-copy {
       color: #26C281;
     }
-    >.anticon-delete {
-      color: #ff4d4f;
-    }
-    >.anticon-file-sync {
-      color: #1890ff;
-    }
     >div >.profile {
       color: purple;
     }
+    .quickly-add {
+      display: inline-block;
+      margin-right: 15px;
+      .ant-btn {
+        background-color: transparent;
+        color: #1890ff;
+        border: none;
+        box-shadow: none !important;
+        height: 18px;
+        padding: 0px;
+      }
+    }
   }
   .ant-table-small > .ant-table-content > .ant-table-body {
     margin: 0;
diff --git a/src/menu/components/table/base-table/index.jsx b/src/menu/components/table/base-table/index.jsx
index 9620ec9..5b982cd 100644
--- a/src/menu/components/table/base-table/index.jsx
+++ b/src/menu/components/table/base-table/index.jsx
@@ -6,6 +6,7 @@
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
+import { getTables } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import getWrapForm from './options'
 import Utils from '@/utils/utils.js'
@@ -38,6 +39,7 @@
       let _card = {
         uuid: card.uuid,
         type: card.type,
+        name: card.name || '',
         format: 'array',    // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
         pageable: true,     // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
         switchable: true,   // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
@@ -53,8 +55,8 @@
         subtype: card.subtype,
         setting: { interType: 'system' },
         wrap: { bordered: 'true', tableType: 'checkbox', show: 'true' },
-        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
-        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
+        style: {},
+        headerStyle: {},
         columns: [],
         cols: [
           { origin: true, uuid: Utils.getuuid(), Align: 'left', label: 'label1', field: '', Hide: 'false', type: 'text', Width: 120 },
@@ -63,6 +65,10 @@
         ],
         scripts: [],
         isNew: true
+      }
+
+      if (card.name === '涓昏〃') {
+        _card.setting.supModule = ['empty']
       }
 
       this.updateComponent(_card)
@@ -76,7 +82,6 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('submitModal', this.handleSave)
     MKEmitter.addListener('completeSave', this.completeSave)
   }
 
@@ -91,7 +96,6 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('submitModal', this.handleSave)
     MKEmitter.removeListener('completeSave', this.completeSave)
   }
 
@@ -105,7 +109,6 @@
       item.cols = item.cols.filter(a => !a.origin)
 
       delete item.isNew
-
       this.setState({card: item}, () => { MKEmitter.emit('revert') })
     }
   }
@@ -148,6 +151,10 @@
         card.errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
       } else if (!card.setting.supModule) {
         card.errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+      }
+
+      if (card.errors.length === 0) {
+        card.$tables = getTables(card)
       }
 
       card.action.forEach(cell => {
@@ -257,26 +264,6 @@
     }
   }
 
-  handleSave = (_cards, btn, modal) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (card.uuid !== _cards.uuid) return
-
-    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
-
-    if (_index === -1) return
-
-    card.action = card.action.map(cell => {
-      if (cell.uuid === btn.uuid) {
-        cell.modal = modal
-      }
-
-      return cell
-    })
-
-    this.updateComponent(card)
-  }
-
   getWrapForms = () => {
     const { wrap, action, columns, cols } = this.state.card
 
@@ -307,6 +294,64 @@
     this.updateComponent({...card, wrap: res})
   }
 
+  updatecolumn = (config) => {
+    config.columns = []
+    config.absFields = []
+    config.cols.forEach(col => {
+      if (!col.field) return
+      if (['text', 'picture', 'video', 'textarea'].includes(col.type)) {
+        config.columns.push({
+          datatype: `Nvarchar(${col.fieldlength || 50})`,
+          field: col.field,
+          fieldlength: col.fieldlength || 50,
+          label: col.label,
+          type: 'text',
+          uuid: col.uuid
+        })
+      } else if (col.type === 'link') {
+        config.columns.push({
+          datatype: `Nvarchar(${col.fieldlength || 50})`,
+          field: col.field,
+          fieldlength: col.fieldlength || 50,
+          label: col.label,
+          type: 'text',
+          uuid: col.uuid
+        })
+        if (col.nameField) {
+          config.columns.push({
+            datatype: `Nvarchar(${col.fieldlength || 50})`,
+            field: col.nameField,
+            fieldlength: col.fieldlength || 50,
+            label: col.label + '(鍚嶇О)',
+            type: 'text',
+            uuid: col.uuid + 'name'
+          })
+        }
+      } else if (col.type === 'number') {
+        config.columns.push({
+          datatype: `Decimal(18,${col.decimal || 0})`,
+          field: col.field,
+          decimal: col.decimal || 0,
+          label: col.label,
+          type: 'number',
+          uuid: col.uuid
+        })
+
+        if (col.format === 'abs') {
+          config.absFields.push(col.field)
+        }
+      }
+    })
+
+    if (config.absFields.length) {
+      config.absFields = Array.from(new Set(config.absFields))
+    } else {
+      config.absFields = null
+    }
+
+    this.updateComponent(config)
+  }
+
   render() {
     const { card } = this.state
     let options = ['action', 'search', 'form', 'cols']
@@ -318,7 +363,7 @@
             <NormalForm title="琛ㄦ牸璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
               <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
             </NormalForm>
-            <CopyComponent type="normaltable" card={card}/>
+            <CopyComponent type="basetable" card={card}/>
             <PasteComponent config={card} options={options} updateConfig={this.updateComponent} />
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
             <SettingComponent config={card} updateConfig={this.updateComponent} />
@@ -328,7 +373,7 @@
         </Popover>
         <SearchComponent config={card} updatesearch={this.updateComponent}/>
         <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
-        <ColumnComponent config={card} updatecolumn={this.updateComponent}/>
+        <ColumnComponent config={card} updatecolumn={this.updatecolumn}/>
       </div>
     )
   }
diff --git a/src/menu/components/table/base-table/index.scss b/src/menu/components/table/base-table/index.scss
index a5c6fd6..017fbe0 100644
--- a/src/menu/components/table/base-table/index.scss
+++ b/src/menu/components/table/base-table/index.scss
@@ -41,21 +41,11 @@
     background: rgba(255, 255, 255, 0.55);
   }
   .model-menu-action-list {
-    line-height: 55px;
+    line-height: 40px;
     padding: 0px;
-    min-height: 55px;
     >.ant-row {
-      min-height: 30px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
+      padding-top: 10px;
+      min-height: 55px;
     }
   }
 
diff --git a/src/menu/components/table/base-table/options.jsx b/src/menu/components/table/base-table/options.jsx
index 3af1d39..6a9c6c7 100644
--- a/src/menu/components/table/base-table/options.jsx
+++ b/src/menu/components/table/base-table/options.jsx
@@ -72,7 +72,7 @@
       field: 'tableMode',
       label: '鍔犺浇妯″紡',
       initval: wrap.tableMode || 'compatible',
-      tooltip: '浣跨敤鎬ラ�熸ā寮忔椂锛岃〃鏍间腑鐨勬爣璁般�佸弻鍑讳簨浠躲�佹牸寮忓寲銆佽鍚堝苟銆佸墠缂�銆佸悗缂�銆佸瓧娈甸�忚绛夋晥鏋滃皢鏃犳晥锛屼笖鏁版嵁閮戒細浠ユ枃鏈牸寮忔樉绀恒��',
+      tooltip: '浣跨敤鎬ラ�熸ā寮忔椂锛岃〃鏍间腑鐨勬爣璁般�佸弻鍑讳簨浠躲�佹牸寮忓寲銆佽鍚堝苟銆佸垪鍚堝苟銆佸墠缂�銆佸悗缂�銆佸瓧娈甸�忚绛夋晥鏋滃皢鏃犳晥锛屼笖鏁版嵁閮戒細浠ユ枃鏈牸寮忔樉绀恒��',
       required: false,
       options: [
         {value: 'compatible', label: '鍏煎'},
diff --git a/src/menu/components/table/edit-table/columns/index.jsx b/src/menu/components/table/edit-table/columns/index.jsx
index e7a7c56..25bd842 100644
--- a/src/menu/components/table/edit-table/columns/index.jsx
+++ b/src/menu/components/table/edit-table/columns/index.jsx
@@ -59,8 +59,14 @@
       </th>
     )
 
+    let style = {cursor: 'move', textAlign: align}
+    if (column.Width) {
+      style.width = column.Width
+      style.minWidth = column.Width
+    }
+
     return connectDragSource(
-      connectDropTarget(<th {...restProps} index={index} style={{ cursor: 'move', textAlign: align }} onDoubleClick={() => this.props.editColumn(column)}>
+      connectDropTarget(<th {...restProps} index={index} style={style} onDoubleClick={() => this.props.editColumn(column)}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
             {['custom', 'action'].includes(column.type) ?
@@ -136,13 +142,13 @@
 
     if (column && column.type === 'custom') {
       return (
-        <td style={{padding: 0, minWidth: column.Width || 100, ...(column.style || {})}} className={className}>
+        <td style={{padding: 0, ...(column.style || {})}} className={className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
     } else if (column && column.type === 'action') {
       return (
-        <td style={{padding: '0 5px', textAlign: column.Align, minWidth: column.Width || 100}} className={'action-column ' + className}>
+        <td style={{padding: '0 5px', textAlign: column.Align}} className={'action-column ' + className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
@@ -158,7 +164,7 @@
         }
       }
       return (
-        <td style={{...style, minWidth: column.Width || 100}} className={className}>
+        <td style={style} className={className}>
           {column.field ? <Paragraph className="col-copy" copyable={{ text: column.field }}>{val}</Paragraph> : val}
           {column.marks && column.marks.length ? <AntDesignOutlined className="profile"/> : null}
           {column.Hide === 'true' ? <CloseCircleOutlined className="close-col"/> : null}
@@ -181,7 +187,6 @@
   }
 
   state = {
-    appType: sessionStorage.getItem('appType'),
     tableId: '',
     data: [{uuid: Utils.getuuid()}],
     refresh: false,    // 寮哄埗鍒锋柊
@@ -262,11 +267,16 @@
     let _columns = fromJS(this.state.columns).toJS()
     let type = item.subType
 
-    if (item.subType === 'link' || item.subType === 'colspan' || item.subType === 'picture') {
+    if (!['text', 'number', 'textarea', 'custom', 'action', 'formula', 'index'].includes(item.subType)) {
       type = 'text'
     }
 
     let col = { focus: true, uuid: Utils.getuuid(), label: 'label', field: '', type: type, elements: [] }
+    if (col.type === 'action') {
+      col.label = '鎿嶄綔'
+    } else if (col.type === 'index') {
+      col.label = '搴忓彿'
+    }
 
     _columns.splice(hoverIndex, 0, col)
 
@@ -292,6 +302,10 @@
       let config = {...this.props.config, cols: _columns}
       if (btn) {
         config.action = config.action.filter(item => item.uuid !== btn.uuid)
+
+        setTimeout(() => {
+          MKEmitter.emit('revert', config.uuid)
+        }, 200)
       }
 
       this.props.updatecolumn(config)
@@ -395,7 +409,6 @@
   }
 
   deleteCol = (col) => {
-    const { appType } = this.state
     let _columns = fromJS(this.state.columns).toJS()
 
     _columns = _columns.filter(column => column.uuid !== col.uuid)
@@ -405,19 +418,6 @@
     }, () => {
       this.props.updatecolumn({...this.props.config, cols: _columns})
     })
-
-    if (col.type !== 'action' || appType === 'mob') return
-
-    let uuids = []
-    col.elements && col.elements.forEach(c => {
-      if (appType === 'pc' && c.OpenType !== 'popview') return
-
-      uuids.push(c.uuid)
-    })
-
-    if (uuids.length === 0) return
-    
-    MKEmitter.emit('delButtons', uuids)
   }
 
   updateLineMarks = (vals) => {
@@ -611,7 +611,6 @@
           <Table
             rowKey="uuid"
             size={config.wrap.size || 'middle'}
-            rowClassName="editable-row"
             bordered={config.wrap.bordered !== 'false'}
             rowSelection={rowSelection}
             components={components}
diff --git a/src/menu/components/table/edit-table/columns/index.scss b/src/menu/components/table/edit-table/columns/index.scss
index 6ca33c7..699ad86 100644
--- a/src/menu/components/table/edit-table/columns/index.scss
+++ b/src/menu/components/table/edit-table/columns/index.scss
@@ -9,6 +9,11 @@
     color: inherit;
     font-size: inherit;
   }
+  .ant-table-selection-column {
+    width: 60px;
+    min-width: 60px;
+    max-width: 60px;
+  }
   .submit-btn {
     min-height: 24px;
     height: auto;
diff --git a/src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx b/src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx
index 4835267..fa48a0f 100644
--- a/src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx
+++ b/src/menu/components/table/edit-table/columns/tableIn/customscript/index.jsx
@@ -61,6 +61,15 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
+        
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
 
         let _quot = values.sql.match(/'{1}/g)
@@ -161,7 +170,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         // 澶栬仈鏁版嵁搴撴浛鎹�
         if (window.GLOB.externalDatabase !== null) {
           param.LText = param.LText.replace(/@db@/ig, window.GLOB.externalDatabase)
diff --git a/src/menu/components/table/edit-table/index.jsx b/src/menu/components/table/edit-table/index.jsx
index d95042e..ecd404c 100644
--- a/src/menu/components/table/edit-table/index.jsx
+++ b/src/menu/components/table/edit-table/index.jsx
@@ -119,7 +119,6 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('submitModal', this.handleSave)
     MKEmitter.addListener('completeSave', this.completeSave)
   }
 
@@ -134,7 +133,6 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('submitModal', this.handleSave)
     MKEmitter.removeListener('completeSave', this.completeSave)
   }
 
@@ -230,7 +228,7 @@
     style.fontSize = card.wrap.fontSize || 14
     style.fontWeight = card.wrap.fontWeight || 'normal'
 
-    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow'], style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -316,26 +314,6 @@
     }
   }
 
-  handleSave = (_cards, btn, modal) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (card.uuid !== _cards.uuid) return
-
-    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
-
-    if (_index === -1) return
-
-    card.action = card.action.map(cell => {
-      if (cell.uuid === btn.uuid) {
-        cell.modal = modal
-      }
-
-      return cell
-    })
-
-    this.updateComponent(card)
-  }
-
   getWrapForms = () => {
     const { wrap, action } = this.state.card
 
@@ -363,6 +341,25 @@
     }
     
     this.updateComponent(_card)
+  }
+
+  updatecolumn = (config) => {
+    config.absFields = []
+    config.cols.forEach(col => {
+      if (col.type === 'number') {
+        if (col.format === 'abs') {
+          config.absFields.push(col.field)
+        }
+      }
+    })
+
+    if (config.absFields.length) {
+      config.absFields = Array.from(new Set(config.absFields))
+    } else {
+      config.absFields = null
+    }
+
+    this.updateComponent(config)
   }
 
   clickComponent = (e) => {
@@ -393,7 +390,7 @@
             <NormalForm title="琛ㄦ牸璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
               <EditOutlined style={{color: '#1890ff'}} title="缂栬緫"/>
             </NormalForm>
-            <CopyComponent type="normaltable" card={card}/>
+            <CopyComponent type="editable" card={card}/>
             <PasteComponent config={card} options={['action', 'search', 'form', 'cols']} updateConfig={this.updateComponent} />
             <FontColorsOutlined className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle}/>
             <UserComponent config={card}/>
@@ -405,7 +402,7 @@
         </Popover>
         <SearchComponent config={card} updatesearch={this.updateComponent}/>
         <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
-        <ColumnComponent config={card} updatecolumn={this.updateComponent}/>
+        <ColumnComponent config={card} updatecolumn={this.updatecolumn}/>
         <div className="component-name">
           <div className="center">
             <div className="title">{card.name}</div>
diff --git a/src/menu/components/table/edit-table/index.scss b/src/menu/components/table/edit-table/index.scss
index 227900c..60bc249 100644
--- a/src/menu/components/table/edit-table/index.scss
+++ b/src/menu/components/table/edit-table/index.scss
@@ -67,20 +67,11 @@
     background: rgba(255, 255, 255, 0.55);
   }
   .model-menu-action-list {
-    line-height: 55px;
+    line-height: 40px;
     padding: 0px;
     >.ant-row {
-      min-height: 30px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
+      padding-top: 10px;
+      min-height: 55px;
     }
   }
 
diff --git a/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx b/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
index e004eb9..b4c2f4f 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
+++ b/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
@@ -43,6 +43,9 @@
     value: 'picture',
     text: '鍥剧墖'
   }, {
+    value: 'video',
+    text: '瑙嗛'
+  }, {
     value: 'link',
     text: '閾炬帴'
   }, {
@@ -175,6 +178,26 @@
       }]
     },
     {
+      type: 'number',
+      key: 'startTime',
+      precision: 0,
+      label: '寮�濮嬫椂闂�',
+      initVal: card.startTime || 0,
+      tooltip: '瑙嗛寮�濮嬫挱鏀剧殑鏃堕棿锛岀敤浜庤皟鏁磋棰戝垵濮嬪寲灞曠ず鐨勭晫闈€��',
+      required: false
+    },
+    {
+      type: 'select',
+      key: 'aspectRatio',
+      label: '闀垮姣�',
+      initVal: card.aspectRatio || '16:9',
+      required: true,
+      options: [
+        { value: '4:3', text: '4:3' },
+        { value: '16:9', text: '16:9' }
+      ]
+    },
+    {
       type: 'radio',
       key: 'rowspan',
       label: '琛屽悎骞�',
@@ -202,7 +225,8 @@
       }, {
         value: 'false',
         text: '鍚�'
-      }]
+      }],
+      forbid: card.isSub
     },
     {
       type: 'number',
@@ -211,7 +235,7 @@
       max: 18,
       decimal: 0,
       label: '灏忔暟浣�',
-      initVal: card.decimal || 0,
+      initVal: card.decimal,
       required: false
     },
     {
@@ -335,7 +359,7 @@
         value: 'linkurl',
         text: '閾炬帴'
       }],
-      forbidden: appType === 'mob'
+      forbid: appType === 'mob'
     },
     {
       type: appType === 'pc' ? 'select' : 'cascader',
@@ -344,7 +368,7 @@
       initVal: card.linkmenu || (appType === 'pc' ? '' : []),
       required: true,
       options: menulist,
-      forbidden: appType === 'mob'
+      forbid: appType === 'mob'
     },
     {
       type: 'textarea',
@@ -352,7 +376,7 @@
       label: '閾炬帴鍦板潃',
       initVal: card.linkurl || '',
       required: true,
-      forbidden: appType === 'mob'
+      forbid: appType === 'mob'
     },
     {
       type: 'multiselect',
@@ -361,7 +385,7 @@
       initVal: card.linkfields || [],
       required: false,
       options: fields,
-      forbidden: appType === 'mob'
+      forbid: appType === 'mob'
     },
     {
       type: 'radio',
@@ -403,7 +427,7 @@
       initVal: card.blacklist || [],
       required: false,
       options: roleList,
-      forbidden: appType === 'mob'
+      forbid: appType === 'mob'
     }
   ]
 }
diff --git a/src/menu/components/table/normal-table/columns/editColumn/index.jsx b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
index cbb8ac4..a186e6e 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/index.jsx
+++ b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -15,6 +15,7 @@
   link: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'joint', 'Width', 'blacklist', 'nameField'],
   textarea: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'blacklist'],
   picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'span'],
+  video: ['label', 'field', 'type', 'Align', 'Hide', 'startTime', 'Width', 'blacklist', 'aspectRatio'],
   colspan: ['label', 'type', 'Align', 'Hide', 'blacklist'],
   custom: ['label', 'type', 'Align', 'Hide', 'Width', 'blacklist'],
   action: ['label', 'type', 'Align', 'Width'],
@@ -164,7 +165,7 @@
     if (!formlist) return null
 
     formlist.forEach((item, index) => {
-      if (item.hidden || item.forbidden) return
+      if (item.hidden || item.forbid) return
 
       if (item.type === 'text') {
         let rules = []
diff --git a/src/menu/components/table/normal-table/columns/index.jsx b/src/menu/components/table/normal-table/columns/index.jsx
index d6097f3..e976c72 100644
--- a/src/menu/components/table/normal-table/columns/index.jsx
+++ b/src/menu/components/table/normal-table/columns/index.jsx
@@ -13,7 +13,7 @@
 
 const { confirm } = Modal
 const { Paragraph } = Typography 
-const EditColumn = asyncIconComponent(() => import('./editColumn'))
+const EditColumn = asyncComponent(() => import('./editColumn'))
 const MarkColumn = asyncIconComponent(() => import('@/menu/components/share/markcomponent'))
 const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
 const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
@@ -54,8 +54,14 @@
     const { connectDragSource, connectDropTarget, moveCol, dropCol, addElement, updateCol, editColumn, pasteCell, changeStyle, deleteCol, index, column, align, fields, children, ...restProps } = this.props
 
     if (index !== undefined) {
+      let style = {cursor: 'move', textAlign: align}
+      if (column.Width) {
+        style.width = column.Width
+        style.minWidth = column.Width
+      }
+
       return connectDragSource(
-        connectDropTarget(<th {...restProps} index={index} style={{ cursor: 'move', textAlign: align }} onDoubleClick={() => column && this.props.editColumn(column)}>
+        connectDropTarget(<th {...restProps} index={index} style={style} onDoubleClick={() => column && this.props.editColumn(column)}>
           <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
             <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
               {column && ['custom', 'colspan', 'action'].includes(column.type) ?
@@ -74,8 +80,14 @@
         </th>),
       )
     } else if (column) {
+      let style = {textAlign: align}
+      if (column.Width) {
+        style.width = column.Width
+        style.minWidth = column.Width
+      }
+
       return (
-        <th {...restProps} key={column.uuid} onDoubleClick={() => this.props.editColumn(column)}>
+        <th {...restProps} style={style} key={column.uuid} onDoubleClick={() => this.props.editColumn(column)}>
           <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
             <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
               {column && ['custom', 'colspan'].includes(column.type) ?
@@ -152,13 +164,13 @@
 
     if (column && column.type === 'custom') {
       return (
-        <td style={{padding: 0, minWidth: column.Width || 100, ...(column.style || {})}} className={className}>
+        <td style={{padding: 0, ...(column.style || {})}} className={className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
     } else if (column && column.type === 'action') {
       return (
-        <td style={{padding: '0 5px', textAlign: column.Align, minWidth: column.Width || 100}} className={'action-column ' + className}>
+        <td style={{padding: '0 5px', textAlign: column.Align}} className={'action-column ' + className}>
           <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
         </td>
       )
@@ -174,7 +186,7 @@
         }
       }
       return (
-        <td style={{...style, minWidth: column.Width || 100}} className={className}>
+        <td style={{...style}} className={className}>
           {column.field ? <Paragraph className="col-copy" copyable={{ text: column.field }}>{val}</Paragraph> : val}
           {column.marks && column.marks.length ? <AntDesignOutlined className="profile"/> : null}
           {column.Hide === 'true' ? <CloseCircleOutlined className="close-col"/> : null}
@@ -277,6 +289,13 @@
     let _columns = fromJS(this.state.columns).toJS()
 
     let col = { focus: true, uuid: Utils.getuuid(), label: 'label', field: '', type: item.subType, elements: [] }
+    if (col.type === 'colspan') {
+      col.subcols = []
+    } else if (col.type === 'action') {
+      col.label = '鎿嶄綔'
+    } else if (col.type === 'index') {
+      col.label = '搴忓彿'
+    }
 
     _columns.splice(hoverIndex, 0, col)
 
@@ -290,7 +309,7 @@
   loopCol = (columns, col) => {
     return columns.map(column => {
       if (column.type === 'colspan') {
-        column.subcols = this.loopCol(column.subcols, col)
+        column.subcols = this.loopCol(column.subcols || [], col)
       }
       if (column.uuid === col.uuid) {
         return col
@@ -309,6 +328,10 @@
       let config = {...this.props.config, cols: _columns}
       if (btn) {
         config.action = config.action.filter(item => item.uuid !== btn.uuid)
+
+        setTimeout(() => {
+          MKEmitter.emit('revert', config.uuid)
+        }, 200)
       }
 
       this.props.updatecolumn(config)
@@ -422,7 +445,6 @@
   }
 
   deleteCol = (col) => {
-    const { appType } = this.state
     let _columns = fromJS(this.state.columns).toJS()
 
     _columns = this.loopDelCol(_columns, col)
@@ -432,19 +454,6 @@
     }, () => {
       this.props.updatecolumn({...this.props.config, cols: _columns})
     })
-
-    if (col.type !== 'action' || appType === 'mob') return
-
-    let uuids = []
-    col.elements && col.elements.forEach(c => {
-      if (appType === 'pc' && c.OpenType !== 'popview') return
-
-      uuids.push(c.uuid)
-    })
-
-    if (uuids.length === 0) return
-    
-    MKEmitter.emit('delButtons', uuids)
   }
 
   updateLineMarks = (vals) => {
@@ -607,7 +616,6 @@
           <Table
             rowKey="uuid"
             size={config.wrap.size || 'middle'}
-            rowClassName="editable-row"
             bordered={config.wrap.bordered !== 'false'}
             components={components}
             dataSource={this.state.data}
diff --git a/src/menu/components/table/normal-table/columns/index.scss b/src/menu/components/table/normal-table/columns/index.scss
index db07412..7c8baaa 100644
--- a/src/menu/components/table/normal-table/columns/index.scss
+++ b/src/menu/components/table/normal-table/columns/index.scss
@@ -10,6 +10,11 @@
     font-size: inherit;
     font-weight: inherit;
   }
+  .ant-table-selection-column {
+    width: 60px;
+    min-width: 60px;
+    max-width: 60px;
+  }
   .ant-table-body {
     overflow-x: auto;
     tr {
diff --git a/src/menu/components/table/normal-table/index.jsx b/src/menu/components/table/normal-table/index.jsx
index 4b65160..cd6df52 100644
--- a/src/menu/components/table/normal-table/index.jsx
+++ b/src/menu/components/table/normal-table/index.jsx
@@ -131,7 +131,6 @@
       if (appType === 'mob') {
         _card.search = []
       }
-
       this.setState({
         card: _card
       })
@@ -139,7 +138,6 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('submitModal', this.handleSave)
     MKEmitter.addListener('completeSave', this.completeSave)
   }
 
@@ -154,7 +152,6 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('submitModal', this.handleSave)
     MKEmitter.removeListener('completeSave', this.completeSave)
   }
 
@@ -283,7 +280,7 @@
     style.fontSize = card.wrap.fontSize || 14
     style.fontWeight = card.wrap.fontWeight || 'normal'
 
-    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow'], style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['font1', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -369,26 +366,6 @@
     }
   }
 
-  handleSave = (_cards, btn, modal) => {
-    let card = fromJS(this.state.card).toJS()
-
-    if (card.uuid !== _cards.uuid) return
-
-    let _index = card.action.findIndex(cell => cell.uuid === btn.uuid)
-
-    if (_index === -1) return
-
-    card.action = card.action.map(cell => {
-      if (cell.uuid === btn.uuid) {
-        cell.modal = modal
-      }
-
-      return cell
-    })
-
-    this.updateComponent(card)
-  }
-
   getWrapForms = () => {
     const { wrap, action, columns, cols } = this.state.card
 
@@ -419,6 +396,33 @@
     this.updateComponent({...card, wrap: res})
   }
 
+  updatecolumn = (config) => {
+    config.absFields = []
+    config.cols.forEach(col => {
+      if (col.type === 'number') {
+        if (col.format === 'abs') {
+          config.absFields.push(col.field)
+        }
+      } else if (col.type === 'colspan' && col.subcols) {
+        col.subcols.forEach(scol => {
+          if (scol.type === 'number') {
+            if (scol.format === 'abs') {
+              config.absFields.push(scol.field)
+            }
+          }
+        })
+      }
+    })
+
+    if (config.absFields.length) {
+      config.absFields = Array.from(new Set(config.absFields))
+    } else {
+      config.absFields = null
+    }
+
+    this.updateComponent(config)
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
diff --git a/src/menu/components/table/normal-table/index.scss b/src/menu/components/table/normal-table/index.scss
index 7db4e8e..c5c5f42 100644
--- a/src/menu/components/table/normal-table/index.scss
+++ b/src/menu/components/table/normal-table/index.scss
@@ -67,21 +67,11 @@
     background: rgba(255, 255, 255, 0.55);
   }
   .model-menu-action-list {
-    line-height: 55px;
+    line-height: 40px;
     padding: 0px;
-    min-height: 55px;
     >.ant-row {
-      min-height: 30px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
+      padding-top: 10px;
+      min-height: 55px;
     }
   }
 
diff --git a/src/menu/components/tabs/antv-tabs/index.jsx b/src/menu/components/tabs/antv-tabs/index.jsx
index 80cd4c0..523c8b2 100644
--- a/src/menu/components/tabs/antv-tabs/index.jsx
+++ b/src/menu/components/tabs/antv-tabs/index.jsx
@@ -10,7 +10,6 @@
 import MkIcon from '@/components/mk-icon'
 import DraggableTabs from './dragabletabs'
 import { resetStyle } from '@/utils/utils-custom.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import { getTabForm, getTabsSetForm } from './options'
 import './index.scss'
@@ -31,7 +30,6 @@
   }
 
   state = {
-    appType: sessionStorage.getItem('appType'),
     tabs: null,
     editab: null,
     defaultActiveKey: ''
@@ -84,7 +82,7 @@
   changeStyle = () => {
     const { tabs } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], tabs.style, this.getStyle)
   }
 
   getStyle = (style) => {
@@ -153,18 +151,12 @@
 
     tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
 
-    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
-
     confirm({
       title: '纭畾鍒犻櫎鏍囩锛�',
       content: '',
       onOk() {
         _this.setState({tabs})
         _this.props.updateConfig(tabs)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -227,7 +219,6 @@
 
     editab.label = res.label
     editab.icon = res.icon
-    // editab.hasSearch = res.hasSearch || ''
     editab.hide = res.hide || 'false'
     editab.backgroundColor = res.backgroundColor
     editab.controlVal = res.controlVal || ''
diff --git a/src/menu/components/tabs/antv-tabs/options.jsx b/src/menu/components/tabs/antv-tabs/options.jsx
index bc2db9d..be2cd65 100644
--- a/src/menu/components/tabs/antv-tabs/options.jsx
+++ b/src/menu/components/tabs/antv-tabs/options.jsx
@@ -34,19 +34,6 @@
       required: false,
       allowClear: true,
     },
-    // {
-    //   type: 'radio',
-    //   field: 'hasSearch',
-    //   label: '鎼滅储',
-    //   initval: tab.hasSearch || 'false',
-    //   required: false,
-    //   options: [
-    //     {value: 'false', label: '鏃�'},
-    //     {value: 'icon', label: '鏈�'},
-    //   ],
-    //   forbid: appType !== 'mob' || setting.position !== 'top' || setting.display !== 'inline-block',
-    //   span: 22
-    // },
     {
       type: 'text',
       field: 'controlVal',
@@ -102,7 +89,7 @@
   let appType = sessionStorage.getItem('appType')
   let roleList = sessionStorage.getItem('sysRoles')
 
-  let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, uuid) || []
+  let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, uuid, window.GLOB.customMenu.interfaces)
   modules.push({
     value: 'preview',
     label: '涓婁竴椤碉紙url鍙傛暟锛�'
diff --git a/src/menu/components/tabs/paste/index.jsx b/src/menu/components/tabs/paste/index.jsx
index 5fadf9c..d8ae618 100644
--- a/src/menu/components/tabs/paste/index.jsx
+++ b/src/menu/components/tabs/paste/index.jsx
@@ -4,7 +4,6 @@
 import { SnippetsOutlined } from '@ant-design/icons'
 
 import MenuUtils from '@/utils/utils-custom.js'
-import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 // import './index.scss'
 
@@ -24,7 +23,7 @@
     this.setState({visible: true})
   }
 
-  resetconfig = (item, copyBtns, uuids = {}) => {
+  resetconfig = (item, uuids = {}) => {
     if (item.type === 'tabs') {
       uuids[item.uuid] = MenuUtils.getuuid()
       item.uuid = uuids[item.uuid]
@@ -36,7 +35,7 @@
         tab.uuid = uuids[tab.uuid]
 
         tab.components = tab.components.map(cell => {
-          cell = this.resetconfig(cell, copyBtns, uuids)
+          cell = this.resetconfig(cell, uuids)
           return cell
         })
       })
@@ -47,12 +46,12 @@
       item.name = item.setting.name
 
       item.components = item.components.map(cell => {
-        cell = MenuUtils.resetComponentConfig(cell, copyBtns, uuids)
+        cell = MenuUtils.resetComponentConfig(cell, uuids)
 
         return cell
       })
     } else {
-      item = MenuUtils.resetComponentConfig(item, copyBtns, uuids)
+      item = MenuUtils.resetComponentConfig(item, uuids)
     }
 
     return item
@@ -60,15 +59,18 @@
 
   pasteSubmit = () => {
     const { Tab } = this.props
-    let options = ['tabs', 'group', 'datacard', 'propcard', 'timeline', 'balcony', 'normaltable', 'mainsearch', 'simpleform', 'stepform', 'tabform', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter', 'chart']
+    let appType = sessionStorage.getItem('appType')
+    let options = ['tabs', 'group', 'datacard', 'propcard', 'timeline', 'balcony', 'normaltable', 'mainsearch', 'simpleform', 'stepform', 'tabform', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter', 'chart', 'sandbox']
     let types = {
       login: '鐧诲綍',
       navbar: '瀵艰埅鏍�',
       topbar: '瀵艰埅鏍�'
     }
 
-    if (sessionStorage.getItem('appType') === 'mob') {
+    if (appType === 'mob') {
       options.push('menubar')
+    } else {
+      options.push('editable')
     }
 
     this.pasteFormRef.handleConfirm().then(res => {
@@ -88,19 +90,11 @@
         return
       }
 
-      let copyBtns = new Map()
-
-      res = this.resetconfig(res, copyBtns, {})
+      res = this.resetconfig(res, {})
 
       delete res.copyType
       
       this.props.insert(res, Tab)
-
-      copyBtns = [...copyBtns.values()]
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       this.setState({visible: false})
 
diff --git a/src/menu/components/tabs/tabcomponents/card.jsx b/src/menu/components/tabs/tabcomponents/card.jsx
index e907928..5827eb9 100644
--- a/src/menu/components/tabs/tabcomponents/card.jsx
+++ b/src/menu/components/tabs/tabcomponents/card.jsx
@@ -61,6 +61,10 @@
     style = { opacity: 0.3}
   }
 
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
+  }
+
   const getCardComponent = () => {
     if (card.type === 'bar' || card.type === 'line') {
       return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
@@ -90,7 +94,7 @@
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
       return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
diff --git a/src/menu/components/tabs/tabcomponents/index.jsx b/src/menu/components/tabs/tabcomponents/index.jsx
index ccecb5f..76caf18 100644
--- a/src/menu/components/tabs/tabcomponents/index.jsx
+++ b/src/menu/components/tabs/tabcomponents/index.jsx
@@ -5,8 +5,6 @@
 import { Empty, notification, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -48,17 +46,11 @@
       })
     }
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
       onOk() {
         handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
diff --git a/src/menu/components/tabs/table-tabs/dragabletabs.jsx b/src/menu/components/tabs/table-tabs/dragabletabs.jsx
index fe563ff..d1dc987 100644
--- a/src/menu/components/tabs/table-tabs/dragabletabs.jsx
+++ b/src/menu/components/tabs/table-tabs/dragabletabs.jsx
@@ -22,6 +22,7 @@
     } else if (!dragKey) {
       monitor.getItem().added = true
       props.tabsDrop(hoverKey)
+      return
     }
 
     props.moveTabNode(dragKey, hoverKey)
diff --git a/src/menu/components/tabs/table-tabs/index.jsx b/src/menu/components/tabs/table-tabs/index.jsx
index d5cfa87..293312c 100644
--- a/src/menu/components/tabs/table-tabs/index.jsx
+++ b/src/menu/components/tabs/table-tabs/index.jsx
@@ -4,20 +4,16 @@
 import { Tabs, Popover, Modal } from 'antd'
 import { PlusOutlined, CloseOutlined, EditOutlined, DeleteOutlined, ToolOutlined } from '@ant-design/icons'
 
-import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import MkIcon from '@/components/mk-icon'
 import DraggableTabs from './dragabletabs'
-import { resetStyle } from '@/utils/utils-custom.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import { getTabForm } from './options'
 import './index.scss'
 
 const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
-// const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
-const PasteComponent = asyncIconComponent(() => import('../paste'))
+const PasteBaseTable = asyncIconComponent(() => import('@/menu/components/share/pastebasetable'))
 const BaseTable = asyncComponent(() => import('@/menu/components/table/base-table'))
 
 const { TabPane } = Tabs
@@ -32,7 +28,8 @@
 
   state = {
     tabs: null,
-    editab: null
+    editab: null,
+    sign: 1
   }
 
   UNSAFE_componentWillMount () {
@@ -42,13 +39,11 @@
       let _tabs = {
         uuid: tabs.uuid,
         type: tabs.type,
-        subtype: tabs.subtype,
-        width: 24,
-        name: tabs.name,
+        subtype: 'tabletabs',
         setting: {},
-        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
+        style: {},
         subtabs: [
-          { uuid: Utils.getuuid(), label: '瀛愯〃1', icon: '', components: [{uuid: Utils.getuuid(), type: 'table', subtype: 'basetable', isNew: true}] },
+          { uuid: Utils.getuuid(), label: '瀛愯〃1', icon: '', components: [{uuid: Utils.getuuid(), name: '瀛愯〃1', type: 'table', subtype: 'basetable', isNew: true}] },
         ]
       }
       this.setState({
@@ -66,8 +61,6 @@
     return !is(fromJS(this.state), fromJS(nextState))
   }
 
-  componentDidMount () {}
-
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
    */
@@ -77,54 +70,11 @@
     }
   }
 
-  changeStyle = () => {
-    const { tabs } = this.state
-
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style, this.getStyle)
-  }
-
-  getStyle = (style) => {
-    let _card = {...this.state.tabs, style}
-
-    this.setState({
-      tabs: _card
-    })
-    
-    this.props.updateConfig(_card)
-  }
-
-  updateComponent = (component) => {
-    component.width = component.setting.width
-    component.name = component.setting.name
-
-    this.setState({
-      tabs: component
-    })
-    this.props.updateConfig(component)
-  }
-
-  updateTabComponent = (tab) => {
-    let tabs = fromJS(this.state.tabs).toJS()
-
-    tabs.subtabs = tabs.subtabs.map(t => {
-      if (t.uuid === tab.uuid) {
-        return tab
-      } else {
-        return t
-      }
-    })
-
-    this.setState({tabs})
-    this.props.updateConfig(tabs)
-  }
-
   delTab = (tab) => {
     let tabs = fromJS(this.state.tabs).toJS()
     const _this = this
 
     tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
-
-    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
 
     confirm({
       title: '纭畾鍒犻櫎鏍囩锛�',
@@ -132,10 +82,6 @@
       onOk() {
         _this.setState({tabs})
         _this.props.updateConfig(tabs)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -161,34 +107,68 @@
   }
 
   dropTable = (hoverKey, dragKey) => {
-    // let tabs = fromJS(this.state.tabs).toJS()
-    // let subtab = {}
-    // tabs.subtabs.forEach(item => {
-    //   subtab[item.uuid] = item
-    // })
+    if (dragKey) {
+      this.props.switchConfig(dragKey, hoverKey, () => {
+        this.setState({sign: this.state.sign + 1}, () => {
+          setTimeout(() => {
+            let node = document.getElementById(dragKey)
+            node && node.click()
+          }, 200)
+        })
+      })
+    } else {
+      let tabs = fromJS(this.state.tabs).toJS()
+      let name = '瀛愯〃' + (tabs.subtabs.length + 1)
+      let tab = { uuid: Utils.getuuid(), label: name, icon: '', components: [{uuid: Utils.getuuid(), type: 'table', name: name, subtype: 'basetable', isNew: true}]}
 
-    // tabs.subtabs = []
+      const hoverIndex = tabs.subtabs.findIndex(item => item.uuid === hoverKey)
 
-    // order.forEach(item => {
-    //   if (subtab[item]) {
-    //     tabs.subtabs.push(subtab[item])
-    //   }
-    // })
+      if (hoverIndex === -1) {
+        tabs.subtabs.push(tab)
+      } else {
+        tabs.subtabs.splice(hoverIndex + 1, 0, tab)
+      }
 
-    // this.setState({tabs})
-    // this.props.updateConfig(tabs)
+      this.setState({tabs}, () => {
+        setTimeout(() => {
+          let node = document.getElementById(tab.uuid)
+          node && node.click()
+        }, 200)
+      })
+      this.props.updateConfig(tabs)
+    }
   }
 
-  insert = (item, tab) => {
+  plusTable = () => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let name = '瀛愯〃' + (tabs.subtabs.length + 1)
+    let tab = { uuid: Utils.getuuid(), label: name, icon: '', components: [{uuid: Utils.getuuid(), type: 'table', name: name, subtype: 'basetable', isNew: true}]}
+
+    tabs.subtabs.push(tab)
+
+    this.setState({tabs}, () => {
+      setTimeout(() => {
+        let node = document.getElementById(tab.uuid)
+        node && node.click()
+      }, 200)
+    })
+    this.props.updateConfig(tabs)
+  }
+
+  insert = (item) => {
     let tabs = fromJS(this.state.tabs).toJS()
 
-    tabs.subtabs.forEach(stab => {
-      if (stab.uuid === tab.uuid) {
-        stab.components.push(item)
-      }
-    })
+    let name = item.name || ('瀛愯〃' + (tabs.subtabs.length + 1))
+    let tab = { uuid: Utils.getuuid(), label: name, icon: '', components: [item]}
 
-    this.setState({tabs})
+    tabs.subtabs.push(tab)
+
+    this.setState({tabs}, () => {
+      setTimeout(() => {
+        let node = document.getElementById(tab.uuid)
+        node && node.click()
+      }, 200)
+    })
     this.props.updateConfig(tabs)
   }
 
@@ -218,10 +198,8 @@
     editab.label = res.label
     editab.icon = res.icon
     editab.hide = res.hide || 'false'
-    editab.backgroundColor = res.backgroundColor
-    editab.controlVal = res.controlVal || ''
-    editab.selectVal = res.selectVal || ''
-    editab.blacklist = res.blacklist
+    editab.permission = res.permission || 'false'
+    editab.components[0].name = res.label
 
     if (editab.uuid) {
       tabs.subtabs = tabs.subtabs.map(t => {
@@ -244,18 +222,22 @@
     this.props.updateConfig(tabs)
   }
 
-  updateConfig = () => {
+  updateConfig = (tb, i) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    tabs.subtabs[i].components = [tb]
 
+    this.setState({ tabs })
+
+    this.props.updateConfig(tabs)
   }
 
   render() {
     const { tabs } = this.state
-    let _style = resetStyle(tabs.style)
 
     return (
-      <div className={'menu-tabs-edit-box ' + (tabs.setting.display || '')} style={_style} id={tabs.uuid}>
+      <div className="table-tabs-edit-box" style={tabs.style} id={tabs.uuid}>
         <DraggableTabs tabsMove={this.moveSwitch} tabsDrop={this.dropTable}>
-          {tabs.subtabs.map(tab => (
+          {tabs.subtabs.map((tab, i) => (
             <TabPane tab={
               <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                 <div className="mk-popover-control">
@@ -265,17 +247,17 @@
                   <CloseOutlined className="close" onClick={() => this.delTab(tab)} />
                 </div>
               } trigger="hover">
-                <span style={{textDecoration: tab.hide === 'true' ? 'line-through' : 'none'}}>{tab.icon ? <MkIcon type={tab.icon} /> : null}{tab.label}</span>
+                <span id={tab.uuid} style={{textDecoration: tab.hide === 'true' ? 'line-through' : 'none'}}>{tab.icon ? <MkIcon type={tab.icon} /> : null}{tab.label}</span>
               </Popover>
             } key={tab.uuid}>
-              <BaseTable card={tab.components[0]} updateConfig={this.updateConfig} />
+              <BaseTable card={tab.components[0]} updateConfig={(tb) => this.updateConfig(tb, i)} />
             </TabPane>
           ))}
         </DraggableTabs>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <PlusOutlined className="plus" title="娣诲姞瀛愯〃"/>
-            <PasteComponent insert={this.insert} />
+            <PlusOutlined className="plus" title="娣诲姞瀛愯〃" onClick={this.plusTable}/>
+            <PasteBaseTable insert={this.insert} />
             <DeleteOutlined className="close" onClick={() => this.props.deletecomponent(tabs.uuid)} />
           </div>
         } trigger="hover">
diff --git a/src/menu/components/tabs/table-tabs/index.scss b/src/menu/components/tabs/table-tabs/index.scss
index 9f77d18..af40884 100644
--- a/src/menu/components/tabs/table-tabs/index.scss
+++ b/src/menu/components/tabs/table-tabs/index.scss
@@ -1,11 +1,4 @@
-.menu-tabs-edit-box {
-  position: relative;
-  box-sizing: border-box;
-  background: #ffffff;
-  background-position: center center;
-  background-repeat: no-repeat;
-  background-size: cover;
-
+.table-tabs-edit-box {
   .ant-tabs-top-bar {
     margin-bottom: 0;
   }
diff --git a/src/menu/components/tabs/table-tabs/options.jsx b/src/menu/components/tabs/table-tabs/options.jsx
index 3793f20..77e10ac 100644
--- a/src/menu/components/tabs/table-tabs/options.jsx
+++ b/src/menu/components/tabs/table-tabs/options.jsx
@@ -2,18 +2,6 @@
  * @description tab琛ㄥ崟閰嶇疆淇℃伅
  */
 export function getTabForm(tab) {
-  let roleList = sessionStorage.getItem('sysRoles')
-
-  if (roleList) {
-    try {
-      roleList = JSON.parse(roleList)
-    } catch (e) {
-      roleList = []
-    }
-  } else {
-    roleList = []
-  }
-
   const tabForm = [
     {
       type: 'text',
@@ -43,13 +31,16 @@
       ],
     },
     {
-      type: 'multiselect',
-      field: 'blacklist',
-      label: '榛戝悕鍗�',
-      initval: tab.blacklist || [],
+      type: 'radio',
+      field: 'permission',
+      label: '鏉冮檺楠岃瘉',
+      initval: tab.permission || 'false',
       required: false,
-      options: roleList
-    },
+      options: [
+        {value: 'true', label: '鍚敤'},
+        {value: 'false', label: '绂佺敤'},
+      ]
+    }
   ]
 
   return tabForm
diff --git a/src/menu/components/timeline/normal-timeline/index.jsx b/src/menu/components/timeline/normal-timeline/index.jsx
index 7dda5d2..237e4fa 100644
--- a/src/menu/components/timeline/normal-timeline/index.jsx
+++ b/src/menu/components/timeline/normal-timeline/index.jsx
@@ -28,8 +28,7 @@
   }
 
   state = {
-    card: null,
-    back: false
+    card: null
   }
 
   UNSAFE_componentWillMount () {
@@ -166,7 +165,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/menu/components/timeline/normal-timeline/index.scss b/src/menu/components/timeline/normal-timeline/index.scss
index 3e27678..dffd8b4 100644
--- a/src/menu/components/timeline/normal-timeline/index.scss
+++ b/src/menu/components/timeline/normal-timeline/index.scss
@@ -50,24 +50,6 @@
     right: -30px;
     font-size: 16px;
   }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
 }
 .menu-timeline-edit-box::after {
   display: block;
diff --git a/src/menu/components/tree/antd-tree/index.jsx b/src/menu/components/tree/antd-tree/index.jsx
index 04211aa..3dd7abd 100644
--- a/src/menu/components/tree/antd-tree/index.jsx
+++ b/src/menu/components/tree/antd-tree/index.jsx
@@ -129,7 +129,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['height', 'background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/menu/components/tree/antd-tree/options.jsx b/src/menu/components/tree/antd-tree/options.jsx
index ee531ff..462aa86 100644
--- a/src/menu/components/tree/antd-tree/options.jsx
+++ b/src/menu/components/tree/antd-tree/options.jsx
@@ -34,16 +34,16 @@
     {
       type: 'select',
       field: 'valueField',
-      label: 'Value',
+      label: '鍊煎瓧娈�',
       initval: wrap.valueField || '',
-      tooltip: '鏁版嵁鍊煎瓧娈点��',
+      tooltip: '鏁版嵁鍊煎瓧娈碉紝鑺傜偣ID鍊硷紝鍞竴鏍囪瘑銆�',
       required: true,
       options: columns
     },
     {
       type: 'select',
       field: 'labelField',
-      label: 'Label',
+      label: '鏂囨湰瀛楁',
       initval: wrap.labelField || '',
       tooltip: '鏄剧ず鏂囧瓧瀛楁銆�',
       required: true,
@@ -52,9 +52,9 @@
     {
       type: 'select',
       field: 'parentField',
-      label: 'Parent',
+      label: '涓婄骇瀛楁',
       initval: wrap.parentField || '',
-      tooltip: '鐖剁骇瀛楁銆�',
+      tooltip: '涓婄骇瀛楁锛岀敤浜庣粍缁囨暟鎹殑涓婁笅绾у叧绯汇��',
       required: true,
       options: columns
     },
@@ -63,7 +63,7 @@
       field: 'mark',
       label: '椤剁骇鏍囪瘑',
       initval: wrap.mark || '',
-      tooltip: '鐖剁骇瀛楁鍊间笌椤剁骇鏍囪瘑鐩稿悓鏃讹紝瑙嗕负椤剁骇鑺傜偣銆�',
+      tooltip: '涓婄骇瀛楁鍊间笌椤剁骇鏍囪瘑鐩稿悓鏃讹紝瑙嗕负椤剁骇鑺傜偣銆�',
       required: false
     },
     {
diff --git a/src/menu/datasource/index.jsx b/src/menu/datasource/index.jsx
index e0bb557..52a1544 100644
--- a/src/menu/datasource/index.jsx
+++ b/src/menu/datasource/index.jsx
@@ -5,7 +5,6 @@
 import { SettingOutlined } from '@ant-design/icons'
 
 import VerifyCard from './verifycard'
-import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
 class DataSource extends Component {
@@ -140,8 +139,6 @@
       visible: true,
       mainSearch: search
     })
-
-    MKEmitter.emit('modalStatus', '鏁版嵁婧�')
   }
 
   verifySubmit = () => {
@@ -187,14 +184,22 @@
             }
           })
         })
+      } else {
+        res.scripts.forEach(item => {
+          if (item.status === 'false') return
+
+          if (/exec\s/ig.test(item.sql)) {
+            maxScript = 1000
+          } else if (item.sql.length > maxScript) {
+            maxScript = item.sql.length
+          }
+        })
       }
 
       res.setting.maxScript = maxScript
 
       this.setState({loading: false, visible: false})
       this.props.updateConfig({...config, ...res})
-
-      MKEmitter.emit('modalStatus', false)
     }, () => {
       this.setState({loading: false})
     })
@@ -216,7 +221,7 @@
           okText="鎻愪氦"
           onOk={this.verifySubmit}
           confirmLoading={loading}
-          onCancel={() => { MKEmitter.emit('modalStatus', false);this.setState({ visible: false }) }}
+          onCancel={() => {this.setState({ visible: false }) }}
           destroyOnClose
         >
           <VerifyCard
diff --git a/src/menu/datasource/verifycard/customscript/index.jsx b/src/menu/datasource/verifycard/customscript/index.jsx
index 9bd1aaa..a673fea 100644
--- a/src/menu/datasource/verifycard/customscript/index.jsx
+++ b/src/menu/datasource/verifycard/customscript/index.jsx
@@ -100,6 +100,15 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
+
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
 
         let _quot = values.sql.match(/'{1}/g)
@@ -151,7 +160,9 @@
             loading: false
           })
 
-          MKEmitter.emit('editLineId', values.uuid)
+          if (values.uuid) {
+            MKEmitter.emit('editLineId', values.uuid)
+          }
 
           this.props.form.setFieldsValue({
             sql: ' '
@@ -264,7 +275,7 @@
                     message: '璇疯緭鍏ql!'
                   }
                 ]
-              })(<CodeMirror />)}
+              })(<CodeMirror/>)}
             </Form.Item>
           </Col>
         </Row>
diff --git a/src/menu/datasource/verifycard/index.jsx b/src/menu/datasource/verifycard/index.jsx
index 66a6a93..59ae132 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -14,6 +14,7 @@
 import SettingForm from './settingform'
 import PasteForm from '@/templates/zshare/pasteform'
 import SettingUtils from './utils'
+import MKEmitter from '@/utils/events.js'
 import MinView from '@/assets/img/minview.png'
 import './index.scss'
 
@@ -354,6 +355,7 @@
     } else {
       values.uuid = Utils.getuuid()
       scripts.push(values)
+      MKEmitter.emit('editLineId', values.uuid)
     }
 
     this.setState({ scripts })
@@ -739,6 +741,7 @@
   }
 
   pasteSubmit = () => {
+    const { config } = this.props
     const { setting } = this.state
 
     this.pasteFormRef.handleConfirm().then(res => {
@@ -762,21 +765,35 @@
         res.data.setting.supModule = setting.supModule
       }
 
-      this.setState({
-        scripts: res.data.scripts.map(col => {
-          col.uuid = Utils.getuuid()
-          return col
-        }),
-        columns: res.data.columns.map(col => {
-          col.uuid = Utils.getuuid()
-          return col
-        }),
-        setting: res.data.setting,
-        reload: true,
-        pvisible: false
-      }, () => {
-        this.setState({reload: false})
-      })
+      if (config.subtype !== 'basetable') {
+        this.setState({
+          scripts: res.data.scripts.map(col => {
+            col.uuid = Utils.getuuid()
+            return col
+          }),
+          columns: res.data.columns.map(col => {
+            col.uuid = Utils.getuuid()
+            return col
+          }),
+          setting: res.data.setting,
+          reload: true,
+          pvisible: false
+        }, () => {
+          this.setState({reload: false})
+        })
+      } else {
+        this.setState({
+          scripts: res.data.scripts.map(col => {
+            col.uuid = Utils.getuuid()
+            return col
+          }),
+          setting: res.data.setting,
+          reload: true,
+          pvisible: false
+        }, () => {
+          this.setState({reload: false})
+        })
+      }
     })
   }
 
@@ -834,7 +851,7 @@
               wrappedComponentRef={(inst) => this.settingForm = inst}
             /> : null}
           </TabPane>
-          <TabPane tab={
+          {config.subtype !== 'basetable' ? <TabPane tab={
             <span>
               瀛楁闆�
               {columns.length ? <span className="count-tip">{columns.length}</span> : null}
@@ -848,7 +865,7 @@
               updatefield={this.updatefields}
             />
             <EditTable actions={['edit', 'move', 'copy', 'del', 'clear']} type="datasourcefield" wrappedComponentRef={(inst) => this.datasource = inst} data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
-          </TabPane>
+          </TabPane> : null}
           <TabPane tab={
             <span>
               鑷畾涔夎剼鏈�
@@ -895,36 +912,50 @@
               title = title && title[0] ? title[0] : ''
               let _text = title ? item.sql.replace(title, '') : item.sql
 
-              return (
-                <div className={'script-item ' + (script && script.uuid === item.uuid ? 'active' : '') } key={item.uuid}>
-                  <div style={{cursor: 'pointer'}} onClick={() => {
-                    this.setState({script: item, scriptValue: item.sql})
-                  }}>
-                    {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
-                    <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
-                    <div>{item.status === 'false' ?
-                      <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
-                        绂佺敤
-                        <StopOutlined style={{marginLeft: '5px'}} />
-                      </span> : 
-                      <span style={{color: '#26C281', marginLeft: '20px'}}>
-                        鍚敤
-                        <CheckCircleOutlined style={{marginLeft: '5px'}}/>
-                      </span>}
+              if (item.status === 'false') {
+                return (
+                  <div className="script-item" key={item.uuid}>
+                    <div style={{cursor: 'not-allowed'}}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>
+                        <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
+                          绂佺敤
+                          <StopOutlined style={{marginLeft: '5px'}} />
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{height: '24px'}}></div>
+                  </div>
+                )
+              } else {
+                return (
+                  <div className={'script-item ' + (script && script.uuid === item.uuid ? 'active' : '') } key={item.uuid}>
+                    <div style={{cursor: 'pointer'}} onClick={() => {
+                      this.setState({script: item, scriptValue: item.sql})
+                    }}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>
+                        <span style={{color: '#26C281', marginLeft: '20px'}}>
+                          鍚敤
+                          <CheckCircleOutlined style={{marginLeft: '5px'}}/>
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{textAlign: 'right'}}>
+                      <span className="operation-btn" onClick={() => this.handleStatus(item)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
+                      <Popconfirm
+                        overlayClassName="popover-confirm"
+                        title="纭畾鍒犻櫎鍚�?"
+                        onConfirm={() => this.deleteScript(item)
+                      }>
+                        <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
+                      </Popconfirm>
                     </div>
                   </div>
-                  <div style={{textAlign: 'right'}}>
-                    <span className="operation-btn" onClick={() => this.handleStatus(item)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
-                    <Popconfirm
-                      overlayClassName="popover-confirm"
-                      title="纭畾鍒犻櫎鍚�?"
-                      onConfirm={() => this.deleteScript(item)
-                    }>
-                      <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
-                    </Popconfirm>
-                  </div>
-                </div>
-              )
+                )
+              }
             })}
           </div>
           <div className="script-button">
diff --git a/src/menu/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index 69041eb..923a39e 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -8,6 +8,7 @@
 import Utils from '@/utils/utils.js'
 import MenuUtils from '@/utils/utils-custom.js'
 import CodeMirror from '@/templates/zshare/codemirror'
+import KeyInterface from '@/components/keyInterface'
 import './index.scss'
 
 const { TextArea } = Input
@@ -16,7 +17,6 @@
   static propTpyes = {
     config: PropTypes.object,     // 缁勪欢閰嶇疆
     setting: PropTypes.object,    // 鏁版嵁婧愰厤缃�
-    modules: PropTypes.array,     // 鍙粦瀹氱殑涓婄骇缁勪欢
     columns: PropTypes.array,     // 鍒楄缃�
     scripts: PropTypes.array,     // 鑷畾涔夎剼鏈�
   }
@@ -26,13 +26,18 @@
     modules: [],
     innerRules: [],
     innertip: '',
-    appType: sessionStorage.getItem('appType')
+    ismain: false
   }
 
   UNSAFE_componentWillMount () {
     const { config, setting } = this.props
 
-    let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, config.uuid) || []
+    let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, config.uuid, window.GLOB.customMenu.interfaces)
+    let ismain = false
+
+    if (window.GLOB.customMenu.Template === 'BaseTable') {
+      ismain = config.name === '涓昏〃'
+    }
 
     modules.unshift({
       value: 'empty',
@@ -69,20 +74,22 @@
     _setting.supModule = setting.supModule || []
     _setting.execute = setting.execute || 'true'
 
-    this.setState({modules, innerRules, innertip, setting: _setting})
+    if (_setting.supModule.length > 0 && _setting.supModule[0] !== 'empty') {
+      let has = MenuUtils.checkSupModules(modules, _setting.supModule.slice(-1)[0])
+      if (!has) {
+        _setting.supModule = []
+      }
+    }
+
+    this.setState({modules, innerRules, innertip, setting: _setting, ismain})
   }
 
   handleConfirm = () => {
-    const { appType } = this.state
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     return new Promise((resolve, reject) => {
       this.props.form.validateFieldsAndScroll((err, values) => {
         if (!err) {
           values.sync = values.sync || 'false'
-
-          if (appType === 'mob' && values.useMSearch === 'true') {
-            values.syncRefresh = 'true'
-          }
 
           // 鏁版嵁婧愬墠绔獙璇�
           if (values.interType === 'system' && values.execute !== 'false' && values.dataresource) {
@@ -155,7 +162,7 @@
   render() {
     const { columns, config } = this.props
     const { getFieldDecorator } = this.props.form
-    const { setting, modules, innerRules, appType, innertip } = this.state
+    const { setting, modules, innerRules, innertip } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -225,19 +232,25 @@
               </Form.Item>
             </Col>
             {setting.interType === 'outer' ? <Col span={8}>
-              <Form.Item label="绯荤粺鎺ュ彛">
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="涓氬姟绯荤粺鎸囧悓涓�sso涓嬬殑鍏朵粬涓氬姟绯荤粺锛屽閮ㄧ郴缁熸寚涓嶅悓sso涓嬬殑鍏朵粬涓氬姟绯荤粺銆�">
+                  <QuestionCircleOutlined className="mk-form-tip" />
+                  绯荤粺绫诲瀷
+                </Tooltip>
+              }>
                 {getFieldDecorator('sysInterface', {
                   initialValue: setting.sysInterface,
                   rules: [
                     {
                       required: true,
-                      message: '璇烽�夋嫨鏄惁浣跨敤绯荤粺鎺ュ彛!'
+                      message: '璇烽�夋嫨绯荤粺绫诲瀷!'
                     }
                   ]
                 })(
                 <Radio.Group onChange={(e) => {this.onOptionChange(e.target.value, 'sysInterface')}}>
-                  <Radio value="true">鏄�</Radio>
-                  <Radio value="false">鍚�</Radio>
+                  <Radio value="false">涓氬姟</Radio>
+                  <Radio value="true">鍗曠偣</Radio>
+                  <Radio value="external">澶栭儴</Radio>
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
@@ -260,7 +273,7 @@
                 })(<Input placeholder={''} autoComplete="off" />)}
               </Form.Item>
             </Col> : null}
-            {setting.interType === 'outer' ? <Col className="outer-interface" span={24}>
+            {setting.interType === 'outer' && setting.sysInterface !== 'external' ? <Col className="outer-interface" span={24}>
               <Form.Item label="鎺ュ彛鍦板潃">
                 {getFieldDecorator('interface', {
                   initialValue: setting.interface || (setting.sysInterface === 'true' ? window.GLOB.mainSystemApi : ''),
@@ -273,8 +286,21 @@
                 })(<TextArea rows={2} readOnly={setting.sysInterface === 'true'}/>)}
               </Form.Item>
             </Col> : null}
-            {setting.interType === 'outer' && setting.sysInterface !== 'true' ? <Col className="outer-interface" span={24}>
-              <Form.Item label={<Tooltip placement="topLeft" title="姝e紡绯荤粺鎺ュ彛鍦板潃锛屼负绌烘椂浣跨敤鎺ュ彛鍦板潃">
+            {setting.interType === 'outer' && setting.sysInterface === 'external' ? <Col className="outer-interface" span={24}>
+              <Form.Item label="鎺ュ彛鍦板潃">
+                {getFieldDecorator('exInterface', {
+                  initialValue: setting.exInterface,
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇风紪杈戞帴鍙e湴鍧�!'
+                    }
+                  ]
+                })(<KeyInterface type="develop"/>)}
+              </Form.Item>
+            </Col> : null}
+            {setting.interType === 'outer' && setting.sysInterface === 'false' ? <Col className="outer-interface" span={24}>
+              <Form.Item label={<Tooltip placement="topLeft" title="姝e紡绯荤粺鎺ュ彛鍦板潃锛屼负绌烘椂浣跨敤鎺ュ彛鍦板潃銆�">
                   <QuestionCircleOutlined className="mk-form-tip" />
                   姝e紡鍦板潃
                 </Tooltip>
@@ -282,6 +308,17 @@
                 {getFieldDecorator('proInterface', {
                   initialValue: setting.proInterface || ''
                 })(<TextArea rows={2}/>)}
+              </Form.Item>
+            </Col> : null}
+            {setting.interType === 'outer' && setting.sysInterface === 'external' ? <Col className="outer-interface" span={24}>
+              <Form.Item label={<Tooltip placement="topLeft" title="姝e紡绯荤粺鎺ュ彛鍦板潃銆�">
+                  <QuestionCircleOutlined className="mk-form-tip" />
+                  姝e紡鍦板潃
+                </Tooltip>
+              }>
+                {getFieldDecorator('exProInterface', {
+                  initialValue: setting.exProInterface || ''
+                })(<KeyInterface type="product"/>)}
               </Form.Item>
             </Col> : null}
             {setting.interType === 'outer' ? <Col span={8}>
@@ -416,7 +453,7 @@
               </Form.Item>
             </Col> : null}
             {/* 1銆佷笉鍒嗛〉涓斾笉瀛樺湪涓婄骇妯″潡 */}
-            {!['navbar', 'interface'].includes(config.type) && !['editable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') ? <Col span={8}>
+            {!['navbar', 'interface'].includes(config.type) && !['editable', 'basetable'].includes(config.subtype) && (!config.pageable || (config.pageable && setting.laypage === 'false')) && (setting.supModule.length === 0 || setting.supModule[0] === 'empty') && setting.interType === 'system' ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'鍒濆鍖栧姞杞芥椂锛屾槸鍚︿笌鍏朵粬缁勪欢涓�鍚屽姞杞芥暟鎹紝娉細浠呭湪浣跨敤绯荤粺鍑芥暟锛屼笖鍒濆鍖栧姞杞芥暟鎹椂鏈夋晥锛屽垎椤佃姹傛椂鏃犳晥銆�'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -433,7 +470,7 @@
                 )}
               </Form.Item>
             </Col> : null}
-            {!['navbar', 'balcony', 'menubar', 'interface'].includes(config.type) ? <Col span={8}>
+            {!['navbar', 'balcony', 'menubar', 'interface'].includes(config.type) && !['basetable'].includes(config.subtype) ? <Col span={8}>
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'浼樺厛浣跨敤鍚岀骇鐨勬悳绱㈡潯浠剁粍浠讹紝鍚岀骇鎼滅储涓嶅瓨鍦ㄦ椂锛屼緷娆″悜涓婇�夊彇锛屼笌褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵竴鍚岀敤浣滄暟鎹繃婊わ紙褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵紭鍏堬級銆�'}>
                   <QuestionCircleOutlined className="mk-form-tip" />
@@ -450,34 +487,23 @@
                 )}
               </Form.Item>
             </Col> : null}
-            {!['navbar', 'balcony', 'menubar', 'interface'].includes(config.type) && setting.useMSearch === 'true' && appType !== 'mob' ? <Col span={8}>
+            {config.subtype === 'basetable' && !this.state.ismain ? <Col span={8}>
               <Form.Item label={
-                <Tooltip placement="topLeft" title={'澶栧眰鎼滅储鏉′欢鏀瑰彉鏃讹紝鏄惁鍒锋柊褰撳墠缁勪欢鏁版嵁銆�'}>
+                <Tooltip placement="topLeft" title="鏄惁浣跨敤涓昏〃鐨勬悳绱㈡潯浠�">
                   <QuestionCircleOutlined className="mk-form-tip" />
-                  鍚屾鍒锋柊
+                  涓昏〃鎼滅储
                 </Tooltip>
               }>
-                {getFieldDecorator('syncRefresh', {
-                  initialValue: setting.syncRefresh || 'true'
+                {getFieldDecorator('useMSearch', {
+                  initialValue: setting.useMSearch || 'false'
                 })(
-                  <Radio.Group>
-                    <Radio value="true">鏄�</Radio>
-                    <Radio value="false">鍚�</Radio>
+                  <Radio.Group onChange={(e) => {this.onOptionChange(e.target.value, 'useMSearch')}}>
+                    <Radio value="true">浣跨敤</Radio>
+                    <Radio value="false">涓嶄娇鐢�</Radio>
                   </Radio.Group>
                 )}
               </Form.Item>
             </Col> : null}
-            {/* <Col span={8}>
-              <Form.Item label="浜嬪姟">
-                {getFieldDecorator('transaction', {
-                  initialValue: setting.transaction || 'false'
-                })(
-                <Radio.Group>
-                  <Radio value="true">浣跨敤</Radio>
-                  <Radio value="false">涓嶄娇鐢�</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col> */}
             {!['navbar', 'balcony', 'menubar', 'interface'].includes(config.type) ? <Col span={8}>
               <Form.Item label="鍒濆鍖栨暟鎹�">
                 {getFieldDecorator('onload', {
diff --git a/src/menu/datasource/verifycard/utils.jsx b/src/menu/datasource/verifycard/utils.jsx
index 835d503..d4145df 100644
--- a/src/menu/datasource/verifycard/utils.jsx
+++ b/src/menu/datasource/verifycard/utils.jsx
@@ -163,7 +163,7 @@
 
     if (arr_field && _dataresource && /\/\*\$sum@/ig.test(_dataresource)) {
       let _sql = _dataresource.replace(/\/\*\$sum@|@sum\$\*\//ig, '')
-      _sql = `${_sql} ${_search}`
+      _sql = `/*system_query*/${_sql} ${_search}`
       if (_customScript) {
         sumSql = `/* sql sum楠岃瘉 */
           ${_customScript}
@@ -186,9 +186,9 @@
       }
 
       if (setting.order) {
-        _dataresource = `select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage === 'true' ?  'where rows > 0' : ''} order by tmptable.rows`
+        _dataresource = `/*system_query*/select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage === 'true' ?  'where rows > 0' : ''} order by tmptable.rows`
       } else {
-        _dataresource = `select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from ${_dataresource} ${_search}`
+        _dataresource = `/*system_query*/select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from ${_dataresource} ${_search}`
       }
     }
 
@@ -231,7 +231,9 @@
       sumSql = sumSql.replace(/\n\s{10}/ig, '\n')
 
       console.info(sumSql)
+      sumSql = sumSql.replace(/\n/g, ' ')
     }
+    sql = sql.replace(/\n/g, ' ')
     
     return { error, sql, sumSql, errors: errors.join('锛�') }
   }
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index 1450e45..3af7dc8 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -28,6 +28,8 @@
 const CustomChart = asyncComponent(() => import('@/menu/components/chart/chart-custom'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const Voucher = asyncComponent(() => import('@/menu/components/module/voucher'))
+const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
+const AntvG6 = asyncComponent(() => import('@/menu/components/chart/antv-G6'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -62,6 +64,10 @@
     style = { opacity: 0.3}
   }
 
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
+  }
+
   const getCardComponent = () => {
     if (card.type === 'card' && card.subtype === 'datacard') {
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
@@ -91,7 +97,7 @@
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
       return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
@@ -109,6 +115,10 @@
       return (<Timeline card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'chart') {
       return (<CustomChart card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'iframe') {
+      return (<Iframe card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'antvG6') {
+      return (<AntvG6 card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'module' && card.subtype === 'voucher') {
       return (<Voucher card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
diff --git a/src/menu/menushell/index.jsx b/src/menu/menushell/index.jsx
index 7f7c73c..ecef5dc 100644
--- a/src/menu/menushell/index.jsx
+++ b/src/menu/menushell/index.jsx
@@ -4,8 +4,6 @@
 import { Empty, notification, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -50,8 +48,6 @@
       })
     }
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
@@ -59,10 +55,6 @@
         const _cards = cards.filter(item => item.uuid !== card.uuid)
         handleList({...menu, components: _cards})
         setCards(_cards)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -106,6 +98,8 @@
         tree: '鏍戝舰鍒楄〃',
         balcony: '娴姩鍗�',
         timeline: '鏃堕棿杞�',
+        antvG6: '鏍戝浘',
+        iframe: 'iframe',
         card: '鍗$墖'
       }
       let i = 1
@@ -146,8 +140,12 @@
     }
   })
 
+  let style = JSON.stringify(menu.style || {})
+  style = style.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+  style = JSON.parse(style)
+
   return (
-    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={menu.style}>
+    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={style}>
       <div className="ant-row">
         {cards.map(card => (
           <Card
diff --git a/src/menu/modalconfig/controller.jsx b/src/menu/modalconfig/controller.jsx
index 881883d..683598d 100644
--- a/src/menu/modalconfig/controller.jsx
+++ b/src/menu/modalconfig/controller.jsx
@@ -47,7 +47,7 @@
   
   handleSave = (modal) => {
     const { config, btn } = this.state
-    MKEmitter.emit('submitModal', config, btn, modal)
+    MKEmitter.emit('submitModal', config.uuid, btn.uuid, modal)
   }
 
   render () {
diff --git a/src/menu/modalconfig/index.jsx b/src/menu/modalconfig/index.jsx
index 6b1c9ae..916fe45 100644
--- a/src/menu/modalconfig/index.jsx
+++ b/src/menu/modalconfig/index.jsx
@@ -200,13 +200,16 @@
     if (!card.span && standardform && standardform.span) {
       card.span = standardform.span
       card.labelwidth = standardform.labelwidth
+    } else if (!card.span) {
+      card.span = 12
+      card.labelwidth = 33.3
     }
 
     this.setState({
       standardform,
       visible: true,
       card: card,
-      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields)
+      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, componentConfig.columns)
     })
   }
 
@@ -276,6 +279,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
@@ -475,6 +479,7 @@
   }
 
   render () {
+    const { btn } = this.props
     const { config, saving } = this.state
 
     return (
@@ -504,7 +509,7 @@
               <SettingOutlined onClick={this.changeSetting} />
               <div className="ant-modal-content" style={{width: config.setting.width > 100 ? config.setting.width : config.setting.width + '%'}}>
                 <div className="ant-modal-header">
-                  <div className="ant-modal-title">{config.setting.title}</div>
+                  <div className="ant-modal-title">{btn.label}</div>
                   <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1鍒�</Button>
                   <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2鍒�</Button>
                   <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3鍒�</Button>
diff --git a/src/menu/modulecell/index.jsx b/src/menu/modulecell/index.jsx
index e1c5fad..5bc7ea3 100644
--- a/src/menu/modulecell/index.jsx
+++ b/src/menu/modulecell/index.jsx
@@ -92,6 +92,7 @@
           { subType: 'text', text: '鏂囨湰', type: 'col', $init: true },
           { subType: 'number', text: '鏁板瓧', type: 'col', $init: true },
           { subType: 'picture', text: '鍥剧墖', type: 'col', $init: true },
+          { subType: 'video', text: '瑙嗛', type: 'col', $init: true },
           { subType: 'link', text: '閾炬帴', type: 'col', $init: true },
           { subType: 'textarea', text: '澶氳鏂囨湰', type: 'col', $init: true },
           { subType: 'custom', text: '鑷畾涔夊垪', type: 'col', $init: true },
@@ -110,7 +111,7 @@
     let options = fromJS(this.state.options).toJS()
 
     if (appType === 'mob') {
-      options[1].children = options[1].children.filter(item => ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton'].includes(item.value))
+      options[1].children = options[1].children.filter(item => ['pop', 'prompt', 'exec', 'excelOut', 'innerpage', 'funcbutton'].includes(item.value))
       options[2].children = options[2].children.filter(item => {
         if (item.subType === 'select') {
           item.text = '閫夋嫨鍣�'
@@ -139,7 +140,7 @@
 
     return (
       <div className="mk-source-box">
-        <div className="tip">娉細褰撴嫋鍔ㄧ被鍨嬩笉鍙楁敮鎸佹椂浼氳閲嶇疆銆�</div>
+        <div className="tip">娉細褰撳厓绱犵被鍨嬩笉鍙楁敮鎸佹椂浼氳閲嶇疆銆�</div>
         {options.map((item, index) => (<div className="mk-class" span={item.span} key={index}>
           <div className="title">{item.title}</div>
           {item.children.map(cell => <SourceWrap key={cell.value || cell.subType} item={cell}/>)}
diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index a0768d5..4c97918 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -26,6 +26,10 @@
 import chart from '@/assets/mobimg/chart.png'
 import tree from '@/assets/mobimg/tree.png'
 import timeline from '@/assets/mobimg/timeline.png'
+import Iframe from '@/assets/img/newpage.jpg'
+import mindmap from '@/assets/mobimg/mindmap.png'
+import indent from '@/assets/mobimg/indent.jfif'
+import kapmap from '@/assets/mobimg/kapmap.jfif'
 // import Voucher from '@/assets/mobimg/voucher.jpg'
 
 // 缁勪欢閰嶇疆淇℃伅
@@ -40,9 +44,9 @@
   { type: 'menu', url: tabForm, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '杞挱-鍔ㄦ�佹暟鎹�', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: Carousel1, component: 'carousel', subtype: 'propcard', title: '杞挱-闈欐�佹暟鎹�', width: 24, forbid: ['billPrint'] },
+  { type: 'menu', url: TableCard, component: 'card', subtype: 'tablecard', title: '琛ㄦ牸锛堝崱鐗囷級', width: 12 },
   { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
   { type: 'menu', url: NormalTable, component: 'table', subtype: 'editable', title: '琛ㄦ牸锛堝彲缂栬緫锛�', width: 24, forbid: ['billPrint'] },
-  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '琛ㄦ牸锛堝崱鐗囷級', width: 12 },
   { type: 'menu', url: timeline, component: 'timeline', subtype: 'timeline', title: '鏃堕棿杞�', width: 12 },
   { type: 'menu', url: tree, component: 'tree', subtype: 'normaltree', title: '鏍戝舰鍒楄〃', width: 12, forbid: ['billPrint'] },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
@@ -56,9 +60,13 @@
   { type: 'menu', url: dashboard, component: 'dashboard', subtype: 'dashboard', title: '浠〃鐩�', width: 12 },
   { type: 'menu', url: ratioboard, component: 'dashboard', subtype: 'ratioboard', title: '鍗犳瘮鍥�', width: 12 },
   { type: 'menu', url: scatter, component: 'scatter', subtype: 'scatter', title: '鏁g偣鍥�', width: 24 },
+  { type: 'menu', url: mindmap, component: 'antvG6', subtype: 'mindmap', title: '鎬濈淮瀵煎浘', width: 24 },
+  { type: 'menu', url: indent, component: 'antvG6', subtype: 'indentTree', title: '缂╄繘鏂囦欢鏍�', width: 24 },
+  { type: 'menu', url: kapmap, component: 'antvG6', subtype: 'kapmap', title: '鐭ヨ瘑鍥捐氨鏍�', width: 24 },
   { type: 'menu', url: chart, component: 'chart', subtype: 'custom', title: '鑷畾涔夊浘琛�', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: Editor, component: 'editor', subtype: 'brafteditor', title: '瀵屾枃鏈�', width: 24, forbid: ['billPrint'] },
   { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 },
   { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24, forbid: ['billPrint'] },
+  { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24, forbid: ['billPrint'] },
   // { type: 'menu', url: Voucher, component: 'module', subtype: 'voucher', title: '鍑瘉', width: 24, forbid: ['billPrint'] },
 ]
diff --git a/src/menu/pastecontroller/index.jsx b/src/menu/pastecontroller/index.jsx
index fbe0827..7c21f66 100644
--- a/src/menu/pastecontroller/index.jsx
+++ b/src/menu/pastecontroller/index.jsx
@@ -4,7 +4,6 @@
 import { SnippetsOutlined } from '@ant-design/icons'
 
 import MenuUtils from '@/utils/utils-custom.js'
-import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 // import './index.scss'
 
@@ -19,7 +18,7 @@
     visible: false
   }
 
-  resetconfig = (item, copyBtns, uuids = {}) => {
+  resetconfig = (item, uuids = {}) => {
     let appType = sessionStorage.getItem('appType')
     
     if (item.type === 'tabs') {
@@ -37,7 +36,7 @@
         }
 
         tab.components = tab.components.map(cell => {
-          cell = this.resetconfig(cell, copyBtns, uuids)
+          cell = this.resetconfig(cell, uuids)
           return cell
         })
       })
@@ -48,28 +47,28 @@
       item.name = item.setting.name
 
       item.components = item.components.map(cell => {
-        cell = MenuUtils.resetComponentConfig(cell, copyBtns, uuids)
+        cell = MenuUtils.resetComponentConfig(cell, uuids)
         return cell
       })
     } else {
-      item = MenuUtils.resetComponentConfig(item, copyBtns, uuids)
+      item = MenuUtils.resetComponentConfig(item, uuids)
     }
 
     return item
   }
 
   pasteSubmit = () => {
-    let options = ['tabs', 'menubar', 'topbar', 'timeline', 'datacard', 'propcard', 'mainsearch', 'simpleform', 'stepform', 'tabform', 'balcony', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter']
+    let appType = sessionStorage.getItem('appType')
+    let options = ['tabs', 'timeline', 'datacard', 'propcard', 'mainsearch', 'simpleform', 'stepform', 'tabform', 'balcony', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter', 'iframe', 'sandbox']
+
+    if (appType === 'mob') {
+      options.push('menubar', 'topbar')
+    } else {
+      options.push('editable')
+    }
 
     this.pasteFormRef.handleConfirm().then(res => {
-      if ((res.copyType === 'menubar' || res.copyType === 'topbar') && sessionStorage.getItem('appType') !== 'mob') {
-        notification.warning({
-          top: 92,
-          message: '褰撳墠绯荤粺涓嶆敮鎸佽彍鍗曠粍浠讹紒',
-          duration: 5
-        })
-        return
-      } else if (!options.includes(res.copyType)) {
+      if (!options.includes(res.copyType)) {
         notification.warning({
           top: 92,
           message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�',
@@ -78,19 +77,11 @@
         return
       }
 
-      let copyBtns = new Map()
+      res = this.resetconfig(res)
 
-      res = this.resetconfig(res, copyBtns)
-
-      delete res.copyType
+      // delete res.copyType
       
       this.props.insert(res)
-
-      copyBtns = [...copyBtns.values()]
-
-      if (copyBtns.length > 0) {
-        MKEmitter.emit('copyButtons', copyBtns)
-      }
 
       this.setState({visible: false})
     })
diff --git a/src/menu/picturecontroller/index.jsx b/src/menu/picturecontroller/index.jsx
index 3fa98d4..e51d89f 100644
--- a/src/menu/picturecontroller/index.jsx
+++ b/src/menu/picturecontroller/index.jsx
@@ -1,15 +1,18 @@
 import React, {Component} from 'react'
-import { Modal, Button, Row, Col, Input, message, Tabs, Empty, Pagination, notification } from 'antd'
+import { Modal, Button, Row, Col, Input, message, Tabs, Empty, Pagination, notification, Typography } from 'antd'
 import { PlusOutlined, CopyOutlined, EditOutlined, DeleteOutlined, PictureOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
 import asyncComponent from '@/utils/asyncComponent'
+import { minkeIconSystem } from '@/utils/option.js'
+import MkIcon from '@/components/mk-icon'
 import './index.scss'
 
 const { Search } = Input
 const { confirm } = Modal
 const { TabPane } = Tabs
+const { Paragraph } = Typography
 
 const EditForm = asyncComponent(() => import('./editform'))
 const Video = asyncComponent(() => import('./video'))
@@ -30,6 +33,7 @@
     colorlist: [],
     piclist: [],
     vidlist: [],
+    iconlist: [...minkeIconSystem.direction, ...minkeIconSystem.edit, ...minkeIconSystem.normal, ...minkeIconSystem.data, ...minkeIconSystem.hint],
     picIndex: 1,
     vidIndex: 1,
   }
@@ -241,7 +245,7 @@
   }
   
   render() {
-    const { visible, editvisible, card, filpictures, filvideos, piclist, vidlist, imageKey, videoKey, pageSize, picIndex, vidIndex, colorlist } = this.state
+    const { visible, editvisible, card, filpictures, filvideos, piclist, vidlist, imageKey, videoKey, pageSize, picIndex, vidIndex, colorlist, iconlist } = this.state
 
     return (
       <div style={{display: 'inline-block'}}>
@@ -276,7 +280,7 @@
                   <Col span={4} key={item.id}>
                     <div className="image-video-box">
                       <div className="image-video-box-body">
-                        <Image url={item.linkurl} />
+                        <Image url={item.linkurl.replace(/@mywebsite@\//ig, window.GLOB.baseurl)} />
                       </div>
                       <div className="image-video-control">
                         <CopyOutlined onClick={() => this.copySource(item)}/>
@@ -348,6 +352,16 @@
                 {!colorlist.length ? <Empty description={null}/> : null}
               </Row>
             </TabPane>
+            <TabPane tab="鍥炬爣搴�" key="icon">
+              <Row gutter={16}>
+                {iconlist.map(item => (
+                  <Col className="icon-wrap" span={4} key={item}>
+                    <MkIcon type={item} />
+                    <Paragraph copyable={{ text: item }}>{item}</Paragraph>
+                  </Col>
+                ))}
+              </Row>
+            </TabPane>
           </Tabs>
         </Modal>
         <Modal
diff --git a/src/menu/picturecontroller/index.scss b/src/menu/picturecontroller/index.scss
index bfd7271..d143734 100644
--- a/src/menu/picturecontroller/index.scss
+++ b/src/menu/picturecontroller/index.scss
@@ -70,6 +70,14 @@
   .ant-pagination {
     text-align: right;
   }
+  .icon-wrap {
+    text-align: center;
+    margin-bottom: 24px;
+    >.anticon {
+      font-size: 34px;
+      margin-bottom: 5px;
+    }
+  }
 }
 .picture-edit-model {
   .ant-modal {
diff --git a/src/menu/replaceField/index.jsx b/src/menu/replaceField/index.jsx
index 81e4921..64353b0 100644
--- a/src/menu/replaceField/index.jsx
+++ b/src/menu/replaceField/index.jsx
@@ -154,10 +154,12 @@
     })
   }
 
+  // 渚濇嵁鍘熷瓧娈垫浛鎹负鏂板瓧娈�
   exec = (map) => {
+    const { type } = this.props
     let config = fromJS(this.props.config).toJS()
 
-    if (this.props.type === 'custom') {
+    if (type === 'custom') {
       let _replace = (components) => {
         return components.map(item => {
           if (item.type === 'tabs') {
@@ -209,6 +211,9 @@
                   return col
                 })
               }
+              if (m.config && m.config.components) {
+                m.config.components = _replace(m.config.components)
+              }
             })
           }
 
@@ -235,6 +240,9 @@
                       return col
                     })
                   }
+                  if (m.config && m.config.components) {
+                    m.config.components = _replace(m.config.components)
+                  }
                   return m
                 })
               }
@@ -259,6 +267,9 @@
                       }
                       return col
                     })
+                  }
+                  if (m.config && m.config.components) {
+                    m.config.components = _replace(m.config.components)
                   }
                   return m
                 })
@@ -295,6 +306,9 @@
                   }
                   return col
                 })
+              }
+              if (m.config && m.config.components) {
+                m.config.components = _replace(m.config.components)
               }
               return m
             })
@@ -363,6 +377,9 @@
                         return col
                       })
                     }
+                    if (m.config && m.config.components) {
+                      m.config.components = _replace(m.config.components)
+                    }
 
                     return m
                   })
@@ -389,12 +406,21 @@
             item.cols = _update(item.cols)
           }
 
+          if (item.subtype === 'basetable') {
+            item.cols = item.cols.map(col => {
+              if (col.field && map[col.field.toLowerCase()]) {
+                col.field = map[col.field.toLowerCase()].FieldName
+              }
+              return col
+            })
+          }
+
           return item
         })
       }
-  
+
       config.components = _replace(config.components)
-    } else if (this.props.type === 'table') {
+    } else if (type === 'table') {
       config.columns = config.columns.map(col => {
         if (col.field && map[col.field.toLowerCase()]) {
           col.field = map[col.field.toLowerCase()].FieldName
@@ -423,7 +449,7 @@
         }
         return m
       })
-    } else if (this.props.type === 'form') {
+    } else if (type === 'form') {
       config.fields = config.fields.map(col => {
         if (col.field && map[col.field.toLowerCase()]) {
           col.field = map[col.field.toLowerCase()].FieldName
@@ -449,10 +475,12 @@
     }, 300)
   }
 
+  // 渚濇嵁瀛楁鏇挎崲鍚嶇О
   execLabel = (map) => {
+    const { type } = this.props
     let config = fromJS(this.props.config).toJS()
 
-    if (this.props.type === 'custom') {
+    if (type === 'custom') {
       let _replace = (components) => {
         return components.map(item => {
           if (item.type === 'tabs') {
@@ -500,6 +528,9 @@
                   return col
                 })
               }
+              if (m.config && m.config.components) {
+                m.config.components = _replace(m.config.components)
+              }
             })
           }
 
@@ -524,6 +555,9 @@
                         return col
                       })
                     }
+                    if (m.config && m.config.components) {
+                      m.config.components = _replace(m.config.components)
+                    }
 
                     return m
                   })
@@ -542,12 +576,21 @@
             item.cols = _update(item.cols)
           }
 
+          if (item.subtype === 'basetable') {
+            item.cols = item.cols.map(col => {
+              if (col.field && map[col.field.toLowerCase()]) {
+                col.label = map[col.field.toLowerCase()].FieldDec
+              }
+              return col
+            })
+          }
+
           return item
         })
       }
 
       config.components = _replace(config.components)
-    } else if (this.props.type === 'table') {
+    } else if (type === 'table') {
       config.columns = config.columns.map(col => {
         if (col.field && map[col.field.toLowerCase()]) {
           col.label = map[col.field.toLowerCase()].FieldDec
@@ -573,7 +616,7 @@
         }
         return m
       })
-    } else if (this.props.type === 'form') {
+    } else if (type === 'form') {
       config.fields = config.fields.map(col => {
         if (col.field && map[col.field.toLowerCase()]) {
           col.label = map[col.field.toLowerCase()].FieldDec
diff --git a/src/menu/replaceField/settingform/index.jsx b/src/menu/replaceField/settingform/index.jsx
index 1bc080f..941c3f3 100644
--- a/src/menu/replaceField/settingform/index.jsx
+++ b/src/menu/replaceField/settingform/index.jsx
@@ -121,7 +121,7 @@
               {getFieldDecorator('resource', {
                 initialValue: 'custom'
               })(
-                <Radio.Group onChange={(e) => this.setState({resource: e.target.value})}>
+                <Radio.Group onChange={(e) => {this.setState({resource: e.target.value});this.props.form.setFieldsValue({reType: 'field'})}}>
                   <Radio value="dict">鏁版嵁瀛楀吀</Radio>
                   <Radio value="custom">鑷畾涔�</Radio>
                 </Radio.Group>
@@ -135,7 +135,7 @@
               })(
                 <Radio.Group onChange={(e) => this.setState({reType: e.target.value})}>
                   <Radio value="field">瀛楁 <SwapRightOutlined /> 鍚嶇О</Radio>
-                  <Radio value="name">鍘熷瓧娈� <SwapRightOutlined /> 鏂板瓧娈�</Radio>
+                  <Radio disabled={resource === 'dict'} value="name">鍘熷瓧娈� <SwapRightOutlined /> 鏂板瓧娈�</Radio>
                 </Radio.Group>
               )}
             </Form.Item>
diff --git a/src/menu/stylecombcontroller/styleInput/index.jsx b/src/menu/stylecombcontroller/styleInput/index.jsx
index 7426b2d..1a2c00c 100644
--- a/src/menu/stylecombcontroller/styleInput/index.jsx
+++ b/src/menu/stylecombcontroller/styleInput/index.jsx
@@ -140,7 +140,7 @@
       <div className="style-input-box">
         <Input value={value} addonAfter={
           options.length > 1 ?
-          <Select value={unit} onChange={this.changeUnit}>
+          <Select value={<span>{unit}</span>} onChange={this.changeUnit}>
             {options.map(item => <Option key={item} value={item}>{item}</Option>)}
           </Select> :
           <div className="single-unit">{unit}</div>
diff --git a/src/menu/stylecombcontroller/styleInput/index.scss b/src/menu/stylecombcontroller/styleInput/index.scss
index 6e9d563..5b4266b 100644
--- a/src/menu/stylecombcontroller/styleInput/index.scss
+++ b/src/menu/stylecombcontroller/styleInput/index.scss
@@ -8,4 +8,7 @@
     text-align: left;
     color: rgba(255, 255, 255, 0.65);
   }
+  div[title="vh"], div[title="vw"] {
+    color: #1890ff;
+  }
 }
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index 02ffba0..736abb5 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -897,6 +897,20 @@
                   </Form.Item>
                 </Col>
               </Panel> : null}
+              {options.includes('clear') ? <Panel header="娴姩" key="clear">
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<SwapOutlined title="娴姩"/>}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <Radio.Group style={{whiteSpace: 'nowrap'}} defaultValue={card.clear || 'none'} onChange={(e) => this.changeNormalStyle(e.target.value, 'clear')}>
+                      <Radio value="none">宸︽诞鍔�</Radio>
+                      <Radio value="left">涓嶆诞鍔�</Radio>
+                    </Radio.Group>
+                  </Form.Item>
+                </Col>
+              </Panel> : null}
             </Collapse> : null}
           </Form>
           <div style={{textAlign: 'right'}}>
diff --git a/src/menu/stylecontroller/styleInput/index.scss b/src/menu/stylecontroller/styleInput/index.scss
index 6e9d563..5b4266b 100644
--- a/src/menu/stylecontroller/styleInput/index.scss
+++ b/src/menu/stylecontroller/styleInput/index.scss
@@ -8,4 +8,7 @@
     text-align: left;
     color: rgba(255, 255, 255, 0.65);
   }
+  div[title="vh"], div[title="vw"] {
+    color: #1890ff;
+  }
 }
diff --git a/src/menu/sysinterface/index.jsx b/src/menu/sysinterface/index.jsx
index 5269b49..240fea2 100644
--- a/src/menu/sysinterface/index.jsx
+++ b/src/menu/sysinterface/index.jsx
@@ -160,8 +160,44 @@
   changeScripts = (interfaces) => {
     const { config } = this.props
 
+    interfaces = interfaces.map(item => {
+      item.$tables = this.getTables(item)
+      return item
+    })
+
     this.setState({ interfaces })
     this.props.updateConfig({...config, interfaces})
+  }
+
+  getTables = (record) => {
+    let tables = []
+    let cuts = []
+    let cutreg = /(from|update|insert\s+into)\s+(@db@)?[a-z_]+/ig
+    let trimreg = /(from|update|insert\s+into)\s+(@db@)?/ig
+
+    if (record.setting.interType === 'system') {
+      if (record.setting.execute !== 'false') {
+        let tbs = record.setting.dataresource.match(cutreg)
+        tbs && cuts.push(...tbs)
+      }
+      record.scripts && record.scripts.forEach(script => {
+        if (script.status === 'false') return
+        let tbs = script.sql.match(cutreg)
+        tbs && cuts.push(...tbs)
+      })
+    } else {
+      let tb = record.setting.tableName.replace(/@db@|\s+/ig, '')
+      if (/[a-z_]+/ig.test(tb)) {
+        tables.push(tb)
+      }
+    }
+
+    cuts = cuts.map(item => item.replace(trimreg, ''))
+    tables.push(...cuts)
+    tables = tables.filter(Boolean)
+    tables = Array.from(new Set(tables))
+
+    return tables
   }
 
   update = (record) => {
@@ -173,6 +209,7 @@
       record.status = 'false'
     }
     record.name = record.setting.name
+    record.$tables = this.getTables(record)
 
     let interfaces = this.state.interfaces.map(item => {
       if (item.uuid !== record.uuid) {
diff --git a/src/menu/tablenodes/index.jsx b/src/menu/tablenodes/index.jsx
index 8f31c88..2b5eed1 100644
--- a/src/menu/tablenodes/index.jsx
+++ b/src/menu/tablenodes/index.jsx
@@ -55,7 +55,56 @@
       })
     }
 
-    traversal(config.components)
+    if (config.Template === 'BaseTable') {
+      config.components.forEach(item => {
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            if (tab.components[0].$tables) {
+              ptbs.push(...tab.components[0].$tables)
+              tab.components[0].$tables.forEach(tb => {
+                tbs.push({
+                  label: tab.label,
+                  table: tb,
+                  color: '#5AD8A6',
+                  id: Utils.getuuid(),
+                  direction: 'left'
+                })
+              })
+            }
+          })
+        } else if (item.$tables) {
+          ptbs.push(...item.$tables)
+          item.$tables.forEach(tb => {
+            tbs.push({
+              label: '涓昏〃',
+              table: tb,
+              color: '#5AD8A6',
+              id: Utils.getuuid(),
+              direction: 'left'
+            })
+          })
+        }
+      })
+    } else {
+      traversal(config.components)
+
+      if (config.interfaces) {
+        config.interfaces.forEach(item => {
+          if (item.$tables) {
+            ptbs.push(...item.$tables)
+            item.$tables.forEach(tb => {
+              tbs.push({
+                label: item.name,
+                table: tb,
+                color: '#5AD8A6',
+                id: Utils.getuuid(),
+                direction: 'left'
+              })
+            })
+          }
+        })
+      }
+    }
 
     return {tbs, ptbs}
   }
@@ -181,6 +230,12 @@
           delete _param.type
           _param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
           window.open(`#/menudesign/${_param}`)
+        } else if (menu.param.MenuType === 'BaseTable') {
+          let _param = {...menu.param}
+          delete _param.type
+          delete _param.MenuType
+          _param = window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
+          window.open(`#/tabledesign/${_param}`)
         }
       } else if (menu.param.type === 'app') {
         if (menu.param.typename !== 'pc') {
@@ -279,6 +334,18 @@
       editNode(evt) {
         const item = evt.item;
         const model = item.get('model');
+
+        // 閫変腑鑺傜偣
+        this.graph.getNodes().forEach(node => {
+          let _model = node.get('model')
+          if (_model.fontcolor === '#1890ff') {
+            _model.fontcolor = ''
+            this.graph.updateItem(node, _model, false)
+          }
+        })
+
+        model.fontcolor = '#1890ff'
+        this.graph.updateItem(item, model, false)
         
         that.changeMenu(model)
       }
diff --git a/src/menu/tablenodes/index.scss b/src/menu/tablenodes/index.scss
index 669585c..6cd97eb 100644
--- a/src/menu/tablenodes/index.scss
+++ b/src/menu/tablenodes/index.scss
@@ -1,6 +1,6 @@
 .view-table-modal {
   .ant-modal {
-    top: 55px;
+    top: 30px;
   }
   .ant-modal-body {
     padding: 20px 50px 20px;
@@ -11,7 +11,7 @@
   }
   .wrap {
     position: relative;
-    height: calc(100vh - 200px);
+    height: calc(100vh - 175px);
     margin: 10px 0px;
     overflow: hidden;
 
diff --git a/src/menu/tableshell/card.jsx b/src/menu/tableshell/card.jsx
index 55926ea..0fb151b 100644
--- a/src/menu/tableshell/card.jsx
+++ b/src/menu/tableshell/card.jsx
@@ -7,7 +7,7 @@
 const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/table-tabs'))
 const BaseTable = asyncComponent(() => import('@/menu/components/table/base-table'))
 
-const Card = ({ id, card, delCard, updateConfig }) => {
+const Card = ({ id, card, delCard, updateConfig, switchConfig }) => {
   const [, drop] = useDrop({
     accept: 'menu',
     canDrop: () => true,
@@ -20,7 +20,7 @@
     if (card.type === 'table') {
       return (<BaseTable card={card} updateConfig={updateConfig}/>)
     } else if (card.type === 'tabs') {
-      return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+      return (<AntvTabs tabs={card} updateConfig={updateConfig} switchConfig={switchConfig} deletecomponent={delCard}/>)
     }
   }
   return (
diff --git a/src/menu/tableshell/index.jsx b/src/menu/tableshell/index.jsx
index 63fefeb..defc741 100644
--- a/src/menu/tableshell/index.jsx
+++ b/src/menu/tableshell/index.jsx
@@ -1,5 +1,6 @@
 import React, { useState } from 'react'
 import { useDrop } from 'react-dnd'
+import { is, fromJS } from 'immutable'
 import update from 'immutability-helper'
 import { Modal } from 'antd'
 
@@ -12,7 +13,7 @@
 const Container = ({menu, handleList }) => {
   const [cards, setCards] = useState(menu.components)
 
-  if (menu.components.length > cards.length) {
+  if (!is(fromJS(cards), fromJS(menu.components))) {
     setCards(menu.components)
   }
   
@@ -25,30 +26,65 @@
   }
 
   const updateConfig = (element) => {
-    const _cards = cards.map(item => item.uuid === element.uuid ? element : item)
+    let _cards = cards.map(item => item.uuid === element.uuid ? element : item)
+
+    if (element.type === 'tabs' && element.subtabs.length === 0) {
+      _cards = cards.filter(item => item.uuid !== element.uuid)
+    }
+
     handleList({...menu, components: _cards})
-    setCards(_cards)
+  }
+
+  const switchConfig = (dragKey, hoverKey, callback) => {
+    let _tab = null
+
+    let hoverTabsIndex = 0
+    let hoverIndex = 0
+    let _cards = cards.map((item, pindex) => {
+      if (item.type === 'tabs') {
+        item.subtabs = item.subtabs.filter((tab, index) => {
+          if (tab.uuid === dragKey) {
+            _tab = tab
+            return false
+          }
+          if (tab.uuid === hoverKey) {
+            hoverTabsIndex = pindex
+            hoverIndex = index
+          }
+          return true
+        })
+      }
+      return item
+    })
+
+    if (hoverTabsIndex && _tab) {
+      _cards[hoverTabsIndex].subtabs.splice(hoverIndex + 1, 0, _tab)
+    } else {
+      return
+    }
+
+    _cards = _cards.filter(item => {
+      if (item.type !== 'tabs') return true
+      if (item.subtabs.length === 0) return false
+
+      return true
+    })
+
+    handleList({...menu, components: _cards})
+
+    setTimeout(() => {
+      callback()
+    }, 100)
   }
 
   const deleteCard = (id) => {
     const { card } = findCard(id)
 
-    let hasComponent = false
-    if (card.type === 'tabs') {
-      card.subtabs.forEach(tab => {
-        if (tab.components.length > 0) {
-          hasComponent = true
-        }
-      })
-    }
-
     confirm({
-      title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
-      content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
+      title: `纭畾鍒犻櫎瀛愯〃缁勫悧锛焋,
       onOk() {
         const _cards = cards.filter(item => item.uuid !== card.uuid)
         handleList({...menu, components: _cards})
-        setCards(_cards)
       },
       onCancel() {}
     })
@@ -57,15 +93,15 @@
   const [, drop] = useDrop({
     accept: 'menu',
     drop(item) {
-      if (item.added) {
+      if (item.added || item.index) {
         delete item.added // 鍒犻櫎缁勪欢娣诲姞鏍囪
+        delete item.dropTargetId
         return
       }
       
       let newcard = {
         uuid: Utils.getuuid(),
         type: 'tabs',
-        subtype: 'tabs',
         isNew: true
       }
       
@@ -81,13 +117,17 @@
       const { index: overIndex } = findCard(`${targetId}`)
       const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
 
+      // setCards(_cards)
       handleList({...menu, components: _cards})
-      setCards(_cards)
     }
   })
 
+  let style = JSON.stringify(menu.style || {})
+  style = style.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+  style = JSON.parse(style)
+
   return (
-    <div ref={drop} className="table-shell-inner" style={menu.style}>
+    <div ref={drop} className="table-shell-inner" style={style}>
       <div className="ant-row">
         {cards.map(card => (
           <Card
@@ -97,6 +137,7 @@
             delCard={deleteCard}
             findCard={findCard}
             updateConfig={updateConfig}
+            switchConfig={switchConfig}
           />
         ))}
       </div>
diff --git a/src/menu/transfer/index.jsx b/src/menu/transfer/index.jsx
new file mode 100644
index 0000000..15b6805
--- /dev/null
+++ b/src/menu/transfer/index.jsx
@@ -0,0 +1,260 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal, Button, notification } from 'antd'
+import moment from 'moment'
+import md5 from 'md5'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import { getTables } from '@/utils/utils-custom.js'
+// import './index.scss'
+
+const { confirm } = Modal
+
+class Transfer extends Component {
+  static propTpyes = {
+    config: PropTypes.object,
+    updateConfig: PropTypes.func
+  }
+
+  state = {}
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  trigger = () => {
+    const { config } = this.props
+    const _this = this
+
+    if (!config.enabled) {
+      notification.warning({
+        top: 92,
+        message: '鏈惎鐢ㄨ彍鍗曚笉鍙崌绾э紒',
+        duration: 5
+      })
+      return
+    }
+    
+    confirm({
+      title: '纭畾鍒囨崲涓鸿嚜瀹氫箟椤甸潰鍚楋紵',
+      content: '',
+      onOk() {
+        return new Promise(resolve => {
+          _this.execUpdate(resolve)
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  execUpdate = (_resolve) => {
+    let _config = fromJS(this.props.config).toJS()
+    _config.permission = 'true'
+    _config.Template = 'CustomPage'
+    _config.version = 2.0
+    _config.enabled = false
+
+    delete _config.autoMatic
+
+    let useMSearch = false
+    _config.components.forEach((item, i) => {
+      if (item.type === 'tabs') {
+        item.name = '鏍囩缁�' + i
+        item.width = 24
+        item.subtype = 'tabs'
+        item.setting = {autoSwitch: 'false', blacklist: [], name: item.name, position: 'top', supModule: [], tabStyle: 'line', width: 24}
+
+        item.subtabs.forEach(tab => {
+          tab.components[0].subtype = 'normaltable'
+          tab.components[0].width = 24
+          tab.components[0].wrap.width = 24
+          tab.components[0].wrap.name = tab.components[0].name
+          if (tab.components[0].setting.useMSearch === 'true') {
+            useMSearch = true
+          }
+
+          tab.components[0].action.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.config) {
+              btn.config.Template = 'CustomPage'
+              if (btn.config.components[0]) {
+                btn.config.components[0].subtype = 'normaltable'
+              }
+            }
+          })
+          tab.components[0].cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              if (btn.OpenType === 'popview' && btn.config) {
+                btn.config.Template = 'CustomPage'
+                if (btn.config.components[0]) {
+                  btn.config.components[0].subtype = 'normaltable'
+                }
+              }
+            })
+          })
+        })
+      } else {
+        item.subtype = 'normaltable'
+        item.width = 24
+        item.wrap.width = 24
+        item.wrap.name = item.name
+        item.action.forEach(btn => {
+          if (btn.OpenType === 'popview' && btn.config) {
+            btn.config.Template = 'CustomPage'
+            if (btn.config.components[0]) {
+              btn.config.components[0].subtype = 'normaltable'
+            }
+          }
+        })
+        item.cols.forEach(col => {
+          if (col.type !== 'action') return
+          col.elements.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.config) {
+              btn.config.Template = 'CustomPage'
+              if (btn.config.components[0]) {
+                btn.config.components[0].subtype = 'normaltable'
+              }
+            }
+          })
+        })
+      }
+    })
+
+    if (useMSearch) {
+      let cell = {
+        uuid: Utils.getuuid(),
+        type: 'search',
+        width: 24,
+        subtype: 'mainsearch',
+        name: '鎼滅储',
+        search: _config.components[0].search || [],
+        style: {borderBottomColor: '#f0f0f0', borderBottomWidth: '1px'},
+        wrap: {float: 'left', blacklist: [], name: '鎼滅储', width: 24}
+      }
+
+      cell.wrap.advanceType = _config.components[0].wrap.advanceType || 'modal'
+      cell.wrap.advanceWidth = _config.components[0].wrap.advanceWidth || 1000
+      cell.wrap.searchLwidth = _config.components[0].wrap.searchLwidth || 33.3
+      cell.wrap.searchRatio = _config.components[0].wrap.searchRatio || 6
+      cell.wrap.show = _config.components[0].wrap.show || 'true'
+      
+      if (cell.wrap.advanceType === 'drawer') {
+        cell.wrap.drawerPlacement = _config.components[0].wrap.drawerPlacement || 'left'
+      }
+
+      _config.components[0].search = []
+      _config.components[0].setting.useMSearch = 'true'
+
+      _config.components.unshift(cell)
+      _config.components[0].$tables = getTables(_config.components[0])
+      _config.components[1].$tables = getTables(_config.components[1])
+    }
+
+    let tbs = []
+    _config.components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          if (tab.components[0].$tables) {
+            tbs.push(...tab.components[0].$tables)
+          }
+        })
+      } else {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+      }
+    })
+    let arr = []
+    tbs = tbs.filter(tb => {
+      let _tb = tb.toLowerCase()
+
+      if (arr.includes(_tb)) return false
+      arr.push(_tb)
+
+      return true
+    })
+    tbs.sort()
+    if (tbs.length && sessionStorage.getItem('mk_tb_names')) {
+      let names = sessionStorage.getItem('mk_tb_names')
+      tbs = tbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1)
+    }
+    tbs = tbs.map(tb => `'${tb}'`).join(';')
+
+    let key = md5(_config.uuid + tbs.toLowerCase())
+
+    let urlparam = {
+      FstId: _config.fstMenuId,
+      ParentId: _config.parentId,
+      MenuId: _config.uuid,
+      MenuName: _config.MenuName,
+      MenuNo: _config.MenuNo,
+      EasyCode: _config.easyCode,
+      type: 'admin',
+      OpenType: _config.OpenType,
+      PageParam: {Template: 'CustomPage', OpenType: 'newtab', hidden: _config.hidden},
+      MenuType: 'custom'
+    }
+
+    let url = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+    _config.tbkey = key
+
+    let param = {
+      func: 'sPC_TrdMenu_AddUpt',
+      FstID: _config.fstMenuId || '',
+      SndID: _config.parentId,
+      ParentID: _config.parentId,
+      MenuID: _config.uuid,
+      MenuNo: _config.MenuNo || '',
+      EasyCode: _config.easyCode || '',
+      Template: 'CustomPage',
+      MenuName: _config.MenuName || '',
+      PageParam: JSON.stringify({Template: 'CustomPage', OpenType: _config.OpenType || 'newtab', hidden: _config.hidden || 'false'}),
+      open_edition: _config.open_edition,
+      debug_md5: key,
+      debug_url: url,
+      debug_list: window.btoa(tbs),
+      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+    }
+
+    param.LText = Utils.formatOptions(param.LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+    Api.getSystemConfig(param).then(res => {
+      _resolve()
+      if (!res.status) {
+        Modal.warning({
+          title: res.message,
+          okText: '鐭ラ亾浜�'
+        })
+        return
+      }
+
+      localStorage.setItem('menuUpdate', new Date().getTime() + ',' + _config.uuid)
+      notification.success({
+        top: 92,
+        message: '鍒囨崲鎴愬姛銆�',
+        duration: 2
+      })
+
+      delete urlparam.type
+
+      let _param = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+
+      setTimeout(() => {
+        window.history.replaceState(null, null, window.location.href.split('#')[0] + `#/menudesign/${_param}`)
+        window.location.reload()
+      }, 2000)
+    })
+  }
+
+  render() {
+    return (
+      <Button className="mk-border-green" icon="retweet" onClick={this.trigger}>妯℃澘鍒囨崲</Button>
+    )
+  }
+}
+
+export default Transfer
\ No newline at end of file
diff --git a/src/views/popdesign/menuform/index.scss b/src/menu/transfer/index.scss
similarity index 100%
copy from src/views/popdesign/menuform/index.scss
copy to src/menu/transfer/index.scss
diff --git a/src/menu/versions/index.jsx b/src/menu/versions/index.jsx
index 1eb76b9..01fb94d 100644
--- a/src/menu/versions/index.jsx
+++ b/src/menu/versions/index.jsx
@@ -6,7 +6,6 @@
 import moment from 'moment'
 
 import Api from '@/api'
-// import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import './index.scss'
 
diff --git a/src/mob/colorsketch/index.jsx b/src/mob/colorsketch/index.jsx
index d0190fc..68d2965 100644
--- a/src/mob/colorsketch/index.jsx
+++ b/src/mob/colorsketch/index.jsx
@@ -7,8 +7,8 @@
 import './index.scss'
 
 const presetColors = [
-  '#f5222d', '#fa541c', '#fa8c16', '#faad14', '#fadb14', '#a0d911', '#52c41a', '#13c2c2', '#1890ff', '#2f54eb', '#722ed1',
-  '#eb2f96', '#595959', '#ffa39e', '#ffbb96', '#ffd591', '#ffe58f', '#fffb8f', '#eaff8f', '#b7eb8f', '#87e8de', '#91d5ff',
+  '#1890ff', '#f5222d', '#fa541c', '#fa8c16', '#faad14', '#fadb14', '#a0d911', '#52c41a', '#13c2c2', '#2f54eb', '#722ed1',
+  '#eb2f96', '#aeb303', '#c32539', '#ffbb96', '#ffd591', '#ffe58f', '#fffb8f', '#eaff8f', '#b7eb8f', '#87e8de', '#91d5ff',
   '#adc6ff', '#d3adf7', '#EBE9E9', '#d9d9d9', '#434343', '#000000', '#ffffff', 'transparent'
 ]
 
@@ -37,25 +37,22 @@
     }
 
     let _colors = sessionStorage.getItem('app_colors')
-    let colors = presetColors
+    let colors = JSON.parse(JSON.stringify(presetColors))
 
-    if (_colors && _colors !== '[]') {
+    if (_colors) {
       try {
         _colors = JSON.parse(_colors)
       } catch (e) {
-        _colors = null
+        _colors = []
       }
       
-      if (_colors) {
-        colors = presetColors.map((item, i) => {
-          if (_colors[i] && _colors[i].linkurl && !presetColors.includes(_colors[i].linkurl)) {
-            return _colors[i].linkurl
-          }
-          return item
-        })
-      }
+      _colors.forEach(item => {
+        if (!colors.includes(item.linkurl)) {
+          colors.push(item.linkurl)
+        }
+      })
     }
-    
+
     this.setState({color: initVal, allowClear: allowClear === true, colors})
   }
 
diff --git a/src/mob/components/menubar/common-menubar/index.jsx b/src/mob/components/menubar/common-menubar/index.jsx
index 37c24c3..fd5fd14 100644
--- a/src/mob/components/menubar/common-menubar/index.jsx
+++ b/src/mob/components/menubar/common-menubar/index.jsx
@@ -98,7 +98,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/mob/components/menubar/normal-menubar/index.jsx b/src/mob/components/menubar/normal-menubar/index.jsx
index a3ca2dc..df95f23 100644
--- a/src/mob/components/menubar/normal-menubar/index.jsx
+++ b/src/mob/components/menubar/normal-menubar/index.jsx
@@ -162,7 +162,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/mob/components/official/index.jsx b/src/mob/components/official/index.jsx
index 7a1c6d0..f5bcb14 100644
--- a/src/mob/components/official/index.jsx
+++ b/src/mob/components/official/index.jsx
@@ -75,7 +75,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   /**
diff --git a/src/mob/components/search/single-search/index.jsx b/src/mob/components/search/single-search/index.jsx
index b733a7c..43debf2 100644
--- a/src/mob/components/search/single-search/index.jsx
+++ b/src/mob/components/search/single-search/index.jsx
@@ -77,7 +77,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], card.style, this.getStyle)
   }
 
   /**
diff --git a/src/mob/components/tabs/antv-tabs/index.jsx b/src/mob/components/tabs/antv-tabs/index.jsx
index dc73a22..45181e1 100644
--- a/src/mob/components/tabs/antv-tabs/index.jsx
+++ b/src/mob/components/tabs/antv-tabs/index.jsx
@@ -9,7 +9,6 @@
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import DraggableTabs from './dragabletabs'
 import { resetStyle } from '@/utils/utils-custom.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Utils from '@/utils/utils.js'
 import { getTabForm, getTabsSetForm } from './options'
 import MkIcon from '@/components/mk-icon'
@@ -89,7 +88,7 @@
   changeStyle = () => {
     const { tabs } = this.state
 
-    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['background', 'border', 'padding', 'margin', 'shadow', 'clear'], tabs.style, this.getStyle)
   }
 
   changeTabStyle = () => {
@@ -174,18 +173,12 @@
 
     tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
 
-    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
-
     confirm({
       title: '纭畾鍒犻櫎鏍囩锛�',
       content: '',
       onOk() {
         _this.setState({tabs})
         _this.props.updateConfig(tabs)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -257,7 +250,6 @@
 
     editab.label = res.label
     editab.icon = res.icon
-    // editab.hasSearch = res.hasSearch || ''
     editab.hide = res.hide || 'false'
     editab.backgroundColor = res.backgroundColor
     editab.controlVal = res.controlVal || ''
diff --git a/src/mob/components/tabs/antv-tabs/options.jsx b/src/mob/components/tabs/antv-tabs/options.jsx
index 24271c3..664d749 100644
--- a/src/mob/components/tabs/antv-tabs/options.jsx
+++ b/src/mob/components/tabs/antv-tabs/options.jsx
@@ -34,19 +34,6 @@
       required: false,
       allowClear: true,
     },
-    // {
-    //   type: 'radio',
-    //   field: 'hasSearch',
-    //   label: '鎼滅储',
-    //   initval: tab.hasSearch || 'false',
-    //   required: false,
-    //   options: [
-    //     {value: 'false', label: '鏃�'},
-    //     {value: 'icon', label: '鏈�'},
-    //   ],
-    //   forbid: appType !== 'mob' || setting.display !== 'inline-block',
-    //   span: 22
-    // },
     {
       type: 'text',
       field: 'controlVal',
@@ -99,7 +86,7 @@
  * @description tabs琛ㄥ崟閰嶇疆淇℃伅
  */
 export function getTabsSetForm(setting, uuid) {
-  let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, uuid) || []
+  let modules = MenuUtils.getSupModules(window.GLOB.customMenu.components, uuid, window.GLOB.customMenu.interfaces)
   modules.push({
     value: 'preview',
     label: '涓婁竴椤碉紙url鍙傛暟锛�'
diff --git a/src/mob/components/tabs/tabcomponents/card.jsx b/src/mob/components/tabs/tabcomponents/card.jsx
index 743d449..700de18 100644
--- a/src/mob/components/tabs/tabcomponents/card.jsx
+++ b/src/mob/components/tabs/tabcomponents/card.jsx
@@ -60,6 +60,10 @@
     style = { opacity: 0.3}
   }
 
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
+  }
+
   const getCardComponent = () => {
     if (card.type === 'bar' || card.type === 'line') {
       return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
@@ -89,7 +93,7 @@
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
       return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
diff --git a/src/mob/components/tabs/tabcomponents/index.jsx b/src/mob/components/tabs/tabcomponents/index.jsx
index 474cd2c..d202e5e 100644
--- a/src/mob/components/tabs/tabcomponents/index.jsx
+++ b/src/mob/components/tabs/tabcomponents/index.jsx
@@ -5,8 +5,6 @@
 import { Empty, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -48,17 +46,11 @@
       })
     }
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
       onOk() {
         handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
diff --git a/src/mob/mobshell/card.jsx b/src/mob/mobshell/card.jsx
index c289560..b0c4f65 100644
--- a/src/mob/mobshell/card.jsx
+++ b/src/mob/mobshell/card.jsx
@@ -30,6 +30,7 @@
 const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
 const OfficialAccount = asyncComponent(() => import('@/mob/components/official'))
+const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -63,6 +64,10 @@
   let style = { opacity: 1}
   if (isDragging) {
     style = { opacity: 0.3}
+  }
+
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
   }
 
   let col = 'ant-col-' + (card.width || 24)
@@ -109,7 +114,7 @@
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
       return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
@@ -135,6 +140,8 @@
       return (<Timeline card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'officialAccount') {
       return (<OfficialAccount card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'iframe') {
+      return (<Iframe card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
 
diff --git a/src/mob/mobshell/index.jsx b/src/mob/mobshell/index.jsx
index 9a40744..2581edb 100644
--- a/src/mob/mobshell/index.jsx
+++ b/src/mob/mobshell/index.jsx
@@ -4,8 +4,6 @@
 import { Empty, notification, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -50,8 +48,6 @@
       })
     }
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎${card.name ? `銆�${card.name}銆媊 : '缁勪欢'}鍚楋紵`,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
@@ -59,10 +55,6 @@
         const _cards = cards.filter(item => item.uuid !== card.uuid)
         handleList({...menu, components: _cards})
         setCards(_cards)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -212,9 +204,13 @@
     }
   })
 
+  let style = JSON.stringify(menu.style || {})
+  style = style.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+  style = JSON.parse(style)
+
   return (
     <div ref={drop} className="mob-shell-inner" id="menu-shell-inner">
-      <div className="ant-row" style={menu.style}>
+      <div className="ant-row" style={style}>
         {cards.map(card => (
           <Card
             id={card.uuid}
diff --git a/src/mob/modalconfig/controller.jsx b/src/mob/modalconfig/controller.jsx
index efd36d6..96ddcd6 100644
--- a/src/mob/modalconfig/controller.jsx
+++ b/src/mob/modalconfig/controller.jsx
@@ -47,7 +47,7 @@
   
   handleSave = (modal) => {
     const { config, btn } = this.state
-    MKEmitter.emit('submitModal', config, btn, modal)
+    MKEmitter.emit('submitModal', config.uuid, btn.uuid, modal)
   }
 
   render () {
diff --git a/src/mob/modalconfig/index.jsx b/src/mob/modalconfig/index.jsx
index ca395cc..f4a090e 100644
--- a/src/mob/modalconfig/index.jsx
+++ b/src/mob/modalconfig/index.jsx
@@ -223,7 +223,7 @@
       standardform,
       visible: true,
       card: card,
-      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields)
+      formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, componentConfig.columns)
     })
   }
 
@@ -275,6 +275,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/mob/modulesource/option.jsx b/src/mob/modulesource/option.jsx
index 3052eb7..7e35a6b 100644
--- a/src/mob/modulesource/option.jsx
+++ b/src/mob/modulesource/option.jsx
@@ -27,6 +27,7 @@
 import CommonBar from '@/assets/mobimg/commonbar.png'
 import timeline from '@/assets/mobimg/timeline.png'
 import officialAccount from '@/assets/mobimg/guanzhu.jpg'
+import Iframe from '@/assets/img/newpage.jpg'
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
@@ -44,8 +45,8 @@
   { type: 'menu', url: tabForm, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24 },
   { type: 'menu', url: Carousel, component: 'carousel', subtype: 'datacard', title: '杞挱-鍔ㄦ�佹暟鎹�', width: 24 },
   { type: 'menu', url: Carousel1, component: 'carousel', subtype: 'propcard', title: '杞挱-闈欐�佹暟鎹�', width: 24 },
+  { type: 'menu', url: TableCard, component: 'card', subtype: 'tablecard', title: '琛ㄦ牸锛堝崱鐗囷級', width: 24 },
   { type: 'menu', url: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
-  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '琛ㄦ牸锛堝崱鐗囷級', width: 24 },
   { type: 'menu', url: timeline, component: 'timeline', subtype: 'timeline', title: '鏃堕棿杞�', width: 24 },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
   { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
@@ -61,4 +62,5 @@
   { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24 },
   { type: 'menu', url: Login, component: 'login', subtype: 'normallogin', title: '娉ㄥ唽/鐧诲綍', width: 24 },
   { type: 'menu', url: officialAccount, component: 'officialAccount', subtype: 'officialAccount', title: '鍏虫敞鍏紬鍙凤紙灏忕▼搴忎腑锛�', width: 24, adapter: 'mini' },
+  { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24 }
 ]
diff --git a/src/mob/searchconfig/index.jsx b/src/mob/searchconfig/index.jsx
index ac35cb7..62c27cb 100644
--- a/src/mob/searchconfig/index.jsx
+++ b/src/mob/searchconfig/index.jsx
@@ -237,6 +237,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/pc/components/login/normal-login/index.jsx b/src/pc/components/login/normal-login/index.jsx
index a35605f..286b250 100644
--- a/src/pc/components/login/normal-login/index.jsx
+++ b/src/pc/components/login/normal-login/index.jsx
@@ -114,7 +114,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', ['width', 'background', 'border', 'padding', 'margin'], card.style, this.getStyle)
+    MKEmitter.emit('changeStyle', ['width', 'background', 'border', 'padding', 'margin', 'clear'], card.style, this.getStyle)
   }
 
   getStyle = (style) => {
diff --git a/src/pc/components/navbar/normal-navbar/index.scss b/src/pc/components/navbar/normal-navbar/index.scss
index 5882cf7..ea0f488 100644
--- a/src/pc/components/navbar/normal-navbar/index.scss
+++ b/src/pc/components/navbar/normal-navbar/index.scss
@@ -98,34 +98,6 @@
     right: -30px;
     font-size: 16px;
   }
-  .model-menu-action-list {
-    line-height: 40px;
-    .ant-row > .anticon-plus {
-      position: absolute;
-      right: -30px;
-      font-size: 16px;
-    }
-  }
-  .card-add-button {
-    text-align: right;
-    clear: left;
-    .anticon-plus {
-      font-size: 20px;
-      color: #26C281;
-      padding: 5px;
-      margin-right: 10px;
-    }
-  }
-  .ant-pagination {
-    float: right;
-    margin: 10px;
-  }
-
-  .model-menu-action-list {
-    .page-card {
-      line-height: 55px;
-    }
-  }
 }
 .normal-navbar-edit-box::after {
   display: block;
diff --git a/src/pc/createview/index.jsx b/src/pc/createview/index.jsx
index 44c6198..580b8cc 100644
--- a/src/pc/createview/index.jsx
+++ b/src/pc/createview/index.jsx
@@ -5,7 +5,7 @@
 import moment from 'moment'
 
 import Utils from '@/utils/utils.js'
-import MenuUtils from '@/utils/utils-custom.js'
+import MenuUtils, { getTables } from '@/utils/utils-custom.js'
 import SettingForm from './settingform'
 import Api from '@/api'
 import './index.scss'
@@ -56,7 +56,7 @@
       })
 
       let config = {
-        version: 1.0,
+        version: 2.0,
         uuid: menuId,
         MenuID: menuId,
         Template: 'webPage',
@@ -170,6 +170,11 @@
             config.tables = _config.tables || []
             config.style = _config.style || {}
             config.statusBarbgColor = _config.statusBarbgColor || ''
+
+            if (config.version !== 2.0) {
+              config.components = this.collectTB(config.components)
+              config.version = 2.0
+            }
           }
   
           param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(config)))
@@ -201,6 +206,36 @@
     })
   }
 
+  collectTB = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          delete tab.floor
+          delete tab.hasSearch
+          delete tab.parentId
+
+          tab.components = this.collectTB(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.collectTB(item.components)
+      } else if (!['search', 'navbar', 'login', 'topbar', 'officialAccount'].includes(item.type)) {
+        item.$tables = getTables(item)
+      }
+
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
+      delete item.tabId
+      delete item.parentId
+      delete item.btnlog
+      delete item.floor
+      delete item.dataName
+
+      return item
+    })
+  }
+
   render () {
     const { config } = this.props
     const { visible, loading } = this.state
diff --git a/src/pc/menushell/card.jsx b/src/pc/menushell/card.jsx
index 7ecb332..12b202c 100644
--- a/src/pc/menushell/card.jsx
+++ b/src/pc/menushell/card.jsx
@@ -29,6 +29,7 @@
 const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const CustomChart = asyncComponent(() => import('@/menu/components/chart/chart-custom'))
 const Timeline = asyncComponent(() => import('@/menu/components/timeline/normal-timeline'))
+const Iframe = asyncComponent(() => import('@/menu/components/iframe'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
@@ -63,6 +64,11 @@
   if (isDragging) {
     style = { opacity: 0.3}
   }
+
+  if (card.style && card.style.clear === 'left') {
+    style.clear = 'left'
+  }
+  
   let col = ' ant-col ant-col-' + (card.width || 24)
   if (card.type === 'login') {
     let height = ''
@@ -111,7 +117,7 @@
       return (<CarouselDataCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'carousel' && card.subtype === 'propcard') {
       return (<CarouselPropCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'table' && card.subtype === 'tablecard') {
+    } else if (card.type === 'card' && card.subtype === 'tablecard') {
       return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'table' && card.subtype === 'normaltable') {
       return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
@@ -131,6 +137,8 @@
       return (<Timeline card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'chart') {
       return (<CustomChart card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'iframe') {
+      return (<Iframe card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
 
diff --git a/src/pc/menushell/index.jsx b/src/pc/menushell/index.jsx
index b56f0f6..af748d6 100644
--- a/src/pc/menushell/index.jsx
+++ b/src/pc/menushell/index.jsx
@@ -4,8 +4,6 @@
 import { Empty, notification, Modal } from 'antd'
 
 import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -50,8 +48,6 @@
       })
     }
 
-    let uuids = MenuUtils.getDelButtonIds(card)
-
     confirm({
       title: `纭畾鍒犻櫎${card.name ? `銆�${card.name}銆媊 : '缁勪欢'}鍚楋紵`,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
@@ -59,10 +55,6 @@
         const _cards = cards.filter(item => item.uuid !== card.uuid)
         handleList({...menu, components: _cards})
         setCards(_cards)
-
-        if (uuids.length === 0) return
-        
-        MKEmitter.emit('delButtons', uuids)
       },
       onCancel() {}
     })
@@ -165,8 +157,12 @@
     }
   })
 
+  let style = JSON.stringify(menu.style || {})
+  style = style.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+  style = JSON.parse(style)
+
   return (
-    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={menu.style}>
+    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={style}>
       <div className="ant-row">
         {cards.map(card => (
           <Card
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
index 4795fe3..db77df6 100644
--- a/src/pc/modulesource/option.jsx
+++ b/src/pc/modulesource/option.jsx
@@ -27,6 +27,7 @@
 import chart from '@/assets/mobimg/chart.png'
 import tree from '@/assets/mobimg/tree.png'
 import timeline from '@/assets/mobimg/timeline.png'
+import Iframe from '@/assets/img/newpage.jpg'
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
@@ -61,4 +62,5 @@
   { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 },
   { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24 },
   { type: 'menu', url: Login, component: 'login', subtype: 'normallogin', title: '娉ㄥ唽/鐧诲綍', width: 24 },
+  { type: 'menu', url: Iframe, component: 'iframe', subtype: 'iframe', title: 'iframe', width: 24 }
 ]
diff --git a/src/router/index.js b/src/router/index.js
index d923557..ab91632 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -16,7 +16,6 @@
 const AppCheck = asyncLoadComponent(() => import('@/views/appcheck'))
 const PCDesign = asyncLoadComponent(() => import('@/views/pcdesign'))
 const MobDesign = asyncLoadComponent(() => import('@/views/mobdesign'))
-const PopDesign = asyncLoadComponent(() => import('@/views/popdesign'))
 const ImDesign = asyncLoadComponent(() => import('@/views/imdesign'))
 const MenuDesign = asyncLoadComponent(() => import('@/views/menudesign'))
 const BaseDesign = asyncLoadComponent(() => import('@/views/basedesign'))
@@ -42,7 +41,6 @@
   {path: '/menudesign/:param', name: 'menudesign', component: MenuDesign, auth: true},
   {path: '/basedesign/:param', name: 'basedesign', component: BaseDesign, auth: true},
   {path: '/tabledesign/:param', name: 'tabledesign', component: TableDesign, auth: true},
-  {path: '/popdesign/:param', name: 'popdesign', component: PopDesign, auth: true},
   {path: '/billprint/:param', name: 'billprint', component: BillPrint, auth: true},
   {path: '/docprint/:menuId', name: 'docprint', component: BillPrint, auth: false},
   {path: '/docprint/:menuId/:id', name: 'docprint', component: BillPrint, auth: false},
diff --git a/src/tabviews/basetable/index.jsx b/src/tabviews/basetable/index.jsx
index a20277d..09c2024 100644
--- a/src/tabviews/basetable/index.jsx
+++ b/src/tabviews/basetable/index.jsx
@@ -1,73 +1,49 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Spin, Tabs, Switch, Row, Col } from 'antd'
+import { notification, Spin, Row, Col } from 'antd'
 
 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 options from '@/store/options.js'
-import UtilsDM from '@/utils/utils-datamanage.js'
-import { updateCommonTable } from '@/utils/utils-update.js'
 import asyncComponent from '@/utils/asyncComponent'
-import asyncSpinComponent from '@/utils/asyncSpinComponent'
-import MkIcon from '@/components/mk-icon'
 import MKEmitter from '@/utils/events.js'
 import NotFount from '@/components/404'
 import './index.scss'
 
 // 閫氱敤缁勪欢
-const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
-const MainAction = asyncSpinComponent(() => import('@/tabviews/zshare/actionList'))
-const MainTable = asyncSpinComponent(() => import('@/tabviews/zshare/normalTable'))
+const AntvTabs = asyncComponent(() => import('@/tabviews/custom/components/tabs/antv-tabs'))
+const MkBaseTable = asyncComponent(() => import('@/tabviews/custom/components/table/base-table'))
 const SettingComponent = asyncComponent(() => import('@/tabviews/zshare/settingcomponent'))
-const SubTable = asyncSpinComponent(() => import('@/tabviews/subtable'))
-const CardComponent = asyncSpinComponent(() => import('@/tabviews/zshare/cardcomponent'))
-const ChartComponent = asyncSpinComponent(() => import('@/tabviews/zshare/chartcomponent'))
-const PagemsgComponent = asyncComponent(() => import('@/tabviews/zshare/pageMessage'))
+const TableNodes = asyncComponent(() => import('@/tabviews/zshare/tablenodes'))
 const AutoMatic = asyncComponent(() => import('@/tabviews/zshare/automatic'))
+const DebugTable = asyncComponent(() => import('@/tabviews/debugtable'))
 
-const { TabPane } = Tabs
-
-class NormalTable extends Component {
+class BasePage extends Component {
   static propTpyes = {
-    param: PropTypes.any,        // 鍏朵粬椤甸潰浼犻�掔殑鎼滅储鏉′欢绛夊弬鏁�
+    param: PropTypes.any,        // 鍏朵粬椤甸潰浼犻�掔殑鍙傛暟
+    Tab: PropTypes.string,       // 寮圭獥鏍囩
     MenuID: PropTypes.string,    // 鑿滃崟Id
     MenuNo: PropTypes.string,    // 鑿滃崟鍙傛暟
-    MenuName: PropTypes.string   // 鑿滃崟鍚嶇О
+    MenuName: PropTypes.string,  // 鑿滃崟鍚嶇О
+    changeTemp: PropTypes.func
   }
 
   state = {
     dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     ContainerId: Utils.getuuid(), // 鑿滃崟澶栧眰html Id
-    BID: null,            // 椤甸潰璺宠浆鏃舵惡甯D
+    BID: '',              // 椤甸潰璺宠浆鏃舵惡甯D
     loadingview: true,    // 椤甸潰鍔犺浇涓�
     viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
     lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
-    config: {},           // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷寜閽�佹悳绱€�佹樉绀哄垪銆佹爣绛剧瓑
-    shortcuts: null,      // 蹇嵎閿�
-    searchlist: null,     // 鎼滅储鏉′欢
-    actions: null,        // 鎸夐挳闆�
-    columns: null,        // 鏄剧ず鍒�
-    arr_field: '',        // 鏌ヨ瀛楁闆�
-    setting: null,        // 椤甸潰鍏ㄥ眬璁剧疆锛氭暟鎹簮銆佹寜閽強鏄剧ず鍒楀浐瀹氥�佷富閿瓑
-    data: [],             // 鍒楄〃鏁版嵁闆�
-    selectedData: [],     // 宸查�夎〃鏍兼暟鎹�
-    total: 0,             // 鎬绘暟
+    config: null,         // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷粍浠剁瓑
+    mainSearch: null,     // 涓绘悳绱�
+    userConfig: null,     // 鐢ㄦ埛鑷畾涔夎缃�
+    data: null,           // 鍒楄〃鏁版嵁闆�
     loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
-    pageIndex: 1,         // 椤电爜
-    pageSize: 10,         // 姣忛〉鏁版嵁鏉℃暟
-    orderBy: '',          // 鎺掑簭
-    search: '',           // 鎼滅储鏉′欢鏁扮粍锛屼娇鐢ㄦ椂闇�鍒嗗満鏅鐞�
-    BIDs: {},             // 涓婄骇琛╥d
-    pickup: false,        // 涓昏〃鏁版嵁闅愯棌鏄剧ず鍒囨崲
-    chartId: '',          // 灞曞紑鍥捐〃ID
-    statFields: [],       // 鍚堣瀛楁
-    statFValue: [],       // 鍚堣鍊�
-    absFields: [],        // 缁濆鍊煎瓧娈�
-    loadCustomApi: true,  // 鍔犺浇澶栭儴璧勬簮
-    hasReqFields: false,
+    visible: false,       // 鏍囩椤垫帶鍒�
+    shortcuts: null,      // 蹇嵎閿�
     autoMatic: null
   }
 
@@ -75,7 +51,7 @@
    * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
    */
   async loadconfig () {
-    const { param, MenuName, MenuID } = this.props
+    const { MenuID } = this.props
 
     let _param = {
       func: 'sPC_Get_LongParam',
@@ -88,42 +64,52 @@
       let shortcuts = []
 
       try { // 閰嶇疆淇℃伅瑙f瀽
-        config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
-        config.MenuID = MenuID
-        config.MenuName = MenuName
-        config.setting.MenuName = MenuName
-        config.setting.$name = MenuName
+        config = window.decodeURIComponent(window.atob(result.LongParam))
+        config = config.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+        config = JSON.parse(config)
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
       }
-      
+
+      // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
+      if (!config) {
+        this.setState({
+          viewlost: true,
+          loadingview: false
+        })
+        return
+      }
+
+      // 椤甸潰鏈惎鐢ㄦ椂锛屾樉绀烘湭鍚敤椤甸潰
+      if (!config.enabled) {
+        this.setState({
+          viewlost: true,
+          loadingview: false,
+          lostmsg: this.state.dict['main.view.unenabled']
+        })
+        return
+      }
+
+      // 妯℃澘閿欒
+      if (config.Template !== 'BaseTable') {
+        if (config.Template === 'CustomPage' || config.Template === 'CommonTable') {
+          this.props.changeTemp(MenuID, config.Template)
+        } else {
+          this.setState({
+            viewlost: true,
+            loadingview: false,
+            lostmsg: '鑿滃崟淇℃伅閿欒锛屽彲鑳藉師鍥狅細1銆佸綋鍓嶇敤鎴锋棤鏉冮檺锛�2銆佹墦寮�姝よ彍鍗曠殑鎸夐挳闇�瑕佹洿鏂般��'
+          })
+        }
+        return
+      }
+
       // HS涓嶄娇鐢ㄨ嚜瀹氫箟璁剧疆
       if (result.LongParamUser && !window.GLOB.mkHS) {
         try { // 閰嶇疆淇℃伅瑙f瀽
           let userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
-          if (userConfig && !userConfig.version) {
-            Object.keys(userConfig).forEach(key => {
-              let component = userConfig[key]
-
-              if (!component.action) return
-
-              Object.keys(component.action).forEach(uuid => {
-                let item = {uuid: uuid, parentId: key, shortcut: component.action[uuid].shortcut}
-                let printer = component.action[uuid].printer
-
-                if (item.shortcut) {
-                  item.$shortcut = item.shortcut.join('+')
-                  shortcuts.push(item)
-                }
-                if (printer) {
-                  item.printer = printer.defaultPrinter || ''
-                  item.printerList = printer.printerList || ''
-                  window.GLOB.UserCacheMap.set(key + uuid, item)
-                }
-              })
-            })
-          } else if (userConfig) {
+          if (userConfig) {
             shortcuts = userConfig.action
             userConfig.printers.forEach(item => {
               window.GLOB.UserCacheMap.set(item.parentId + item.uuid, item)
@@ -134,308 +120,95 @@
         }
       }
 
-      // 椤甸潰閰嶇疆瑙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 = updateCommonTable(config)
-
       // 鏉冮檺杩囨护
-      if (!window.GLOB.mkHS) {
-        config.action = config.action.filter(item => item.hidden !== 'true' && window.GLOB.mkActions[item.uuid])
-        config.tabgroups.forEach(group => {
-          group.sublist = group.sublist.filter(tab => {
-            if (tab.supMenu === 'mainTable') {
-              tab.supMenu = MenuID
-            }
-            return window.GLOB.mkActions[tab.linkTab]
-          })
-        })
-      } else {
-        config.action = config.action.filter(item => item.hidden !== 'true')
-        config.tabgroups.forEach(group => {
-          group.sublist = group.sublist.map(tab => {
-            if (tab.supMenu === 'mainTable') {
-              tab.supMenu = MenuID
-            }
-            return tab
-          })
-        })
-      }
-      // 鍘婚櫎绌鸿鏍囩
-      config.tabgroups = config.tabgroups.filter(group => group.sublist.length > 0)
-
       let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
+      let skip = window.GLOB.mkHS
+      let param = this.props.param || {} // url鍙傛暟
 
-      let chartId = ''
+      window.GLOB.CacheData.set(MenuID, param)
 
-      if (config.charts) {
-        // 瑙嗗浘鏉冮檺
-        config.charts = config.charts.filter(item => {
-          if (item.Hide === 'true') return false
-          if (!item.blacklist || item.blacklist.length === 0) return true
-          return item.blacklist.filter(v => roleId.indexOf(v) > -1).length === 0
-        })
-  
-        if (config.charts.length <= 1) {
-          config.expand = true
-        }
-        chartId = config.charts[0] ? config.charts[0].uuid : ''
+      let userName = sessionStorage.getItem('User_Name') || ''
+      let fullName = sessionStorage.getItem('Full_Name') || ''
+
+      if (sessionStorage.getItem('isEditState') === 'true') {
+        userName = sessionStorage.getItem('CloudUserName') || ''
+        fullName = sessionStorage.getItem('CloudFullName') || ''
       }
 
-      config.search = Utils.initSearchVal(config.search)
-
-      // 瀛楁閫忚鍙婂繀濉爣蹇�
-      let hasReqFields = false
-      config.search = config.search.map(item => {
-        if (['text', 'select', 'link'].includes(item.type) && param && param.$searchkey === item.field) {
-          item.initval = param.$searchval
-        }
-
-        if (item.required) {
-          hasReqFields = true
-        }
-
-        return item
-      })
-
-      config.columns = config.columns.map(col => {
-        if (!col.field || !col.blacklist || col.blacklist.length === 0) return col
-        if (col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-          col.Hide = 'true'
-        }
-
-        return col
-      })
-
-      // 鏍囪涓婚〉闈紝鐢ㄤ簬鎸夐挳鍥哄畾鍙婅〃鍗曟寕杞借缃�
-      config.setting.tabType = 'main'
-
-      // 鏁版嵁婧愪俊鎭澶勭悊
-      config.setting.laypage = config.setting.laypage !== 'false'     // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
-      config.setting.execute = config.setting.default !== 'false'     // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
-      config.setting.customScript = ''                                // 鑷畾涔夎剼鏈�
-      config.setting.dataresource = config.setting.dataresource || ''
-
-      if (config.setting.interType === 'system' || (config.setting.interType === 'custom' && config.setting.requestMode === 'system')) {
-        if (config.setting.scripts && config.setting.scripts.length > 0) {
-          let _customScript = ''
-          config.setting.scripts.forEach(item => {
-            if (item.status === 'false') return
-            _customScript += `
-              ${item.sql}
-            `
-          })
-          config.setting.customScript = _customScript
-        }
-  
-        if (!config.setting.execute) { // 榛樿sql 涓嶆墽琛屾椂 缃┖
-          config.setting.dataresource = ''
-        } else if (/\s/.test(config.setting.dataresource)) {
-          config.setting.dataresource = '(' + config.setting.dataresource + ') tb'
-        }
-  
-        if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-          config.setting.dataresource = config.setting.dataresource.replace(/\$@/ig, '/*')
-          config.setting.dataresource = config.setting.dataresource.replace(/@\$/ig, '*/')
-          config.setting.customScript = config.setting.customScript.replace(/\$@/ig, '/*')
-          config.setting.customScript = config.setting.customScript.replace(/@\$/ig, '*/')
-        } else {
-          config.setting.dataresource = config.setting.dataresource.replace(/@\$|\$@/ig, '')
-          config.setting.customScript = config.setting.customScript.replace(/@\$|\$@/ig, '')
-        }
-
-        let userName = sessionStorage.getItem('User_Name') || ''
-        let fullName = sessionStorage.getItem('Full_Name') || ''
-
-        if (sessionStorage.getItem('isEditState') === 'true') {
-          userName = sessionStorage.getItem('CloudUserName') || ''
-          fullName = sessionStorage.getItem('CloudFullName') || ''
-        }
-
-        let regs = [
-          { reg: /@userName@/ig, value: `'${userName}'` },
-          { reg: /@fullName@/ig, value: `'${fullName}'` }
-        ]
-
-        regs.forEach(cell => {
-          config.setting.dataresource = config.setting.dataresource.replace(cell.reg, cell.value)
-          config.setting.customScript = config.setting.customScript.replace(cell.reg, cell.value)
-        })
-
-        if (config.urlFields) {
-          let _param = param || {}
-          config.urlFields.forEach(field => {
-            let reg = new RegExp('@' + field + '@', 'ig')
-            let val = `'${_param[field] || ''}'`
-            config.setting.dataresource = config.setting.dataresource.replace(reg, val)
-            config.setting.customScript = config.setting.customScript.replace(reg, val)
-          })
-        }
-      }
-
-      let _arrField = []     // 瀛楁闆�
-      let _columns = []      // 鏄剧ず鍒�
-      let _hideCol = []      // 闅愯棌鍙婂悎骞跺垪涓瓧娈电殑uuid闆�
-      let colMap = new Map() // 鐢ㄤ簬瀛楁杩囨护
-      let statFields = []    // 鍚堣瀛楁淇℃伅
-      let absFields = []     // 缁濆鍊煎瓧娈�
-
-      let _actions = []      // 宸ュ叿鏍忔寜閽�
-      let _operations = []   // 鎿嶄綔鍒楁寜閽紙瀛樺湪鏃讹級
-
-      config.action.forEach(item => {
-        item.logLabel = MenuName + '-' + item.label // 鐢ㄤ簬sPC_TableData_InUpDe璁板綍鎿嶄綔鎸夐挳
-        item.$menuId = this.props.MenuID
-
-        if (item.OpenType === 'funcbutton' && item.funcType === 'print' && item.verify) { // 鎵撳嵃鏈鸿缃�
-          let _item = window.GLOB.UserCacheMap.get(this.props.MenuID + item.uuid)
-
-          if (_item) {
-            item.printer = _item.printer || ''
-            item.verify.defaultPrinter = _item.printer || ''
-            if (item.verify.printerTypeList && _item.printerList) {
-              item.verify.printerTypeList = item.verify.printerTypeList.map(cell => {
-                cell.printer = _item.printerList[cell.Value] || ''
-  
-                return cell
-              })
-            }
-          }
-        }
-        
-        if (item.position === 'toolbar') {
-          item.$toolbtn = true
-          _actions.push(item)
-        } else if (item.position === 'grid') {
-          _operations.push(item)
-        }
-      })
-
-      // 1銆佺瓫閫夊瓧娈甸泦锛�2銆佽繃婊ら殣钘忓垪鍙婂悎骞跺垪涓殑瀛楁uuid
-      config.columns.forEach(col => {
-        if (col.field) {
-          _arrField.push(col.field)
-
-          if (col.linkmenu && col.linkmenu.length > 0) {
-            let menu_id = col.linkmenu.slice(-1)[0]
-            col.linkThdMenu = window.GLOB.mkThdMenus.filter(m => m.MenuID === menu_id)[0] || ''
-          } else {
-            col.linkThdMenu = ''
-          }
-
-          col.nameField && _arrField.push(col.nameField) // 閾炬帴鍚嶅瓧娈�
-          if (col.Hide !== 'true' && col.type === 'number' && col.sum === 'true') {
-            statFields.push(col)
-          }
-          if (col.format === 'abs') {
-            absFields.push(col.field)
-          }
-        }
-        if (col.type === 'colspan' && col.sublist) { // 绛涢�夐殣钘忓垪
-          _hideCol = _hideCol.concat(col.sublist)
-        }
-        colMap.set(col.uuid, col)
-      })
-
-      // 鐢熸垚鏄剧ず鍒楋紝澶勭悊鍚堝苟鍒椾腑鐨勫瓧娈�
-      config.columns.forEach((col, index) => {
-        if (_hideCol.includes(col.uuid)) return
-
-        col.sort = index
-
-        if (col.type === 'colspan') {
-          if (col.unfold !== 'true') { // 涓嶅睍寮�涓烘棫鐗堝悎骞跺垪
-            col.type = 'old_colspan'
-          }
-
-          let _col = fromJS(col).toJS()
-          let subcols = []
-          _col.sublist && _col.sublist.forEach(sub => {
-            if (colMap.has(sub)) {
-              subcols.push(colMap.get(sub))
-            }
-          })
-          if (subcols.length > 0) {
-            _col.subcols = subcols
-            _columns.push(_col)
-          }
-        } else {
-          _columns.push(col)
-        }
-      })
+      let regs = [
+        { reg: /@userName@/ig, value: `'${userName}'` },
+        { reg: /@fullName@/ig, value: `'${fullName}'` }
+      ]
       
-      if (config.gridBtn && config.gridBtn.display && _operations.length > 0) {
-        config.gridBtn.operations = _operations
-        _columns.push(config.gridBtn)
+      if (window.GLOB.externalDatabase !== null) {
+        regs.push({
+          reg: /@db@/ig,
+          value: window.GLOB.externalDatabase
+        })
+      }
+      if (config.urlFields) {
+        config.urlFields.forEach(field => {
+          let val = `'${param[field] || ''}'`
+          regs.push({
+            reg: new RegExp('@' + field + '@', 'ig'),
+            value: val
+          })
+        })
       }
 
-      if (config.setting.selected !== 'init' && config.setting.selected !== 'always') {
-        config.setting.selected = 'false'
-      } else if (config.setting.selected === 'init' && config.setting.onload === 'false') {
-        config.setting.selected = 'false'
-      } else {
-        config.setting.orisel = true
-      }
+      config.components = this.filterComponent(config.components, roleId, window.GLOB.mkActions, skip, param, MenuID)
 
       let autoMatic = null
       if (config.autoMatic && config.autoMatic.enable === 'true') {
-        _actions.forEach(item => {
-          if (item.uuid === config.autoMatic.action && (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print'))) {
+        config.components[0].action.forEach(item => {
+          if (item.uuid === config.autoMatic.action) {
             autoMatic = config.autoMatic
             autoMatic.OpenType = item.execMode || item.OpenType
-            config.setting.selected = 'false'
+            config.components[0].wrap.selected = 'false'
+            config.components[0].MenuID = config.components[0].uuid
+            config.components[0].autoMatic = true
             item.autoMatic = true
           }
         })
       }
+      
+      // 鑾峰彇涓绘悳绱㈡潯浠�
+      let mainSearch = []
+      config.components.forEach(component => {
+        if (component.type === 'tabs') return
 
-      if (config.setting.controlField) {
-        if (config.setting.controlVal) {
-          config.setting.controlVal = config.setting.controlVal.split(',')
-        } else {
-          config.setting.controlVal = ['']
-        }
-      }
+        component.search = component.search.map(item => {
+          item.oriInitval = item.initval
+          if (['text', 'select', 'link'].includes(item.type) && param.$searchkey === item.field) {
+            item.initval = param.$searchval
+          }
+
+          return item
+        })
+
+        mainSearch = Utils.initMainSearch(component.search)
+      })
+
+      let BID = param.$BID || ''
+
+      config.components = this.formatSetting(config.components, mainSearch, regs)
 
       this.setState({
-        pageSize: config.setting.pageSize || 10,
-        loadingview: false,
-        absFields,
-        autoMatic,
-        chartId,
+        BID: BID,
+        shortcuts: shortcuts.length > 0 ? shortcuts : null,
         config,
-        statFields,
-        shortcuts,
-        setting: config.setting,
-        searchlist: config.search,
-        actions: _actions,
-        columns: _columns,
-        arr_field: _arrField.join(','),
-        BID: param && param.$BID ? param.$BID : '',
-        search: Utils.initMainSearch(config.search),
-        hasReqFields
+        autoMatic,
+        mainSearch
       }, () => {
-        if (config.setting.onload !== 'false') { // 鍒濆鍖栧彲鍔犺浇
-          this.loadData()
+        setTimeout(() => { // 寤舵椂鍔犺浇鐘舵��
+          this.setState({
+            loadingview: false
+          })
+        }, 1000)
+        
+        if (!this.props.Tab) {
+          this.setShortcut()
         }
-        this.setShortcut()
       })
     } else {
       this.setState({
@@ -453,11 +226,6 @@
   setShortcut = () => {
     const { shortcuts } = this.state
 
-    if (!shortcuts || shortcuts.length === 0) {
-      document.onkeydown = () => {}
-      return
-    }
-
     document.onkeydown = (event) => {
       let e = event || window.event
       let keyCode = e.keyCode || e.which || e.charCode
@@ -472,18 +240,22 @@
       }
 
       if (!preKey || !keyCode) return
-
+      
       let _shortcut = `${preKey}+${keyCode}`
+
+      if (window.GLOB.breakpoint && _shortcut === 'ctrl+67') {
+        window.debugger = false
+        window.GLOB.breakpoint = false
+        sessionStorage.removeItem('breakpoint')
+        
+        MKEmitter.emit('debugChange')
+      }
+
+      if (!shortcuts) return
 
       shortcuts.some(item => {
         if (item.$shortcut === _shortcut) {
           MKEmitter.emit('triggerBtnId', item.uuid)
-
-          let element = item.parentId && item.parentId !== this.props.MenuID ? document.getElementById(item.parentId) : '' // 鏍囩鍒囨崲
-          if (element && element.click) {
-            element.click()
-          }
-
           return true
         }
         return false
@@ -491,593 +263,256 @@
     }
   }
 
-  loadData = (id) => {
-    const { MenuID } = this.props
-    const { setting, search, loadCustomApi, hasReqFields, ContainerId } = this.state
+  filterComponent = (components, roleId, permAction, skip, urlparam, pageId) => {
+    return components.filter(item => {
+      item.$pageId = pageId
 
-    this.setState({
-      selectedData: []
-    })
-    MKEmitter.emit('changeTableLine', ContainerId, MenuID, '', '')
+      item.$menuname = (this.props.MenuName || '') + '-' + (item.name || '涓昏〃')
 
-    if (hasReqFields) {
-      let requireFields = search.filter(item => item.required && item.value === '')
-
-      if (requireFields.length > 0) {
-        this.setState({
-          loading: false
+      if (item.type === 'tabs') {
+        item.subtabs = item.subtabs.filter(tab => {
+          if (!skip && !permAction[tab.components[0].uuid] && tab.permission === 'true') {
+            return false
+          } else if (tab.hide === 'true') {
+            return false
+          }
+          return true
         })
-        return
-      }
-    }
 
-    if (window.GLOB.systemType === 'production' && setting.interType === 'custom' && !setting.proInterface) {
-      notification.warning({
-        top: 92,
-        message: '鏈缃寮忕郴缁熷湴鍧�!',
-        duration: 3
-      })
-      return
-    }
+        if (item.subtabs.length === 0) return false
 
-    if (setting.interType === 'custom' && loadCustomApi) {
-      if (setting.execTime === 'once') {
-        this.setState({loadCustomApi: false})
+        item.subtabs = item.subtabs.map(tab => {
+          tab.components[0].name = tab.label
+          if (tab.permission !== 'true') { // 鏉冮檺鏈紑鍚笉鍋氭潈闄愭帶鍒�
+            skip = true
+          }
+          tab.components = this.filterComponent(tab.components, roleId, permAction, skip, urlparam, pageId)
+          return tab
+        })
+
+        return true
       }
 
-      this.loadOutResource()
-      if (setting.execType === 'async') {
-        this.loadmaindata(id)
+      // 鎼滅储鏉′欢鍒濆鍖�
+      if (item.search && item.search.length > 0) {
+        item.search = Utils.initSearchVal(item.search)
       }
-    } else {
-      this.loadmaindata(id)
-    }
-  }
 
-  loadOutResource = () => {
-    const { setting, search, BID } = this.state
-
-    let param = UtilsDM.getPrevQueryParams(setting, search, BID)
-
-    if (setting.execType === 'sync') {
-      this.setState({
-        loading: true
-      })
-    }
-
-    Api.genericInterface(param).then(res => {
-      if (res.status) {
-        if (res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) {
-          this.loadmaindata()
+      if (item.setting.supModule) {
+        let pid = item.setting.supModule.pop()
+        if (pid && pid !== 'empty') {
+          item.setting.supModule = pid
         } else {
-          this.customOuterRequest(res)
-        }
-      } else {
-        this.setState({
-          loading: false
-        })
-        notification.error({
-          top: 92,
-          message: res.message,
-          duration: 10
-        })
-      }
-    }, () => {
-      this.setState({
-        loading: false
-      })
-    })
-  }
-
-  customOuterRequest = (result) => {
-    const { setting } = this.state
-    let url = ''
-
-    if (window.GLOB.systemType === 'production') {
-      url = setting.proInterface
-    } else {
-      url = setting.interface
-    }
-
-    let mkey = result.mk_api_key || ''
-
-    delete result.mk_ex_invoke // 鏄惁缁х画鎵ц
-    delete result.status
-    delete result.message
-    delete result.ErrCode
-    delete result.ErrMesg
-    delete result.mk_api_key   // 褰撳墠璇锋眰鐨刱ey鍊硷紝鐢ㄤ簬鍥炶皟
-
-    let param = {}
-
-    Object.keys(result).forEach(key => {
-      key = key.replace(/^mk_/ig, '')
-      param[key] = result[key]
-    })
-
-    Api.directRequest(url, setting.method, param, setting.cross).then(res => {
-      if (typeof(res) !== 'object') {
-        let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
-
-        if (typeof(res) === 'string') {
-          error = res.replace(/'/ig, '"')
-        }
-
-        let _result = {
-          mk_api_key: mkey,
-          $ErrCode: 'E',
-          $ErrMesg: error
-        }
-
-        this.customCallbackRequest(_result)
-      } else {
-        if (Array.isArray(res)) {
-          res = { data: res }
-        }
-        res.mk_api_key = mkey
-        this.customCallbackRequest(res)
-      }
-    }, (e) => {
-      let _result = {
-        mk_api_key: mkey,
-        $ErrCode: 'E',
-        $ErrMesg: e && e.statusText ? e.statusText : ''
-      }
-
-      this.customCallbackRequest(_result)
-    })
-  }
-
-  customCallbackRequest = (result) => {
-    const { setting, BID } = this.state
-    let errSql = ''
-    if (result.$ErrCode === 'E') {
-      errSql = `
-        set @ErrorCode='E'
-        set @retmsg='${result.$ErrMesg}'
-      `
-      delete result.$ErrCode
-      delete result.$ErrMesg
-    }
-
-    let lines = UtilsDM.getCallBackSql(setting, result)
-    let param = {}
-
-    if (setting.callbackType === 'script') { // 浣跨敤鑷畾涔夎剼鏈�
-      let sql = lines.map(item => (`
-        ${item.insert}
-        ${item.selects.join(` union all
-        `)}
-      `))
-      sql = sql.join('')
-      
-      param = UtilsDM.getCallBackQueryParams(setting, sql, errSql, BID)
-    } else {
-      param.func = 's_ex_result_back'
-      param.s_ex_result = lines.map((item, index) => ({
-        MenuID: this.state.config.MenuID,
-        MenuName: this.state.config.MenuName,
-        TableName: item.table,
-        LongText: window.btoa(window.encodeURIComponent(`${item.insert}  ${item.selects.join(` union all `)}`)),
-        Sort: index + 1
-      }))
-
-      if (window.GLOB.debugger === true || (window.debugger === true && options.sysType !== 'cloud')) {
-        let sql = lines.map(item => (`
-          ${item.insert}
-          ${item.selects.join(` union all
-          `)}
-        `))
-        sql = sql.join('')
-        console.info(sql.replace(/\n\s{10}/ig, '\n'))
-      }
-    }
-
-    Api.genericInterface(param).then(res => {
-      if (res.status) {
-        this.loadmaindata()
-      } else {
-        this.setState({
-          loading: false
-        })
-        notification.error({
-          top: 92,
-          message: res.message,
-          duration: 10
-        })
-      }
-    }, () => {
-      this.setState({
-        loading: false
-      })
-    })
-  }
-
-  /**
-   * @description 涓昏〃鏁版嵁鍔犺浇
-   */ 
-  async loadmaindata (id) {
-    const { setting, arr_field, search, orderBy, BID, pageIndex, pageSize, absFields, autoMatic } = this.state
-
-    this.setState({
-      loading: true
-    })
-
-    let _orderBy = orderBy || setting.order
-    let param = UtilsDM.getQueryDataParams(setting, arr_field, search, _orderBy, pageIndex, pageSize, BID)
-
-    let result = await Api.genericInterface(param)
-
-    this.getStatFieldsValue()
-
-    if (result.status) {
-      let start = 1
-      if (setting.laypage) {
-        start = pageSize * (pageIndex - 1) + 1
-      }
-
-      if (setting.selected !== 'false' || (setting.orisel && id)) {
-        setTimeout(() => {
-          MKEmitter.emit('mkTableCheckTopLine', this.props.MenuID, id)
-        }, 200)
-        if (setting.selected === 'init') {
-          this.setState({setting: {...setting, selected: 'false'}})
+          item.setting.supModule = ''
         }
       }
 
-      this.setState({
-        data: result.data.map((item, index) => {
-          if (absFields.length) {
-            absFields.forEach(field => {
-              if (!item[field]) return
-              if (isNaN(Math.abs(item[field]))) return
-
-              item[field] = Math.abs(item[field])
+      let statFields = []
+      let getCols = (cols) => {
+        return cols.filter(col => {
+          if (col.blacklist && col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+            return false
+          } else if (col.Hide === 'true') {
+            return false
+          }
+          
+          if (col.type === 'number') {
+            if (col.sum === 'true') {
+              statFields.push(col)
+            }
+            if (typeof(col.decimal) === 'number') {
+              col.round = Math.pow(10, col.decimal)
+              if (col.format === 'percent') {
+                col.decimal = col.decimal > 2 ? col.decimal - 2 : 0
+              }
+            }
+          } else if (col.type === 'colspan') {
+            col.subcols = getCols(col.subcols || [])
+            if (col.subcols.length === 0) {
+              return false
+            }
+          } else if (col.type === 'custom') {
+            col.elements = col.elements.map(cell => {
+              if (['text', 'number', 'formula'].includes(cell.eleType)) {
+                if (!cell.height) {
+                  cell.innerHeight = 'auto'
+                }
+                if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
+                  cell.round = Math.pow(10, cell.decimal)
+                  if (cell.format === 'percent') {
+                    cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
+                  }
+                }
+              }
+              return cell
             })
           }
-
-          item.key = index
-          item.$$uuid = item[setting.primaryKey] || ''
-          item.$$key = '' + item.key + item.$$uuid
-          item.$$BID = BID || ''
-          item.$Index = start + index + ''
-
-          if (setting.controlField) {
-            if (setting.controlVal.includes(item[setting.controlField])) {
-              item.$disabled = true
-            }
+    
+          if (col.linkmenu && col.linkmenu.length > 0) {
+            let menu_id = col.linkmenu.pop()
+            col.linkThdMenu = window.GLOB.mkThdMenus.filter(m => m.MenuID === menu_id)[0] || ''
+          } else {
+            col.linkThdMenu = ''
           }
 
-          return item
-        }),
-        total: result.total,
-        loading: false,
-        pickup: false
-      })
-
-      if (autoMatic) {
-        if (result.data && result.data.length > 0) {
-          MKEmitter.emit('autoGetData', this.props.MenuID)
-        } else {
-          MKEmitter.emit('autoMaticOver', this.props.MenuID)
-        }
-      }
-    } else {
-      this.setState({
-        loading: false
-      })
-      if (autoMatic) {
-        MKEmitter.emit('autoMaticError', this.props.MenuID)
-      }
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
-    }
-  }
-
-  /**
-   * @description 鑾峰彇鍗曡鏁版嵁
-   */ 
-  async loadmainLinedata (id) {
-    const { setting, arr_field, search, orderBy, BID, pageIndex, pageSize, absFields } = this.state
-
-    this.setState({
-      loading: true
-    })
-
-    let _orderBy = orderBy || setting.order
-    let param = UtilsDM.getQueryDataParams(setting, arr_field, search, _orderBy, pageIndex, pageSize, BID, id)
-
-    let result = await Api.genericInterface(param)
-    if (result.status) {
-      let data = fromJS(this.state.data).toJS()
-      let selectedData = fromJS(this.state.selectedData).toJS()
-      if (result.data && result.data[0]) {
-        let _data = result.data[0] || {}
-
-        if (absFields.length) {
-          absFields.forEach(field => {
-            if (!_data[field]) return
-            if (isNaN(Math.abs(_data[field]))) return
-            
-            _data[field] = Math.abs(_data[field])
-          })
-        }
-        _data.$$uuid = _data[setting.primaryKey] || ''
-        _data.$$BID = BID || ''
-
-        try {
-          data = data.map(item => {
-            if (item.$$uuid === _data.$$uuid) {
-              _data.key = item.key
-              _data.$$key = '' + item.key + item.$$uuid
-              _data.$Index = item.$Index
-              return _data
-            } else {
-              return item
-            }
-          })
-          selectedData = selectedData.map(item => {
-            if (_data.$$uuid === item.$$uuid) {
-              return _data
-            }
-            return item
-          })
-        } catch (e) {
-          console.warn('鏁版嵁鏌ヨ閿欒')
-        }
-      }
-
-      this.setState({
-        data,
-        selectedData,
-        loading: false
-      })
-    } else {
-      this.setState({
-        loading: false
-      })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
-    }
-  }
-
-  /**
-   * @description 鑾峰彇鍚堣瀛楁鍊�
-   */
-  getStatFieldsValue = () => {
-    const { setting, search, BID, orderBy, statFields } = this.state
-
-    if (statFields.length === 0 || !(setting.interType === 'system' || (setting.interType === 'custom' && setting.requestMode === 'system')) || !setting.dataresource) return
-
-    let _orderBy = orderBy || setting.order
-    let param = UtilsDM.getStatQueryDataParams(setting, statFields, search, _orderBy, BID)
-
-    Api.genericInterface(param).then(res => {
-      if (res.status) {
-        let _data = res.data[0]
-        let values = []
-
-        if (_data) {
-          statFields.forEach(item => {
-            if (_data[item.field] || _data[item.field] === 0) {
-              let val = +_data[item.field]
-              if (isNaN(val)) {
-                val = 0
-              }
-              val = val.toFixed(item.decimal)
-              values.push({label: item.label, value: val})
-            }
-          })
-        }
-        this.setState({
-          statFValue: values
-        })
-      } else {
-        this.setState({
-          statFValue: []
-        })
-        notification.error({
-          top: 92,
-          message: res.message,
-          duration: 10
+          return true
         })
       }
-    })
-  }
+      
+      item.cols = getCols(item.cols)
+      item.statFields = Array.from(new Set(statFields))
 
-  /**
-   * @description 鎼滅储鏉′欢鏀瑰彉鏃讹紝閲嶇疆琛ㄦ牸鏁版嵁
-   * 鍚湁鍒濆涓嶅姞杞界殑椤甸潰锛屼慨鏀硅缃�
-   */
-  refreshbysearch = (searches) => {
-    const { setting } = this.state
+      // 鏉冮檺杩囨护
+      if (item.action && item.action.length > 0) {
+        item.action = item.action.filter(cell => {
+          if (cell.hidden === 'true') return false
 
-    if (setting.onload === 'false') {
-      this.setState({
-        pageIndex: 1,
-        search: searches,
-        setting: {...setting, onload: 'true'}
-      }, () => {
-        this.loadData()
-      })
-    } else {
-      MKEmitter.emit('resetTable', this.props.MenuID) // 鍒楄〃閲嶇疆
-      this.setState({
-        pageIndex: 1,
-        search: searches
-      }, () => {
-        this.loadData()
-      })
-    }
-  }
+          cell.logLabel = item.$menuname + '-' + cell.label
+          cell.ContainerId = this.state.ContainerId
+          cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+          cell.$menuId = item.uuid
+          cell.$MenuID = this.props.MenuID
+          cell.$view = 'popview'
+          cell.$toolbtn = true
 
-  /**
-   * @description 琛ㄦ牸鏉′欢鏀瑰彉鏃堕噸缃暟鎹紙鍒嗛〉鎴栨帓搴忥級
-   */
-  refreshbytable = (pagination, filters, sorter) => {
-    if (!sorter) {
-      this.setState({
-        pageIndex: pagination.pageIndex
-      }, () => {
-        this.loadData()
-      })
-      return
-    }
+          if (cell.syncComponentId === item.setting.supModule) {
+            cell.syncComponentId = ''
+          }
 
-    if (sorter.order) {
-      let _chg = {
-        ascend: 'asc',
-        descend: 'desc'
+          if (cell.OpenType === 'funcbutton' && cell.funcType === 'print' && cell.verify) { // 鎵撳嵃鏈鸿缃�
+            cell = this.getPrinter(cell, item.uuid)
+          }
+
+          return skip || permAction[cell.uuid]
+        })
       }
-      sorter.order = _chg[sorter.order]
-    }
 
-    this.setState({
-      pageIndex: pagination.current,
-      pageSize: pagination.pageSize,
-      orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
-    }, () => {
-      this.loadData()
-    })
-  }
+      item.cols = item.cols.filter(col => {
+        if (col.type !== 'action') return true
+        col.elements = col.elements.filter(cell => {
+          if (cell.hidden === 'true') return false
+          
+          cell.logLabel = item.$menuname + '-' + cell.label
+          cell.Ot = cell.Ot || 'requiredSgl'
+          cell.ContainerId = this.state.ContainerId
+          cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+          cell.$menuId = item.uuid
+          cell.$MenuID = this.props.MenuID
+          cell.$view = 'popview'
 
-  /**
-   * @description 琛ㄦ牸鍒锋柊
-   */
-  reloadtable = (btn, id = '') => {
-    if (!btn || btn.resetPageIndex !== 'false') {
-      MKEmitter.emit('resetTable', this.props.MenuID) // 鍒楄〃閲嶇疆
-      this.setState({
-        pageIndex: 1
-      }, () => {
-        this.loadData(id)
+          if (cell.syncComponentId === item.setting.supModule) {
+            cell.syncComponentId = ''
+          }
+
+          if (cell.OpenType === 'funcbutton' && cell.funcType === 'print' && cell.verify) { // 鎵撳嵃鏈鸿缃�
+            cell = this.getPrinter(cell, item.uuid)
+          }
+
+          return skip || permAction[cell.uuid]
+        })
+        return col.elements.length !== 0
       })
-    } else {
-      MKEmitter.emit('resetTable', this.props.MenuID, 'false') // 鍒楄〃閲嶇疆
-      this.loadData(id)
+      
+      return true
+    })
+  }
+
+  getPrinter = (item, parentId) => {
+    let _item = window.GLOB.UserCacheMap.get(parentId + item.uuid)
+
+    if (_item) {
+      item.printer = _item.printer || ''
+      item.verify.defaultPrinter = _item.printer || ''
+      if (item.verify.printerTypeList && _item.printerList) {
+        item.verify.printerTypeList = item.verify.printerTypeList.map(cell => {
+          cell.printer = _item.printerList[cell.Value] || ''
+
+          return cell
+        })
+      }
     }
+
+    return item
   }
 
-  /**
-   * @description 椤甸潰鍒锋柊锛岄噸鏂拌幏鍙栭厤缃�
-   */
-  reloadview = () => {
-    this.setState({ loadingview: true, viewlost: false, config: {}, setting: null,
-      data: null, total: 0, loading: false, pageIndex: 1, shortcuts: null,
-      pageSize: 10, orderBy: '', search: '', BIDs: {}, pickup: false, searchlist: null
-    }, () => {
-      this.loadconfig()
-    })
-  }
+  // 鏍煎紡鍖栭粯璁よ缃�
+  formatSetting = (components, regs) => {
+    let delay = 20
+    return components.map(component => {
+      if (component.type === 'tabs') {
+        component.subtabs = component.subtabs.map(tab => {
+          tab.components = this.formatSetting(tab.components, regs)
+          return tab
+        })
+        return component
+      }
 
-  /**
-   * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
-   */
-  queryModuleParam = (menuId, callback) => {
-    const { MenuName, MenuID } = this.props
-    const { arr_field, orderBy, search, setting} = this.state
+      component.setting.useMSearch = component.setting.useMSearch === 'true'
 
-    if (MenuID !== menuId) return
+      if (component.setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
+        component.setting.sync = 'false'
+        component.setting.laypage = component.setting.laypage === 'true'
+        return component
+      }
 
-    callback({
-      arr_field: arr_field,
-      orderBy: orderBy || setting.order,
-      search: search,
-      menuName: MenuName
-    })
-  }
+      let _customScript = ''
+      component.scripts && component.scripts.forEach(script => {
+        if (script.status !== 'false') {
+          _customScript += `
+          ${script.sql}
+          `
+        }
+      })
+      delete component.scripts
+      component.setting.$name = component.$menuname || ''
+      component.setting.execute = component.setting.execute !== 'false'  // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
+      component.setting.laypage = component.setting.laypage === 'true'   // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
 
-  /**
-   * @description 琛ㄦ牸閫夋嫨椤瑰垏鎹�
-   */
-  changeSelectedData = (selectedData) => {
-    this.setState({selectedData})
-  }
+      if (!component.setting.execute) {
+        component.setting.dataresource = ''
+      }
+      if (/\s/.test(component.setting.dataresource)) {
+        component.setting.dataresource = '(' + component.setting.dataresource + ') tb'
+      }
   
-  /**
-   * @description 鏁版嵁灞曞紑鍚堝苟鍒囨崲
-   */
-  pickupChange = () => {
-    const { pickup } = this.state
-    this.setState({
-      pickup: !pickup
+      if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+        component.setting.dataresource = component.setting.dataresource.replace(/\$@/ig, '/*').replace(/@datam@/ig, '\'Y\'')
+        component.setting.dataresource = component.setting.dataresource.replace(/@\$/ig, '*/')
+        _customScript = _customScript.replace(/\$@/ig, '/*').replace(/@datam@/ig, '\'Y\'')
+        _customScript = _customScript.replace(/@\$/ig, '*/')
+      } else {
+        component.setting.dataresource = component.setting.dataresource.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+        _customScript = _customScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+      }
+
+      regs.forEach(cell => {
+        component.setting.dataresource = component.setting.dataresource.replace(cell.reg, cell.value)
+        _customScript = _customScript.replace(cell.reg, cell.value)
+      })
+
+      component.setting.customScript = _customScript // 鏁寸悊鍚庤嚜瀹氫箟鑴氭湰
+
+      component.setting.delay = delay
+      delay += 20
+
+      return component
     })
   }
 
-  /**
-   * @description 鍥捐〃瑙嗗浘鍒囨崲
-   */
-  changeChart = (uuid) => {
-    this.setState({chartId: uuid})
-  }
-
-  reloadData = (menuId, id, btn) => {
+  reloadMenuView = (menuId) => {
     const { MenuID } = this.props
 
     if (MenuID !== menuId) return
 
-    if (id === 'formtab') { // 琛ㄥ崟鏍囩椤靛埛鏂�
-      this.reloadtable(btn)
-      return
-    }
-    
-    if (!id) {
-      this.reloadtable()
-    } else {
-      this.loadmainLinedata(id)
-    }
-  }
-
-  reloadMenuView = (menuId, position) => {
-    const { MenuID } = this.props
-
-    if (MenuID !== menuId) return
-
-    if (position === 'table') {
-      this.reloadtable()
-    } else {
-      this.reloadview()
-    }
+    this.reloadview()
   }
 
   resetActiveMenu = (menuId) => {
-    const { MenuID } = this.props
+    const { MenuID, Tab } = this.props
 
-    if (MenuID !== menuId) return
+    if (MenuID !== menuId || Tab) return
 
     this.setShortcut()
-  }
-
-  changeTableLine = (ContainerId, tableId, id, data) => {
-    if (this.state.ContainerId !== ContainerId) return
-
-    this.setState({
-      BIDs: {...this.state.BIDs, [tableId]: id, [tableId + 'data']: data}
-    })
-  }
-
-  /**
-   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
-   * @param {*} menuId     // 鑿滃崟Id
-   * @param {*} position   // 鍒锋柊浣嶇疆
-   * @param {*} btn        // 鎵ц鐨勬寜閽�
-   */
-  refreshByButtonResult = (menuId, position, btn, id, lines) => {
-    const { MenuID } = this.props
-
-    if (MenuID !== menuId) return
-
-    if (position === 'line' && lines && lines.length === 1) {
-      this.loadmainLinedata(lines[0].$$uuid)
-    } else {
-      this.reloadtable(btn, id)
-    }
   }
 
   UNSAFE_componentWillMount () {
@@ -1090,12 +525,9 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('debugChange', this.debugChange)
     MKEmitter.addListener('reloadMenuView', this.reloadMenuView)
-    MKEmitter.addListener('changeTableLine', this.changeTableLine)
     MKEmitter.addListener('resetActiveMenu', this.resetActiveMenu)
-    MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
-    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
   }
 
   /**
@@ -1105,171 +537,94 @@
     this.setState = () => {
       return
     }
-    document.onkeydown = () => {}
-    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('debugChange', this.debugChange)
     MKEmitter.removeListener('reloadMenuView', this.reloadMenuView)
-    MKEmitter.removeListener('changeTableLine', this.changeTableLine)
     MKEmitter.removeListener('resetActiveMenu', this.resetActiveMenu)
-    MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
-    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+
+    window.GLOB.CacheData.delete(this.props.MenuID)
+    if (this.state.config) {
+      this.deleteCache(this.state.config.components)
+    }
+  }
+
+  debugChange = () => {
+    this.setState({visible: !this.state.visible})
+  }
+
+  deleteCache = (components) => {
+    components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          this.deleteCache(tab.components)
+        })
+      } else {
+        window.GLOB.CacheData.delete(item.uuid)
+      }
+    })
+  }
+
+  reloadview = () => {
+    window.GLOB.CacheData.delete(this.props.MenuID)
+    if (this.state.config) {
+      this.deleteCache(this.state.config.components)
+    }
+    
+    this.setState({
+      BID: '',              // 椤甸潰璺宠浆鏃舵惡甯D
+      loadingview: true,    // 椤甸潰鍔犺浇涓�
+      viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+      config: null,         // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷粍浠剁瓑
+      loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
+      shortcuts: null,
+      data: ''
+    }, () => {
+      this.loadconfig()
+    })
+  }
+
+  resetSearch = (search) => {
+    this.setState({mainSearch: null}, () => {
+      this.setState({mainSearch: search})
+    })
+  }
+
+  getComponents = () => {
+    const { config, data, mainSearch } = this.state
+
+    if (!config || !config.components) return
+
+    return config.components.map(item => {
+      if (item.type === 'tabs') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <AntvTabs config={item} mainSearch={mainSearch} />
+          </Col>
+        )
+      } else {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <MkBaseTable config={item} data={data}/>
+          </Col>
+        )
+      }
+    })
   }
 
   render() {
-    const { MenuID } = this.props
-    const { BID, setting, searchlist, pageSize, actions, columns, loadingview, viewlost, pickup, config, chartId, search, selectedData, shortcuts, autoMatic } = this.state
+    const { loadingview, viewlost, config, loading, shortcuts, autoMatic } = this.state
 
     return (
-      <div className="commontable" id={this.state.ContainerId}>
-        {loadingview ? <Spin size="large" /> : null}
-        {searchlist && searchlist.length ?
-          <MainSearch BID={BID} searchlist={searchlist} setting={setting} refreshdata={this.refreshbysearch}/> : null
-        }
-        {setting && config.charts ? <Row className="chart-view" gutter={16}>
-          {/* 瑙嗗浘缁� */}
-          {!config.expand ? <Tabs activeKey={chartId} onChange={this.changeChart}>
-            {config.charts.map(item => (
-              <TabPane tab={<MkIcon type={item.icon} />} key={item.uuid}></TabPane>
-            ))}
-          </Tabs> : null}
-          {config.charts.map(item => {
-            if (!config.expand && chartId !== item.uuid) return null
-
-            if (item.chartType === 'table') {
-              return (
-                <Col span={item.width || 24} key={item.uuid}>
-                  {config.charts.length > 1 && item.title ? <p className="chart-table chart-title">{item.title}</p> : null}
-                  <div className="commontable-main-action">
-                    <MainAction
-                      BID={BID}
-                      setting={setting}
-                      actions={actions}
-                      columns={columns}
-                      dict={this.state.dict}
-                      MenuID={MenuID}
-                      selectedData={selectedData}
-                      ContainerId={this.state.ContainerId}
-                    />
-                  </div>
-                  <div className="main-table-box">
-                    {(setting.tableType === 'radio' || setting.tableType === 'checkbox') && this.state.data && this.state.data.length > 0 ?
-                      <Switch title="鏀惰捣" className="main-pickup" checkedChildren="寮�" unCheckedChildren="鍏�" checked={pickup} onChange={this.pickupChange} /> : null
-                    }
-                    <MainTable
-                      MenuID={MenuID}
-                      tableId={MenuID}
-                      pickup={pickup}
-                      setting={setting}
-                      columns={columns}
-                      pageSize={pageSize}
-                      dict={this.state.dict}
-                      data={this.state.data}
-                      total={this.state.total}
-                      loading={this.state.loading}
-                      statFValue={this.state.statFValue}
-                      ContainerId={this.state.ContainerId}
-                      refreshdata={this.refreshbytable}
-                      chgSelectData={this.changeSelectedData}
-                    />
-                  </div>
-                </Col>
-              )
-            } else if (item.chartType === 'card') {
-              return (
-                <Col className="card-view" span={item.width} key={item.uuid}>
-                  <CardComponent
-                    BID={BID}
-                    plot={item}
-                    MenuID={MenuID}
-                    config={config}
-                    tableId={MenuID}
-                    columns={columns}
-                    data={this.state.data}
-                    loading={this.state.loading}
-                    ContainerId={this.state.ContainerId}
-                  />
-                </Col>
-              )
-            } else {
-              return (
-                <Col span={item.width} key={item.uuid}>
-                  <ChartComponent
-                    BID={BID}
-                    plot={item}
-                    config={config}
-                    data={this.state.data}
-                    loading={this.state.loading}
-                  />
-                </Col>
-              )
-            }
-          })}
-        </Row> : null }
-        {setting && !config.charts ? <div className="chart-view">
-          <div className="commontable-main-action">
-            <MainAction
-              BID={BID}
-              setting={setting}
-              actions={actions}
-              columns={columns}
-              dict={this.state.dict}
-              MenuID={MenuID}
-              selectedData={selectedData}
-              ContainerId={this.state.ContainerId}
-            />
-          </div>
-          <div className="main-table-box">
-            {(setting.tableType === 'radio' || setting.tableType === 'checkbox') && this.state.data && this.state.data.length > 0 ?
-              <Switch title="鏀惰捣" className="main-pickup" checkedChildren="寮�" unCheckedChildren="鍏�" checked={pickup} onChange={this.pickupChange} /> : null
-            }
-            <MainTable
-              MenuID={MenuID}
-              tableId={MenuID}
-              pickup={pickup}
-              setting={setting}
-              columns={columns}
-              pageSize={pageSize}
-              dict={this.state.dict}
-              data={this.state.data}
-              total={this.state.total}
-              loading={this.state.loading}
-              statFValue={this.state.statFValue}
-              ContainerId={this.state.ContainerId}
-              refreshdata={this.refreshbytable}
-              chgSelectData={this.changeSelectedData}
-            />
-          </div>
-        </div> : null }
-        {setting && config.tabgroups.map(group => (
-          <Tabs key={group.uuid}>
-            {group.sublist.map(_tab => {
-              return (
-                <TabPane tab={
-                  <span id={_tab.uuid}>
-                    {_tab.icon ? <MkIcon type={_tab.icon} /> : null}
-                    {_tab.label}
-                  </span>
-                } key={_tab.uuid}>
-                  <SubTable
-                    Tab={_tab}
-                    SupMenuID={MenuID}
-                    MenuID={_tab.linkTab}
-                    mainSearch={_tab.searchPass === 'true' ? search : null}
-                    ContainerId={this.state.ContainerId}
-                    BID={this.state.BIDs[_tab.supMenu] || ''}
-                    BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
-                  />
-                </TabPane>
-              )
-            })}
-          </Tabs>))
-        }
-        {!window.GLOB.mkHS && autoMatic ? <AutoMatic autoMatic={autoMatic} config={config} /> : null}
-        {!window.GLOB.mkHS && window.GLOB.systemType !== 'production' ? <PagemsgComponent menu={{MenuName: this.props.MenuName, MenuNo: this.props.MenuNo}} config={config} dict={this.state.dict} /> : null}
-        {!window.GLOB.mkHS && shortcuts ? <SettingComponent config={config} dict={this.state.dict} shortcuts={shortcuts}/> : null}
+      <div className={'custom-page-wrap ' + (loadingview || loading ? 'loading' : '')} id={this.state.ContainerId} style={config ? config.style : null}>
+        {(loadingview || loading) ? <Spin className="view-spin" size="large" /> : null}
+        <Row className="component-wrap">{this.getComponents()}</Row>
+        {config && window.GLOB.breakpoint ? <DebugTable /> : null}
+        {!window.GLOB.mkHS && config && autoMatic ? <AutoMatic autoMatic={autoMatic} config={config.components[0]} /> : null}
+        {!window.GLOB.mkHS && window.GLOB.systemType !== 'production' ? <TableNodes config={config} /> : null}
+        {!window.GLOB.mkHS && config ? <SettingComponent config={config} dict={this.state.dict} shortcuts={shortcuts || []}/> : null}
         {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
       </div>
     )
   }
 }
 
-export default NormalTable
\ No newline at end of file
+export default BasePage
\ No newline at end of file
diff --git a/src/tabviews/calendar/index.jsx b/src/tabviews/calendar/index.jsx
index 110ad1f..6eda2ef 100644
--- a/src/tabviews/calendar/index.jsx
+++ b/src/tabviews/calendar/index.jsx
@@ -31,7 +31,6 @@
 
   state = {
     dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    ContainerId: Utils.getuuid(), // 鑿滃崟澶栧眰html Id
     BID: null,            // 椤甸潰璺宠浆鏃舵惡甯D
     loadingview: true,    // 椤甸潰鍔犺浇涓�
     viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
@@ -270,11 +269,17 @@
       this.setState({
         loading: false
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -546,7 +551,7 @@
     const { BID, setting, searchlist, loadingview, viewlost, config, loading, data, triggerTime } = this.state
 
     return (
-      <div className="calendar-page" id={this.state.ContainerId}>
+      <div className="calendar-page">
         {loadingview && <Spin size="large" />}
         {searchlist && searchlist.length > 0 ?
           <MainSearch BID={BID} searchlist={searchlist} setting={setting} refreshdata={this.refreshbysearch}/> : null
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index 940775d..f224e98 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Spin, Tabs, Switch, Row, Col } from 'antd'
+import { notification, Spin, Tabs, Switch, Row, Col, Modal } from 'antd'
 
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -36,7 +36,8 @@
     param: PropTypes.any,        // 鍏朵粬椤甸潰浼犻�掔殑鎼滅储鏉′欢绛夊弬鏁�
     MenuID: PropTypes.string,    // 鑿滃崟Id
     MenuNo: PropTypes.string,    // 鑿滃崟鍙傛暟
-    MenuName: PropTypes.string   // 鑿滃崟鍚嶇О
+    MenuName: PropTypes.string,  // 鑿滃崟鍚嶇О
+    changeTemp: PropTypes.func
   }
 
   state = {
@@ -91,13 +92,34 @@
 
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
-        config.MenuID = MenuID
-        config.MenuName = MenuName
-        config.setting.MenuName = MenuName
-        config.setting.$name = MenuName
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
+      }
+
+      // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
+      if (!config) {
+        this.setState({
+          loadingview: false,
+          viewlost: true
+        })
+        return
+      }
+
+      // 椤甸潰鏈惎鐢ㄦ椂锛屾樉绀烘湭鍚敤椤甸潰
+      if (!config.enabled) {
+        this.setState({
+          loadingview: false,
+          viewlost: true,
+          lostmsg: this.state.dict['main.view.unenabled']
+        })
+        return
+      }
+
+      // 妯℃澘閿欒
+      if (config.Template === 'BaseTable' || config.Template === 'CustomPage') {
+        this.props.changeTemp(MenuID, config.Template)
+        return
       }
       
       // HS涓嶄娇鐢ㄨ嚜瀹氫箟璁剧疆
@@ -136,24 +158,11 @@
         }
       }
 
-      // 椤甸潰閰嶇疆瑙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.MenuID = MenuID
+      config.MenuName = MenuName
+      config.setting.MenuName = MenuName
+      config.setting.$name = MenuName
 
       // 鐗堟湰鍏煎
       config = updateCommonTable(config)
@@ -166,6 +175,8 @@
             if (tab.supMenu === 'mainTable') {
               tab.supMenu = MenuID
             }
+            tab.ContainerId = this.state.ContainerId
+
             return window.GLOB.mkActions[tab.linkTab]
           })
         })
@@ -176,6 +187,8 @@
             if (tab.supMenu === 'mainTable') {
               tab.supMenu = MenuID
             }
+            tab.ContainerId = this.state.ContainerId
+
             return tab
           })
         })
@@ -228,6 +241,7 @@
 
       // 鏍囪涓婚〉闈紝鐢ㄤ簬鎸夐挳鍥哄畾鍙婅〃鍗曟寕杞借缃�
       config.setting.tabType = 'main'
+      config.setting.ContainerId = this.state.ContainerId
 
       // 鏁版嵁婧愪俊鎭澶勭悊
       config.setting.laypage = config.setting.laypage !== 'false'     // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
@@ -305,6 +319,7 @@
       config.action.forEach(item => {
         item.logLabel = MenuName + '-' + item.label // 鐢ㄤ簬sPC_TableData_InUpDe璁板綍鎿嶄綔鎸夐挳
         item.$menuId = this.props.MenuID
+        item.ContainerId = this.state.ContainerId
 
         if (item.OpenType === 'funcbutton' && item.funcType === 'print' && item.verify) { // 鎵撳嵃鏈鸿缃�
           let _item = window.GLOB.UserCacheMap.get(this.props.MenuID + item.uuid)
@@ -785,11 +800,18 @@
       if (autoMatic) {
         MKEmitter.emit('autoMaticError', this.props.MenuID)
       }
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -1169,7 +1191,6 @@
                       dict={this.state.dict}
                       MenuID={MenuID}
                       selectedData={selectedData}
-                      ContainerId={this.state.ContainerId}
                     />
                   </div>
                   <div className="main-table-box">
@@ -1188,7 +1209,6 @@
                       total={this.state.total}
                       loading={this.state.loading}
                       statFValue={this.state.statFValue}
-                      ContainerId={this.state.ContainerId}
                       refreshdata={this.refreshbytable}
                       chgSelectData={this.changeSelectedData}
                     />
@@ -1207,7 +1227,6 @@
                     columns={columns}
                     data={this.state.data}
                     loading={this.state.loading}
-                    ContainerId={this.state.ContainerId}
                   />
                 </Col>
               )
@@ -1236,7 +1255,6 @@
               dict={this.state.dict}
               MenuID={MenuID}
               selectedData={selectedData}
-              ContainerId={this.state.ContainerId}
             />
           </div>
           <div className="main-table-box">
@@ -1255,7 +1273,6 @@
               total={this.state.total}
               loading={this.state.loading}
               statFValue={this.state.statFValue}
-              ContainerId={this.state.ContainerId}
               refreshdata={this.refreshbytable}
               chgSelectData={this.changeSelectedData}
             />
@@ -1276,7 +1293,6 @@
                     SupMenuID={MenuID}
                     MenuID={_tab.linkTab}
                     mainSearch={_tab.searchPass === 'true' ? search : null}
-                    ContainerId={this.state.ContainerId}
                     BID={this.state.BIDs[_tab.supMenu] || ''}
                     BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                   />
diff --git a/src/tabviews/custom/components/card/balcony/index.jsx b/src/tabviews/custom/components/card/balcony/index.jsx
index c027da2..dde04a1 100644
--- a/src/tabviews/custom/components/card/balcony/index.jsx
+++ b/src/tabviews/custom/components/card/balcony/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Checkbox } from 'antd'
+import { Spin, notification, Checkbox, Modal } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -347,11 +347,17 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/card/cardcellList/asyncButtonComponent.jsx b/src/tabviews/custom/components/card/cardcellList/asyncButtonComponent.jsx
index b4313e8..aa0307b 100644
--- a/src/tabviews/custom/components/card/cardcellList/asyncButtonComponent.jsx
+++ b/src/tabviews/custom/components/card/cardcellList/asyncButtonComponent.jsx
@@ -19,6 +19,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+    
     // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
     render() {
       const C = this.state.component
diff --git a/src/tabviews/custom/components/card/cardcellList/index.jsx b/src/tabviews/custom/components/card/cardcellList/index.jsx
index aa8772a..a0eb57b 100644
--- a/src/tabviews/custom/components/card/cardcellList/index.jsx
+++ b/src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -229,6 +229,12 @@
     let contents = []
 
     elements.forEach(card => {
+      let _style_ = null
+
+      if (card.style && card.style.clear === 'left') {
+        _style_ = {clear: 'left'}
+      }
+
       if (card.eleType === 'sequence') {
         let _style = {}
         if (card.marks) {
@@ -241,7 +247,7 @@
           _style = mark.style
         }
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               <div className="ant-mk-text line1" style={{height: card.innerHeight || 'auto'}}><span className="sequence-wrap" style={_style}>{data.$Index || ''}</span></div>
             </div>
@@ -379,7 +385,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
               <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
             </div>
@@ -446,7 +452,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={_style}>
               <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
             </div>
@@ -454,7 +460,14 @@
         )
       } else if (card.eleType === 'icon') {
         let val = ''
+        let icon = ''
         let height = card.innerHeight
+
+        if (card.datatype === 'dynamic') {
+          icon = data[card.field] || ''
+        } else {
+          icon = card.icon
+        }
 
         if (!height) { // 鍏煎
           let fontSize = 14
@@ -470,18 +483,18 @@
           height = fontSize * lineHeight
         }
   
-        if (card.datatype === 'static') {
+        if (data.hasOwnProperty(card.tooltip)) {
+          val = data[card.tooltip]
+        } else {
           val = card.tooltip
-        } else if (data.hasOwnProperty(card.field)) {
-          val = data[card.field]
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               {val ? <Tooltip title={val}>
-                <MkIcon className="ant-mk-icon" style={{height: height}} type={card.icon}/>
-              </Tooltip> : <MkIcon className="ant-mk-icon" style={{height: height}} type={card.icon}/>}
+                <MkIcon className="ant-mk-icon" style={{height: height}} type={icon}/>
+              </Tooltip> : <MkIcon className="ant-mk-icon" style={{height: height}} type={icon}/>}
             </div>
           </Col>
         )
@@ -507,7 +520,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               <MkProgress value={val} config={card} color={color}/>
             </div>
@@ -558,7 +571,7 @@
         let urls = url ? url.split(',').filter(Boolean) : ['']
 
         urls.forEach((u, i) => {
-          contents.push(<Col key={card.uuid + i} span={card.width}>
+          contents.push(<Col key={card.uuid + i} style={_style_} span={card.width}>
             <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
               <MkPicture style={_imagestyle} scale={scale} url={u} urls={urls}/>
             </div>
@@ -566,8 +579,10 @@
         })
       } else if (card.eleType === 'splitline') {
         let _borderWidth = card.borderWidth === undefined ? 1 : card.borderWidth
+        _style_ = _style_ || {}
+        _style_.minHeight = _borderWidth
         contents.push(
-          <Col key={card.uuid} span={card.width} style={{minHeight: _borderWidth}}>
+          <Col key={card.uuid} span={card.width} style={_style_}>
             <div style={card.style}>
               <div className="ant-mk-splitline" style={{borderColor: card.color, borderWidth: _borderWidth}}></div>
             </div>
@@ -587,7 +602,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               <div style={{height: card.innerHeight || 25}}>
                 {val ? <BarCode card={card} value={val}/> : null}
@@ -620,7 +635,7 @@
   
         urls.forEach((u, i) => {
           contents.push(
-            <Col key={card.uuid + i} span={card.width}>
+            <Col key={card.uuid + i} style={_style_} span={card.width}>
               <div className="video-wrap" style={card.style}>
                 <Video card={card} poster={poster} value={u}/>
               </div>
@@ -641,7 +656,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               <div style={{minHeight: card.qrWidth || 50}}>
                 {val ? <QrCode card={card} value={val}/> : null}
@@ -660,7 +675,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={card.style}>
               <div className="ant-mk-text line1" style={{height: card.innerHeight || 'auto'}}>{val}</div>
             </div>
@@ -745,7 +760,7 @@
         }
   
         contents.push(
-          <Col key={card.uuid} span={card.width}>
+          <Col key={card.uuid} style={_style_} span={card.width}>
             <div style={_style}>
               <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
             </div>
@@ -774,10 +789,15 @@
         } else if (data.$$empty) {
           _data = []
         }
+
+        _style_ = _style_ || {}
+        if (card.wrapStyle) {
+          _style_ = {..._style_, ...card.wrapStyle}
+        }
   
         if (['exec', 'prompt', 'pop', 'form'].includes(card.OpenType)) {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <NormalButton
                 btn={card}
                 BID={data.$$BID}
@@ -791,7 +811,7 @@
           )
         } else if (card.OpenType === 'excelIn') {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <ExcelInButton
                 btn={card}
                 BID={data.$$BID}
@@ -804,7 +824,7 @@
           )
         } else if (card.OpenType === 'excelOut') {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <ExcelOutButton
                 btn={card}
                 BID={data.$$BID}
@@ -817,7 +837,7 @@
           )
         } else if (card.OpenType === 'popview') {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <PopupButton
                 btn={card}
                 BID={data.$$BID}
@@ -830,9 +850,10 @@
           )
         } else if (card.OpenType === 'tab') {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <TabButton
                 btn={card}
+                BID={data.$$BID}
                 BData={data.$$BData || ''}
                 disabled={_disabled}
                 selectedData={_data}
@@ -841,7 +862,7 @@
           )
         } else if (card.OpenType === 'innerpage') {
           contents.push(
-            <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+            <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
               <NewPageButton
                 btn={card}
                 BData={data.$$BData || ''}
@@ -853,7 +874,7 @@
         } else if (card.OpenType === 'funcbutton') {
           if (card.funcType === 'changeuser' || card.funcType === 'closetab') {
             contents.push(
-              <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+              <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
                 <ChangeUserButton
                   btn={card}
                   BID={data.$$BID}
@@ -866,7 +887,7 @@
             )
           } else if (card.funcType === 'print') {
             contents.push(
-              <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+              <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
                 <PrintButton
                   btn={card}
                   BID={data.$$BID}
@@ -879,7 +900,7 @@
             )
           } else if (card.funcType === 'megvii') {
             contents.push(
-              <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+              <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
                 <FuncMegvii
                   btn={card}
                   BID={data.$$BID}
@@ -891,7 +912,7 @@
             )
           } else if (card.funcType === 'filezip') {
             contents.push(
-              <Col key={card.uuid} className="mk-cell-btn" style={card.wrapStyle} span={card.width}>
+              <Col key={card.uuid} className="mk-cell-btn" style={_style_} span={card.width}>
                 <FuncZip
                   btn={card}
                   BID={data.$$BID}
diff --git a/src/tabviews/custom/components/card/data-card/index.jsx b/src/tabviews/custom/components/card/data-card/index.jsx
index 557efcd..b489eaa 100644
--- a/src/tabviews/custom/components/card/data-card/index.jsx
+++ b/src/tabviews/custom/components/card/data-card/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, Empty, notification, message, Row, Col, Pagination } from 'antd'
+import { Spin, Empty, notification, message, Row, Col, Pagination, Modal } from 'antd'
 import { DownOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
@@ -254,7 +254,7 @@
       }
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({pageIndex: 1}, () => {
         this.loadData()
       })
@@ -604,11 +604,17 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/card/prop-card/index.jsx b/src/tabviews/custom/components/card/prop-card/index.jsx
index 69708a2..7bce4be 100644
--- a/src/tabviews/custom/components/card/prop-card/index.jsx
+++ b/src/tabviews/custom/components/card/prop-card/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Col, Row } from 'antd'
+import { Spin, notification, Col, Row, Modal } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -185,7 +185,7 @@
   }
 
   /**
-   * @description 鍥捐〃鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   * @description 鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
    */
   UNSAFE_componentWillReceiveProps (nextProps) {
     const { sync, config, BID, BData, selected } = this.state
@@ -210,7 +210,7 @@
           this.transferLine()
         }
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -397,11 +397,17 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/card/table-card/index.jsx b/src/tabviews/custom/components/card/table-card/index.jsx
index f2616d8..189b3a3 100644
--- a/src/tabviews/custom/components/card/table-card/index.jsx
+++ b/src/tabviews/custom/components/card/table-card/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Row, Col, Empty, Pagination } from 'antd'
+import { Spin, notification, Row, Col, Empty, Pagination, Modal } from 'antd'
 import { DownOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
@@ -179,7 +179,7 @@
       })
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -330,11 +330,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/carousel/data-card/index.jsx b/src/tabviews/custom/components/carousel/data-card/index.jsx
index 788da3d..8829c61 100644
--- a/src/tabviews/custom/components/carousel/data-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -1,7 +1,8 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, Empty, notification, Carousel } from 'antd'
+import { Spin, Empty, notification, Carousel, Modal, Checkbox, Button } from 'antd'
+import { LeftOutlined, RightOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -11,6 +12,7 @@
 import './index.scss'
 
 const CardItem = asyncComponent(() => import('../cardItem'))
+const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 
 class DataCard extends Component {
   static propTpyes = {
@@ -26,7 +28,8 @@
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
     card: null,                // 鍗$墖璁剧疆
     data: null,                // 鏁版嵁
-    BData: null
+    BData: null,
+    visible: false
   }
 
   UNSAFE_componentWillMount () {
@@ -74,8 +77,6 @@
       _cols.set(item.field, item)
     })
 
-    _card.style.height = _config.style.height
-
     if (_card.setting.click) {
       _card.style.cursor = 'pointer'
     }
@@ -86,6 +87,13 @@
       }
       return item
     })
+
+    if (!_config.wrap.height) { // 鍏煎
+      _config.wrap.height = _config.style.height || '300px'
+      delete _config.style.height
+    }
+
+    _card.style.height = _config.wrap.height
 
     _config.wrap.speed = (_config.wrap.speed || 3) * 1000
 
@@ -102,6 +110,8 @@
         setTimeout(() => {
           this.loadData()
         }, _config.setting.delay || 0)
+      } else {
+        this.openModal()
       }
     })
   }
@@ -143,8 +153,10 @@
         return item
       })
 
-      this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({sync: false, data: _data}, () => {
+        this.openModal()
+      })
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -160,6 +172,27 @@
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
 
     this.timer && this.timer.stop()
+  }
+
+  openModal = () => {
+    const { config, data } = this.state
+    
+    if (config.wrap.display !== 'modal' || !data || data.length === 0) return
+
+    let code = config.wrap.code || ('modal' + config.uuid)
+    let tip = localStorage.getItem(code)
+
+    if (!data[0].$$uuid || tip === data[0].$$uuid + data.length) return
+
+    if (config.wrap.modalContent === 'update') {
+      localStorage.setItem(code, data[0].$$uuid + data.length)
+
+      Api.getAppVersion(true)
+    }
+
+    setTimeout(() => {
+      this.setState({visible: true})
+    }, 200)
   }
 
   /**
@@ -253,25 +286,85 @@
           return item
         }),
         loading: false
+      }, () => {
+        this.openModal()
       })
     } else {
       this.setState({
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    }
+  }
+
+  onTipChange = (e) => {
+    const { config, data } = this.state
+
+    let code = config.wrap.code || ('modal' + config.uuid)
+    if (e.target.checked) {
+      localStorage.setItem(code, data[0].$$uuid + data.length)
+    } else {
+      localStorage.removeItem(code)
     }
   }
 
   render() {
-    const { config, loading, data, card } = this.state
+    const { config, loading, data, card, visible } = this.state
 
     if (config.wrap.empty === 'hidden' && (!data || data.length === 0)) return null
     
+    if (config.wrap.display === 'modal') {
+      return <Modal
+        wrapClassName='mk-carousel-modal'
+        visible={visible}
+        maskClosable={false}
+        width={config.wrap.modalWidth}
+        onCancel={() => this.setState({visible: false})}
+        footer={null}
+        destroyOnClose
+      >
+        {visible ? <div className="custom-data-carousel-box" style={config.style}>
+          <NormalHeader config={config}/>
+          <div className="carousel-wrap" style={{height: config.wrap.height}}>
+            {data.length > 1 && config.wrap.autoplay === 'false' ? <div className="prev" onClick={() => this.node && this.node.prev()}><LeftOutlined /></div> : null}
+            {data.length > 1 && config.wrap.autoplay === 'false' ? <div className="next" onClick={() => this.node && this.node.next()}><RightOutlined /></div> : null}
+            <Carousel
+              autoplay={config.wrap.autoplay !== 'false'}
+              dots={config.wrap.dots !== 'false'}
+              dotPosition={config.wrap.dotPosition || 'bottom'}
+              effect={config.wrap.effect || 'scrollx'}
+              autoplaySpeed={config.wrap.speed}
+              ref={ref => this.node = ref}
+            >
+              {data.map((item, index) => (
+                <div key={index}>
+                  <CardItem card={card} cards={config} data={item}/>
+                </div>
+              ))}
+            </Carousel>
+          </div>
+        </div> : null}
+        {config.wrap.modalContent !== 'update' ? <div className="mk-footer">
+          <Checkbox defaultChecked={false} onChange={this.onTipChange}>涓嶅湪鎻愰啋</Checkbox>
+          <Button className="close" onClick={() => this.setState({visible: false})}>绔嬪嵆浣撻獙</Button>
+        </div> : <div className="mk-footer">
+          <Button className="close" onClick={() => this.setState({visible: false})}>鐭ラ亾浜�</Button>
+        </div>}
+      </Modal>
+    }
+
     return (
       <div className="custom-data-carousel-box" id={'anchor' + config.uuid} style={config.style}>
         {loading ?
@@ -280,20 +373,22 @@
             <Spin />
           </div> : null
         }
-        {data && data.length > 0 ? <Carousel
-          autoplay={config.wrap.autoplay !== 'false'}
-          dots={config.wrap.dots !== 'false'}
-          dotPosition={config.wrap.dotPosition || 'bottom'}
-          effect={config.wrap.effect || 'scrollx'}
-          autoplaySpeed={config.wrap.speed}
-        >
-          {data.map((item, index) => (
-            <div key={index}>
-              <CardItem card={card} cards={config} data={item}/>
-            </div>
-          ))}
-        </Carousel> : null}
-        {!data || data.length === 0 ? <Empty description={false}/> : null}
+        <NormalHeader config={config}/>
+        <div className="carousel-wrap" style={{height: config.wrap.height}}>
+          {data && data.length > 0 ? <Carousel
+            autoplay={config.wrap.autoplay !== 'false'}
+            dots={config.wrap.dots !== 'false'}
+            dotPosition={config.wrap.dotPosition || 'bottom'}
+            effect={config.wrap.effect || 'scrollx'}
+            autoplaySpeed={config.wrap.speed}
+          >
+            {data.map((item, index) => (
+              <div key={index}>
+                <CardItem card={card} cards={config} data={item}/>
+              </div>
+            ))}
+          </Carousel> : <Empty description={false}/>}
+        </div>
       </div>
     )
   }
diff --git a/src/tabviews/custom/components/carousel/data-card/index.scss b/src/tabviews/custom/components/carousel/data-card/index.scss
index 570aed6..ee17b7f 100644
--- a/src/tabviews/custom/components/carousel/data-card/index.scss
+++ b/src/tabviews/custom/components/carousel/data-card/index.scss
@@ -3,20 +3,26 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
+  position: relative;
   min-height: 30px;
-  overflow: hidden;
 
   .card-item-box {
+    position: relative;
     background-position: center center;
     background-repeat: no-repeat;
     background-size: cover;
     overflow: hidden;
   }
 
+  .carousel-wrap {
+    position: relative;
+    overflow: hidden;
+  }
+
   .ant-empty {
-    width: 100%;
-    min-height: 100px;
-    padding-top: 15px;
+    position: absolute;
+    top: calc(50% - 34px);
+    left: calc(50% - 92px);
 
     .ant-empty-image {
       height: 60px;
@@ -49,3 +55,82 @@
   display: block;
   clear: both;
 }
+
+.mk-carousel-modal {
+  .ant-modal-body {
+    padding: 20px;
+  }
+  .ant-modal-close-x {
+    width: 40px;
+    height: 40px;
+    font-size: 14px;
+    line-height: 40px;
+  }
+  .card-item-box {
+    overflow-y: auto!important;
+  }
+  .card-item-box::-webkit-scrollbar {
+    width: 7px;
+  }
+  .card-item-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);
+  }
+  .card-item-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-carousel {
+    height: 100%;
+    .slick-slider {
+      height: 100%;
+    }
+  }
+  .prev {
+    position: absolute;
+    top: calc(50% - 15px);
+    left: 0px;
+    z-index: 1;
+    cursor: pointer;
+    color: #ffffff;
+    padding: 5px 3px;
+    background: rgba(0,0,0,0.15);
+  }
+  .next {
+    position: absolute;
+    top: calc(50% - 15px);
+    right: 0px;
+    z-index: 1;
+    cursor: pointer;
+    color: #ffffff;
+    padding: 5px 3px;
+    background: rgba(0,0,0,0.15);
+  }
+  .mk-footer {
+    padding-top: 10px;
+    .ant-checkbox-wrapper {
+      font-size: 13px;
+      line-height: 26px;
+    }
+    .ant-checkbox-inner {
+      width: 15px;
+      height: 15px;
+    }
+    .close {
+      height: 32px;
+      color: #ffffff;
+      background-color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+      padding: 0 20px;
+      float: right;
+    }
+  }
+  .mk-footer::after {
+    content: ' ';
+    display: block;
+    clear: both;
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.jsx b/src/tabviews/custom/components/carousel/prop-card/index.jsx
index c5b37fe..9b7b8d8 100644
--- a/src/tabviews/custom/components/carousel/prop-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -1,7 +1,8 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Carousel } from 'antd'
+import { Spin, notification, Carousel, Modal, Checkbox, Button } from 'antd'
+import { LeftOutlined, RightOutlined } from '@ant-design/icons'
 
 import asyncComponent from '@/utils/asyncComponent'
 import Api from '@/api'
@@ -11,6 +12,7 @@
 import './index.scss'
 
 const CardItem = asyncComponent(() => import('../cardItem'))
+const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 
 class PropCard extends Component {
   static propTpyes = {
@@ -24,8 +26,9 @@
     config: null,              // 鍥捐〃閰嶇疆淇℃伅
     loading: false,            // 鏁版嵁鍔犺浇鐘舵��
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
-    data: {},                  // 鏁版嵁
-    BData: null
+    data: {$$empty: true},
+    BData: null,
+    visible: false
   }
 
   UNSAFE_componentWillMount () {
@@ -66,6 +69,7 @@
       }
     }
 
+    _data.$$uuid = _data[_config.setting.primaryKey] || ''
     _data.$$BID = BID || ''
     _data.$$BData = BData || ''
 
@@ -74,8 +78,13 @@
       _cols.set(item.field, item)
     })
 
+    if (!_config.wrap.height) { // 鍏煎
+      _config.wrap.height = _config.style.height || '300px'
+      delete _config.style.height
+    }
+
     _config.subcards.forEach(card => {
-      card.style.height = _config.style.height
+      card.style.height = _config.wrap.height
       if (card.setting.click) {
         card.style.cursor = 'pointer'
       }
@@ -101,6 +110,8 @@
         setTimeout(() => {
           this.loadData()
         }, _config.setting.delay || 0)
+      } else if (_config.wrap.display === 'modal' && _config.wrap.datatype === 'static') {
+        this.openModal()
       }
     })
   }
@@ -152,15 +163,39 @@
         }
       }
 
+      _data.$$uuid = _data[config.setting.primaryKey] || ''
       _data.$$BID = BID || ''
       _data.$$BData = BData || ''
 
-      this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({sync: false, data: _data}, () => {
+        if (config.wrap.display === 'modal') {
+          this.openModal()
+        }
+      })
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
     }
+  }
+
+  openModal = () => {
+    const { config, data } = this.state
+    
+    let code = config.wrap.code || ('modal' + config.uuid)
+    let tip = localStorage.getItem(code)
+
+    if ((data.$$uuid && tip === data.$$uuid) || (!data.$$uuid && tip)) return
+
+    if (config.wrap.modalContent === 'update') {
+      localStorage.setItem(code, data.$$uuid || 'true')
+
+      Api.getAppVersion(true)
+    }
+
+    setTimeout(() => {
+      this.setState({visible: true})
+    }, 200)
   }
 
   /**
@@ -264,31 +299,94 @@
       } else {
         _data = result.data[0]
       }
-
+      
+      _data.$$uuid = _data[config.setting.primaryKey] || ''
       _data.$$BID = BID || ''
       _data.$$BData = BData || ''
 
       this.setState({
         data: _data,
         loading: false
+      }, () => {
+        if (config.wrap.display === 'modal') {
+          this.openModal()
+        }
       })
     } else {
       this.setState({
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    }
+  }
+
+  onTipChange = (e) => {
+    const { config, data } = this.state
+
+    let code = config.wrap.code || ('modal' + config.uuid)
+    if (e.target.checked) {
+      localStorage.setItem(code, data.$$uuid || 'true')
+    } else {
+      localStorage.removeItem(code)
     }
   }
 
   render() {
-    const { config, loading, data } = this.state
+    const { config, loading, data, visible } = this.state
 
-    if (config.wrap.empty === 'hidden' && (!data || data.$$empty)) return null
+    if (config.wrap.empty === 'hidden' && data.$$empty) return null
+
+    if (config.wrap.display === 'modal') {
+      return <Modal
+        wrapClassName='mk-carousel-modal'
+        visible={visible}
+        maskClosable={false}
+        width={config.wrap.modalWidth}
+        onCancel={() => this.setState({visible: false})}
+        footer={null}
+        destroyOnClose
+      >
+        <div className="custom-prop-carousel-box" style={config.style}>
+          <NormalHeader config={config}/>
+          <div className="carousel-wrap" style={{height: config.wrap.height}}>
+            {config.subcards.length > 1 && config.wrap.autoplay === 'false' ? <div className="prev" onClick={() => this.node && this.node.prev()}><LeftOutlined /></div> : null}
+            {config.subcards.length > 1 && config.wrap.autoplay === 'false' ? <div className="next" onClick={() => this.node && this.node.next()}><RightOutlined /></div> : null}
+            <Carousel
+              autoplay={config.wrap.autoplay !== 'false'}
+              dots={config.wrap.dots !== 'false'}
+              dotPosition={config.wrap.dotPosition || 'bottom'}
+              effect={config.wrap.effect || 'scrollx'}
+              autoplaySpeed={config.wrap.speed}
+              ref={ref => this.node = ref}
+            >
+              {config.subcards.map((item, index) => (
+                <div key={index}>
+                  <CardItem card={item} cards={config} data={data}/>
+                </div>
+              ))}
+            </Carousel>
+          </div>
+        </div>
+        {config.wrap.modalContent !== 'update' ? <div className="mk-footer">
+          <Checkbox defaultChecked={false} onChange={this.onTipChange}>涓嶅湪鎻愰啋</Checkbox>
+          <Button className="close" onClick={() => this.setState({visible: false})}>绔嬪嵆浣撻獙</Button>
+        </div> : <div className="mk-footer">
+          <Button className="close" onClick={() => this.setState({visible: false})}>鐭ラ亾浜�</Button>
+        </div>}
+      </Modal>
+    }
 
     return (
       <div className="custom-prop-carousel-box" id={'anchor' + config.uuid} style={config.style}>
@@ -298,19 +396,22 @@
             <Spin />
           </div> : null
         }
-        <Carousel
-          autoplay={config.wrap.autoplay !== 'false'}
-          dots={config.wrap.dots !== 'false'}
-          dotPosition={config.wrap.dotPosition || 'bottom'}
-          effect={config.wrap.effect || 'scrollx'}
-          autoplaySpeed={config.wrap.speed}
-        >
-          {config.subcards.map((item, index) => (
-            <div key={index}>
-              <CardItem card={item} cards={config} data={data}/>
-            </div>
-          ))}
-        </Carousel>
+        <NormalHeader config={config}/>
+        <div className="carousel-wrap" style={{height: config.wrap.height}}>
+          <Carousel
+            autoplay={config.wrap.autoplay !== 'false'}
+            dots={config.wrap.dots !== 'false'}
+            dotPosition={config.wrap.dotPosition || 'bottom'}
+            effect={config.wrap.effect || 'scrollx'}
+            autoplaySpeed={config.wrap.speed}
+          >
+            {config.subcards.map((item, index) => (
+              <div key={index}>
+                <CardItem card={item} cards={config} data={data}/>
+              </div>
+            ))}
+          </Carousel>
+        </div>
       </div>
     )
   }
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.scss b/src/tabviews/custom/components/carousel/prop-card/index.scss
index b397692..6c5d3ee 100644
--- a/src/tabviews/custom/components/carousel/prop-card/index.scss
+++ b/src/tabviews/custom/components/carousel/prop-card/index.scss
@@ -5,13 +5,17 @@
   background-size: cover;
   min-height: 30px;
   position: relative;
-  overflow: hidden;
 
   .card-item-box {
     position: relative;
     background-position: center center;
     background-repeat: no-repeat;
     background-size: cover;
+    overflow: hidden;
+  }
+
+  .carousel-wrap {
+    position: relative;
     overflow: hidden;
   }
 
@@ -42,3 +46,82 @@
   display: block;
   clear: both;
 }
+
+.mk-carousel-modal {
+  .ant-modal-body {
+    padding: 20px;
+  }
+  .ant-modal-close-x {
+    width: 40px;
+    height: 40px;
+    font-size: 14px;
+    line-height: 40px;
+  }
+  .card-item-box {
+    overflow-y: auto!important;
+  }
+  .card-item-box::-webkit-scrollbar {
+    width: 7px;
+  }
+  .card-item-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);
+  }
+  .card-item-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-carousel {
+    height: 100%;
+    .slick-slider {
+      height: 100%;
+    }
+  }
+  .prev {
+    position: absolute;
+    top: calc(50% - 15px);
+    left: 0px;
+    z-index: 1;
+    cursor: pointer;
+    color: #ffffff;
+    padding: 5px 3px;
+    background: rgba(0,0,0,0.15);
+  }
+  .next {
+    position: absolute;
+    top: calc(50% - 15px);
+    right: 0px;
+    z-index: 1;
+    cursor: pointer;
+    color: #ffffff;
+    padding: 5px 3px;
+    background: rgba(0,0,0,0.15);
+  }
+  .mk-footer {
+    padding-top: 10px;
+    .ant-checkbox-wrapper {
+      font-size: 13px;
+      line-height: 26px;
+    }
+    .ant-checkbox-inner {
+      width: 15px;
+      height: 15px;
+    }
+    .close {
+      height: 32px;
+      color: #ffffff;
+      background-color: var(--mk-sys-color);
+      border-color: var(--mk-sys-color);
+      padding: 0 20px;
+      float: right;
+    }
+  }
+  .mk-footer::after {
+    content: ' ';
+    display: block;
+    clear: both;
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/chart/antv-bar-line/asyncButtonComponent.jsx b/src/tabviews/custom/components/chart/antv-bar-line/asyncButtonComponent.jsx
index 5fb9c1a..f596832 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/asyncButtonComponent.jsx
+++ b/src/tabviews/custom/components/chart/antv-bar-line/asyncButtonComponent.jsx
@@ -21,7 +21,12 @@
       this.setState({component})
     }
 
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+    
     render() {
       const C = this.state.component
       const btn = this.props.btn || {}
diff --git a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
index 2e6524a..e9e806e 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -3,7 +3,7 @@
 import { is, fromJS } from 'immutable'
 import { Chart } from '@antv/g2'
 import DataSet from '@antv/data-set'
-import { Spin, Empty, notification } from 'antd'
+import { Spin, Empty, notification, Modal } from 'antd'
 import { DownloadOutlined } from '@ant-design/icons'
 import moment from 'moment'
 
@@ -108,7 +108,13 @@
       })
     }
 
-    _config.style.height = config.plot.height || 400
+    _config.plot.height = Utils.getHeight(_config.plot.height)
+    _config.style.height = 'auto'
+    _config.style.minHeight = _config.plot.height + 30
+
+    if (_config.plot.title) {
+      _config.style.minHeight = _config.style.minHeight + 45
+    }
 
     let transfield = {}
     _config.columns.forEach(col => {
@@ -364,7 +370,7 @@
       this.setState({sync: false, data: _data}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -534,11 +540,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -550,7 +563,9 @@
     if (_element) {
       _element.innerHTML = ''
     }
-    this.viewrender()
+    setTimeout(() => {
+      this.viewrender()
+    }, 100)
   }
 
   /**
@@ -896,7 +911,7 @@
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 25
+      height: plot.height
     })
 
     chart.data(_data)
@@ -1080,7 +1095,7 @@
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 25
+      height: plot.height
     })
     
     // 鍧愭爣杞存牸寮忓寲
@@ -1490,7 +1505,7 @@
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 25
+      height: plot.height
     })
 
     chart.data(_data)
@@ -1710,8 +1725,6 @@
     chart.on('element:click', (ev) => {
       let data = ev.data.data
 
-      MKEmitter.emit('resetSelectLine', config.uuid, (data ? data.$$uuid : ''), data)
-
       if (plot.click === 'menus') {
         let menu = null
         
@@ -1740,16 +1753,23 @@
         } else {
           MKEmitter.emit('modifyTabs', newtab, 'plus', true)
         }
-      } else if (plot.click === 'menu' && plot.MenuID) {
-        let menu = {
-          MenuID: plot.MenuID,
-          MenuName: plot.MenuName,
-          MenuNo: plot.MenuNo,
-          type: plot.tabType
+      } else if (plot.click === 'menu') {
+        let menuId = plot.menu.slice(-1)[0]
+        let newtab = window.GLOB.mkThdMenus.filter(m => m.MenuID === menuId)[0]
+
+        if (!newtab && plot.MenuID) {
+          newtab = {
+            MenuID: plot.MenuID,
+            MenuName: plot.MenuName,
+            MenuNo: plot.MenuNo,
+            type: plot.tabType
+          }
+        } else if (!newtab) {
+          return
         }
   
-        let newtab = {
-          ...menu,
+        newtab = {
+          ...newtab,
           param: {}
         }
   
@@ -1762,6 +1782,8 @@
         } else {
           MKEmitter.emit('modifyTabs', newtab, 'plus', true)
         }
+      } else {
+        MKEmitter.emit('resetSelectLine', config.uuid, (data ? data.$$uuid : ''), data)
       }
     })
 
@@ -1839,7 +1861,7 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
-        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+        <div className="canvas-wrap">
           {config.plot.download === 'enable' && this.state.chart && !empty ? <DownloadOutlined onClick={this.downloadImage} className="system-color download"/> : null}
           <div className={'chart-action' + (config.plot.download === 'enable' ? ' downable' : '')}>
             {config.action.map(item => {
diff --git a/src/tabviews/custom/components/chart/antv-bar-line/index.scss b/src/tabviews/custom/components/chart/antv-bar-line/index.scss
index 04b5487..f0903f8 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/index.scss
+++ b/src/tabviews/custom/components/chart/antv-bar-line/index.scss
@@ -5,13 +5,10 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
-  display: flex;
-  flex-flow: column;
 
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    flex: 1;
     .chart-action {
       position: absolute;
       top: 0px;
@@ -43,9 +40,8 @@
 
   .canvas {
     margin: 0;
-    padding: 15px 10px 10px;
+    padding: 15px;
     letter-spacing: 0px;
-    height: 100%;
   }
   .canvas.empty {
     div {
diff --git a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
index bbda7f1..25aae9e 100644
--- a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Chart, registerShape } from '@antv/g2'
-import { Spin, notification } from 'antd'
+import { Spin, notification, Modal } from 'antd'
 import { DownloadOutlined } from '@ant-design/icons'
 import moment from 'moment'
 
@@ -107,7 +107,13 @@
       }
     }
 
-    _config.style.height = config.plot.height || 400
+    _config.plot.height = Utils.getHeight(_config.plot.height)
+    _config.style.height = 'auto'
+    _config.style.minHeight = _config.plot.height + 30
+
+    if (_config.plot.title) {
+      _config.style.minHeight = _config.style.minHeight + 45
+    }
 
     this.setState({
       config: _config,
@@ -155,7 +161,7 @@
       this.setState({sync: false, data: _data}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -278,11 +284,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -327,7 +340,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: plot.height
     })
     
     chart.data(data)
@@ -436,7 +449,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: plot.height,
       padding: [0, 0, 0, 0],
     })
     chart.data([_data])
@@ -620,7 +633,7 @@
           </div> : null
         }
         <NormalHeader config={config} />
-        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+        <div className="canvas-wrap">
           {config.plot.download === 'enable' && this.state.chart ? <DownloadOutlined onClick={this.downloadImage} className="system-color download"/> : null}
           <div className="canvas" id={this.state.chartId}></div>
         </div>
diff --git a/src/tabviews/custom/components/chart/antv-dashboard/index.scss b/src/tabviews/custom/components/chart/antv-dashboard/index.scss
index 89aa840..aeb6028 100644
--- a/src/tabviews/custom/components/chart/antv-dashboard/index.scss
+++ b/src/tabviews/custom/components/chart/antv-dashboard/index.scss
@@ -5,13 +5,10 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
-  display: flex;
-  flex-flow: column;
   
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    flex: 1;
     .download {
       position: absolute;
       z-index: 1;
diff --git a/src/tabviews/custom/components/chart/antv-pie/index.jsx b/src/tabviews/custom/components/chart/antv-pie/index.jsx
index 8916b90..8662310 100644
--- a/src/tabviews/custom/components/chart/antv-pie/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-pie/index.jsx
@@ -3,7 +3,7 @@
 import { is, fromJS } from 'immutable'
 import { Chart } from '@antv/g2'
 import DataSet, { DataView } from '@antv/data-set'
-import { Spin, Empty, notification } from 'antd'
+import { Spin, Empty, notification, Modal } from 'antd'
 import { DownloadOutlined } from '@ant-design/icons'
 import moment from 'moment'
 
@@ -64,7 +64,13 @@
       _sync = false
     }
 
-    _config.style.height = config.plot.height || 400
+    _config.plot.height = Utils.getHeight(_config.plot.height)
+    _config.style.height = 'auto'
+    _config.style.minHeight = _config.plot.height + 30
+
+    if (_config.plot.title) {
+      _config.style.minHeight = _config.style.minHeight + 45
+    }
 
     let decimal = 0
     _config.columns.forEach(col => {
@@ -110,7 +116,7 @@
       this.setState({sync: false, data: _data}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -239,11 +245,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -371,14 +384,15 @@
           return
         }
 
-        let sign = item[plot.type] + item[plot.Xaxis]
+        item.$type = item[plot.type]
+        let sign = item.$type + item[plot.Xaxis]
 
         if (!_mdata.has(sign)) {
           let _sort = sort
-          if (map.has(item.type)) {
-            _sort = map.get(item.type)
+          if (map.has(item.$type)) {
+            _sort = map.get(item.$type)
           } else {
-            map.set(item.type, _sort)
+            map.set(item.$type, _sort)
             sort++
           }
 
@@ -414,14 +428,15 @@
           return
         }
 
-        let sign = item[plot.type] + item[plot.Xaxis]
+        item.$type = item[plot.type]
+        let sign = item.$type + item[plot.Xaxis]
 
         if (!_mdata.has(sign)) {
           let _sort = sort
-          if (map.has(item.type)) {
-            _sort = map.get(item.type)
+          if (map.has(item.$type)) {
+            _sort = map.get(item.$type)
           } else {
-            map.set(item.type, _sort)
+            map.set(item.$type, _sort)
             sort++
           }
 
@@ -456,11 +471,13 @@
           }
         }
 
+        item.$type = item[plot.type]
+
         let _sort = sort
-        if (map.has(item.type)) {
-          _sort = map.get(item.type)
+        if (map.has(item.$type)) {
+          _sort = map.get(item.$type)
         } else {
-          map.set(item.type, _sort)
+          map.set(item.$type, _sort)
           sort++
         }
 
@@ -528,7 +545,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 30,
+      height: plot.height,
       padding: 0,
     })
 
@@ -724,7 +741,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 30
+      height: plot.height
     })
 
     if (plot.shape !== 'nightingale' && plot.show !== 'value') {
@@ -1032,7 +1049,7 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
-        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+        <div className="canvas-wrap">
           {config.plot.download === 'enable' && this.state.chart && !empty ? <DownloadOutlined onClick={this.downloadImage} className="system-color download"/> : null}
           <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
         </div>
diff --git a/src/tabviews/custom/components/chart/antv-pie/index.scss b/src/tabviews/custom/components/chart/antv-pie/index.scss
index 223d368..38e9566 100644
--- a/src/tabviews/custom/components/chart/antv-pie/index.scss
+++ b/src/tabviews/custom/components/chart/antv-pie/index.scss
@@ -5,13 +5,10 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
-  display: flex;
-  flex-flow: column;
   
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    flex: 1;
     .download {
       position: absolute;
       z-index: 1;
@@ -33,7 +30,6 @@
     margin: 0;
     padding: 15px;
     letter-spacing: 0px;
-    height: 100%;
   }
   .canvas.empty {
     div {
diff --git a/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx b/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx
index 5fb9c1a..f596832 100644
--- a/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx
+++ b/src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx
@@ -21,7 +21,12 @@
       this.setState({component})
     }
 
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+    
     render() {
       const C = this.state.component
       const btn = this.props.btn || {}
diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.jsx b/src/tabviews/custom/components/chart/antv-scatter/index.jsx
index bffcc3b..644a98f 100644
--- a/src/tabviews/custom/components/chart/antv-scatter/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-scatter/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Chart } from '@antv/g2'
-import { Spin, Empty, notification } from 'antd'
+import { Spin, Empty, notification, Modal } from 'antd'
 import { DownloadOutlined } from '@ant-design/icons'
 import moment from 'moment'
 
@@ -64,7 +64,13 @@
       _sync = false
     }
 
-    _config.style.height = config.plot.height || 400
+    _config.plot.height = Utils.getHeight(_config.plot.height)
+    _config.style.height = 'auto'
+    _config.style.minHeight = _config.plot.height + 30
+
+    if (_config.plot.title) {
+      _config.style.minHeight = _config.style.minHeight + 45
+    }
 
     this.setState({
       config: _config,
@@ -101,7 +107,7 @@
       this.setState({sync: false, data: _data, empty: !_data,}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -273,11 +279,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -300,7 +313,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: this.wrap.offsetHeight - 25
+      height: plot.height
     })
 
     chart.data(data);
@@ -415,7 +428,7 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
-        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+        <div className="canvas-wrap">
           {config.plot.download === 'enable' && this.state.chart && !empty ? <DownloadOutlined onClick={this.downloadImage} className="system-color download"/> : null}
           <div className={'chart-action' + (config.plot.download === 'enable' ? ' downable' : '')}>
             {config.action.map(item => {
diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.scss b/src/tabviews/custom/components/chart/antv-scatter/index.scss
index eaad237..9faf7ac 100644
--- a/src/tabviews/custom/components/chart/antv-scatter/index.scss
+++ b/src/tabviews/custom/components/chart/antv-scatter/index.scss
@@ -5,13 +5,10 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
-  display: flex;
-  flex-flow: column;
 
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    flex: 1;
     .chart-action {
       position: absolute;
       top: 0px;
@@ -44,9 +41,8 @@
 
   .canvas {
     margin: 0;
-    padding: 15px 10px 10px;
+    padding: 15px;
     letter-spacing: 0px;
-    height: 100%;
   }
   .canvas.empty {
     div {
diff --git a/src/tabviews/custom/components/chart/custom-chart/index.jsx b/src/tabviews/custom/components/chart/custom-chart/index.jsx
index 2680e59..a3f4b6d 100644
--- a/src/tabviews/custom/components/chart/custom-chart/index.jsx
+++ b/src/tabviews/custom/components/chart/custom-chart/index.jsx
@@ -58,7 +58,8 @@
       _sync = false
     }
 
-    _config.style.height = config.plot.height || 400
+    _config.plot.height = Utils.getHeight(_config.plot.height)
+    _config.style.height = 'auto'
 
     this.setState({
       config: _config,
@@ -95,7 +96,7 @@
       this.setState({sync: false, data: _data, empty: _data.length === 0}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -347,7 +348,9 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} refresh={this.refreshSearch} />
-        <div className={'canvas' + (empty ? ' empty' : '')} ref={ref => this.wrap = ref}></div>
+        <div className="canvas-wrap" style={{height: (config.plot.height + 30) + 'px'}}>
+          <div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 'px'}} ref={ref => this.wrap = ref}></div>
+        </div>
         {empty ? <Empty description={false}/> : null}
       </div>
     )
diff --git a/src/tabviews/custom/components/chart/custom-chart/index.scss b/src/tabviews/custom/components/chart/custom-chart/index.scss
index 009f7a6..199c4b5 100644
--- a/src/tabviews/custom/components/chart/custom-chart/index.scss
+++ b/src/tabviews/custom/components/chart/custom-chart/index.scss
@@ -5,14 +5,15 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
-  display: flex;
-  flex-flow: column;
 
+  .canvas-wrap {
+    padding: 15px;
+  }
   .canvas {
     margin: 0;
     padding: 0px;
     letter-spacing: 0px;
-    flex: 1;
+    height: 100%;
   }
   .canvas.empty {
     div {
diff --git a/src/tabviews/custom/components/code/sand-box/index.jsx b/src/tabviews/custom/components/code/sand-box/index.jsx
index 66338af..2456f8b 100644
--- a/src/tabviews/custom/components/code/sand-box/index.jsx
+++ b/src/tabviews/custom/components/code/sand-box/index.jsx
@@ -48,15 +48,9 @@
 
       if (_sync && data) {
         _data = data[_config.dataName] || {}
-        if (_data && Array.isArray(_data)) {
-          _data = _data[0] || {}
-        }
         _sync = false
       } else if (_sync && initdata) {
         _data = initdata || {}
-        if (_data && Array.isArray(_data)) {
-          _data = _data[0] || {}
-        }
         _sync = false
       }
     } else {
@@ -64,7 +58,11 @@
     }
     
     if (_config.css) {
+      let node = document.getElementById(_config.uuid)
+      node && node.remove()
+
       let ele = document.createElement('style')
+      ele.id = _config.uuid
       ele.innerHTML = _config.css
       document.getElementsByTagName('head')[0].appendChild(ele)
     }
@@ -110,15 +108,12 @@
       let _data = {}
       if (nextProps.data && nextProps.data[config.dataName]) {
         _data = nextProps.data[config.dataName]
-        if (_data && Array.isArray(_data)) {
-          _data = _data[0]
-        }
       }
 
       this.setState({sync: false, data: _data}, () => {
         this.renderView()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -167,7 +162,7 @@
 
     let result = await Api.genericInterface(param)
     if (result.status) {
-      let _data = result.data && result.data[0] ? result.data[0] : {}
+      let _data = result.data || {}
 
       this.setState({
         data: _data,
@@ -192,23 +187,34 @@
     const { html, js, wrap, columns } = this.state.config
 
     let _html = html
-    if (_html && wrap.datatype !== 'static') {
+    if (_html && wrap.datatype !== 'static' && wrap.compileMode !== 'custom') {
+      let _data = data
+      if (Array.isArray(_data)) {
+        _data = _data[0] || {}
+      }
       columns.forEach(col => {
         if (col.field) {
-          let val = (data[col.field] || data[col.field] === 0) ? data[col.field] : ''
+          let val = (_data[col.field] || _data[col.field] === 0) ? _data[col.field] : ''
           let reg = new RegExp('@' + col.field + '@', 'ig')
 
           _html = _html.replace(reg, val)
         }
       })
     }
+
     this.setState({html: _html}, () => {
       if (js) {
         try {
-          // eslint-disable-next-line no-eval
-          eval(js)
+          if (wrap.compileMode !== 'custom') {
+            // eslint-disable-next-line no-eval
+            eval(js)
+          } else {
+            // eslint-disable-next-line
+            let evalfunc = eval('(true && function (data) {' + js + '})')
+            evalfunc(data)
+          }
         } catch (e) {
-          console.warn('JS 鎵ц澶辫触锛�')
+          console.warn(e)
         }
       }
     })
diff --git a/src/tabviews/custom/components/editor/braft-editor/index.jsx b/src/tabviews/custom/components/editor/braft-editor/index.jsx
index e8f4f4b..3a2a484 100644
--- a/src/tabviews/custom/components/editor/braft-editor/index.jsx
+++ b/src/tabviews/custom/components/editor/braft-editor/index.jsx
@@ -116,7 +116,7 @@
       }
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
diff --git a/src/tabviews/custom/components/form/simple-form/index.jsx b/src/tabviews/custom/components/form/simple-form/index.jsx
index 5711995..cecadd1 100644
--- a/src/tabviews/custom/components/form/simple-form/index.jsx
+++ b/src/tabviews/custom/components/form/simple-form/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification } from 'antd'
+import { Spin, notification, Modal, Button } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -57,8 +57,8 @@
     if (config.wrap.datatype !== 'static') {
       _sync = config.setting.sync === 'true'
 
-      if (_sync && data && data[config.dataName]) {
-        _data = data[config.dataName]
+      if (_sync && data) {
+        _data = data[config.dataName] || {$$empty: true}
         if (Array.isArray(_data)) {
           _data = _data[0] || {$$empty: true}
         }
@@ -70,7 +70,7 @@
 
     let _group = config.subcards[0]
 
-    if (_group.subButton.enable === 'false') {
+    if (_group.subButton.enable === 'false' && (!_group.closeButton || _group.closeButton.enable !== 'true')) {
       _group.subButton.style.display = 'none'
       _group.$button = 'no-button'
     }
@@ -129,7 +129,7 @@
       }
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -258,11 +258,18 @@
       this.setState({
         loading: false,
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -274,6 +281,12 @@
     this.formRef.handleConfirm().then(res => {
       MKEmitter.emit('triggerFormSubmit', {menuId: btnId, form: res})
     })
+  }
+
+  closeTab = () => {
+    const { group } = this.state
+
+    MKEmitter.emit('closeTabView', group.subButton.$MenuID)
   }
 
   render() {
@@ -305,6 +318,7 @@
             columns={config.columns}
             selectedData={data.$$empty ? [] : [data]}
           />
+          {group.closeButton && group.closeButton.enable === 'true' ? <Button type="link" onClick={this.closeTab} style={group.closeButton.style}>{group.closeButton.label}</Button> : null}
         </div> : null}
       </div>
     )
diff --git a/src/tabviews/custom/components/form/step-form/index.jsx b/src/tabviews/custom/components/form/step-form/index.jsx
index d942371..c9c0773 100644
--- a/src/tabviews/custom/components/form/step-form/index.jsx
+++ b/src/tabviews/custom/components/form/step-form/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Button } from 'antd'
+import { Spin, notification, Button, Modal } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -56,8 +56,8 @@
     if (config.wrap.datatype !== 'static') {
       _sync = config.setting.sync === 'true'
 
-      if (_sync && data && data[config.dataName]) {
-        _data = data[config.dataName]
+      if (_sync && data) {
+        _data = data[config.dataName] || {$$empty: true}
         if (Array.isArray(_data)) {
           _data = _data[0] || {$$empty: true}
         }
@@ -77,10 +77,10 @@
 
     config.subcards = config.subcards.map((group, i) => {
       group.sort = i + 1
-      if (i === 0) {
+      if (i === 0 && group.prevButton.actionType !== 'close') {
         group.prevButton.enable = 'false'
       }
-      if (i + 1 === config.subcards.length) {
+      if (i + 1 === config.subcards.length && group.nextButton.actionType !== 'close') {
         group.nextButton.enable = 'false'
       }
 
@@ -166,7 +166,7 @@
       }
 
       this.setState({sync: false, data: _data, group: _group, step: _group.sort - 1,})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -326,11 +326,18 @@
       this.setState({
         loading: false,
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -347,6 +354,11 @@
   prevStep = () => {
     const { config, group } = this.state
 
+    if (group.prevButton.actionType === 'close') {
+      MKEmitter.emit('closeTabView', group.subButton.$MenuID)
+      return
+    }
+
     let _group = config.subcards.filter(item => item.sort === (group.sort - 1))[0]
 
     this.setState({group: null, step: group.sort - 2}, () => {
@@ -357,6 +369,11 @@
   nextStep = () => {
     const { config, group } = this.state
 
+    if (group.nextButton.actionType === 'close') {
+      MKEmitter.emit('closeTabView', group.subButton.$MenuID)
+      return
+    }
+
     let _group = config.subcards.filter(item => item.sort === (group.sort + 1))[0]
 
     this.setState({group: null, step: group.sort}, () => {
diff --git a/src/tabviews/custom/components/form/step-form/index.scss b/src/tabviews/custom/components/form/step-form/index.scss
index 77767fb..06b56f4 100644
--- a/src/tabviews/custom/components/form/step-form/index.scss
+++ b/src/tabviews/custom/components/form/step-form/index.scss
@@ -71,8 +71,10 @@
       border: none;
     }
     .skip {
-      float: right;
+      color: rgba(0, 0, 0, 0.85);
+      position: absolute;
       height: auto;
+      right: 0px;
     }
   }
   .mk-form-action.no-button {
diff --git a/src/tabviews/custom/components/form/tab-form/index.jsx b/src/tabviews/custom/components/form/tab-form/index.jsx
index 870b4ad..18716aa 100644
--- a/src/tabviews/custom/components/form/tab-form/index.jsx
+++ b/src/tabviews/custom/components/form/tab-form/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification } from 'antd'
+import { Spin, notification, Modal } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -55,8 +55,8 @@
     if (config.wrap.datatype !== 'static') {
       _sync = config.setting.sync === 'true'
 
-      if (_sync && data && data[config.dataName]) {
-        _data = data[config.dataName]
+      if (_sync && data) {
+        _data = data[config.dataName] || {$$empty: true}
         if (Array.isArray(_data)) {
           _data = _data[0] || {$$empty: true}
         }
@@ -140,7 +140,7 @@
       this.setState({sync: false, data: _data, group: null}, () => {
         this.setState({group: _group})
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -268,11 +268,18 @@
       this.setState({
         loading: false,
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/group/normal-group/index.jsx b/src/tabviews/custom/components/group/normal-group/index.jsx
index c8e1631..ac0cc08 100644
--- a/src/tabviews/custom/components/group/normal-group/index.jsx
+++ b/src/tabviews/custom/components/group/normal-group/index.jsx
@@ -139,123 +139,129 @@
     const { mainSearch, data } = this.state
 
     return config.components.map(item => {
-      if (item.type === 'bar' || item.type === 'line') {
+      let style = null
+
+      if (item.style && item.style.clear === 'left') {
+        style = {clear: 'left'}
+      }
+
+      if (item.type === 'card' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvBarAndLine data={data} config={item} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'pie') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvPie data={data} config={item} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'dashboard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'simpleform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'stepform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <NormalForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'tabform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <TabForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'scatter') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'carousel' && item.subtype === 'datacard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <CarouselDataCard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'carousel' && item.subtype === 'propcard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <CarouselPropCard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'card' && item.subtype === 'datacard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <DataCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'propcard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <PropCard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'table' && item.subtype === 'tablecard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <TableCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'table' && item.subtype === 'normaltable') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalTable config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'bar' || item.type === 'line') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvBarAndLine data={data} config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'pie') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvPie data={data} config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'dashboard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'simpleform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TabForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'scatter') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'carousel' && item.subtype === 'datacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <CarouselDataCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'carousel' && item.subtype === 'propcard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <CarouselPropCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'card' && item.subtype === 'tablecard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TableCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'table' && item.subtype === 'editable') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <EditTable config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'tree') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalTree config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'editor') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <BraftEditor config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'code') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <SandBox config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'balcony') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <Balcony config={item} data={data}/>
           </Col>
         )
       } else if (item.type === 'timeline') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TimeLine config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'chart') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CustomChart config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
@@ -265,12 +271,32 @@
     })
   }
 
+  canvasToImage(canvas) {
+    let image = new Image()
+    image.src = canvas.toDataURL('image/jpg')
+    image.style = 'width:100%;height:100%;position:absolute;z-index:1;left:0px;top:0px;'
+    return image
+  }
+
   print = () => {
     const { config } = this.props
     const { printing } = this.state
 
     if (printing) return
     this.setState({printing: true})
+
+    let qrcodes = document.getElementsByClassName('qrcode-box')
+
+    for (let i = 0; i < qrcodes.length; i++) {
+      let canvas = qrcodes[i].getElementsByTagName('canvas')[0]
+
+      if (canvas) {
+        let img = this.canvasToImage(canvas)
+  
+        canvas.remove()
+        qrcodes[i].append(img)
+      }
+    }
 
     let pageSize = ['A4', 'A3', 'A5'].includes(config.setting.pageSize) ? config.setting.pageSize : 'A4'
     let pageLayout = config.setting.pageLayout !== 'horizontal' ? 'vertical' : 'horizontal'
@@ -345,6 +371,9 @@
     
     return (
       <div className={'normal-group-wrap ' + (config.setting.layout || '')} id={config.uuid} style={config.style}>
+        {config.setting && config.setting.title ? <div className="group-header" style={config.headerStyle}>
+          <span className="title">{config.setting.title}</span>
+        </div> : null}
         {config.setting && config.setting.print === 'true' ? <Button className="print-button" icon="printer" loading={printing} onClick={this.print}></Button> : null}
         <Row className="component-wrap">{this.getComponents()}</Row>
       </div>
diff --git a/src/tabviews/custom/components/group/normal-group/index.scss b/src/tabviews/custom/components/group/normal-group/index.scss
index 419bf1b..a179389 100644
--- a/src/tabviews/custom/components/group/normal-group/index.scss
+++ b/src/tabviews/custom/components/group/normal-group/index.scss
@@ -7,6 +7,28 @@
     border: 0;
     background: transparent;
   }
+
+  .group-header {
+    position: relative;
+    height: 45px;
+    padding-right: 8px;
+    border-bottom: 1px solid #e8e8e8;
+    overflow: hidden;
+    letter-spacing: 0px;
+    line-height: 45px;
+    box-sizing: content-box;
+  
+    .title {
+      text-decoration: inherit;
+      font-weight: inherit;
+      font-style: inherit;
+      float: left;
+      line-height: inherit;
+      margin-left: 10px;
+      position: relative;
+      z-index: 1;
+    }
+  }
 }
 
 .normal-group-wrap::after {
diff --git a/src/tabviews/custom/components/iframe/index.jsx b/src/tabviews/custom/components/iframe/index.jsx
new file mode 100644
index 0000000..e8d4fd5
--- /dev/null
+++ b/src/tabviews/custom/components/iframe/index.jsx
@@ -0,0 +1,249 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Input, Modal, notification, Empty } from 'antd'
+
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const { Search } = Input
+
+class NormalIframe extends Component {
+  static propTpyes = {
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    activeKey: '',             // 閫変腑鏁版嵁
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: {},                  // 鏁版嵁
+    linkUrl: ''
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data } = this.props
+    let _config = fromJS(this.props.config).toJS()
+
+    let _data = { $$empty: true }
+    let _sync = false
+
+    let BID = ''
+    let BData = ''
+    let linkUrl = ''
+
+    if (_config.setting.supModule) {
+      BData = window.GLOB.CacheData.get(_config.setting.supModule)
+    } else {
+      BData = window.GLOB.CacheData.get(_config.$pageId)
+    }
+    if (BData) {
+      BID = BData.$BID || ''
+    }
+    
+    if (_config.wrap.datatype === 'dynamic') {
+      _sync = _config.setting.sync === 'true'
+
+      if (_sync && data) {
+        _data = data[_config.dataName] || {$$empty: true}
+        if (Array.isArray(_data)) {
+          _data = _data[0] || {$$empty: true}
+        }
+        _sync = false
+      }
+
+      linkUrl = _data[_config.wrap.linkField] || ''
+    } else {
+      if (_config.wrap.linkType !== 'input') {
+        linkUrl = _config.wrap.linkUrl || ''
+      }
+    }
+
+    this.setState({
+      linkUrl: linkUrl,
+      sync: _sync,
+      data: _data,
+      BID: BID || '',
+      config: _config,
+      arr_field: _config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (_config.wrap.datatype === 'dynamic' && _config.setting.sync !== 'true' && _config.setting.onload === 'true') {
+        setTimeout(() => {
+          this.loadData()
+        }, _config.setting.delay || 0)
+      }
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+
+    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+  }
+
+  /**
+   * @description 鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   */
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = { $$empty: true }
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName]
+        if (Array.isArray(_data)) {
+          _data = _data[0] || {$$empty: true}
+        }
+      }
+
+      let linkUrl = _data[config.wrap.linkField] || ''
+
+      this.setState({sync: false, data: _data, linkUrl})
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({}, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  resetParentParam = (MenuID, id, data) => {
+    const { config } = this.state
+
+    if (!config.setting.supModule || config.setting.supModule !== MenuID) return
+    
+    if (id !== this.state.BID || id !== '') {
+      this.setState({ BID: id }, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  reloadData = (menuId) => {
+    const { config } = this.state
+
+    if (menuId !== config.uuid) return
+
+    this.loadData()
+  }
+
+  async loadData () {
+    const { mainSearch } = this.props
+    const { config, arr_field, BID } = this.state
+    
+    if (config.wrap.datatype === 'static') {
+      this.setState({
+        data: {$$empty: true}
+      })
+      return
+    } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: {$$empty: true}
+      })
+      return
+    }
+
+    let searches = config.setting.useMSearch && mainSearch ? mainSearch : []
+
+    let requireFields = searches.filter(item => item.required && item.value === '')
+    if (requireFields.length > 0) {
+      return
+    }
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let _data = {}
+
+      if (result.data && result.data[0]) {
+        _data = result.data[0]
+      }
+
+      this.setState({
+        linkUrl: _data[config.wrap.linkField] || '',
+        data: _data,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    }
+  }
+
+  inputUrl = (e) => {
+    let val = e.target.value
+    let submit = /\n/g.test(val)
+
+    val = val.replace(/\n/g, '')
+
+    if (/\s/.test(val)) {
+      submit = false
+    }
+
+    if (submit) {
+      this.setState({linkUrl: val}, () => {
+        let node = document.getElementById(this.state.config.uuid)
+        node && node.select && node.select()
+      })
+    }
+  }
+
+  enterUrl = (val) => {
+    this.setState({linkUrl: val}, () => {
+      let node = document.getElementById(this.state.config.uuid)
+      node && node.select && node.select()
+    })
+  }
+
+  render() {
+    const { config, linkUrl } = this.state
+
+    return (
+      <div className="menu-iframe-box" style={config.style}>
+        {config.wrap.title || config.wrap.linkType === 'input' ? <div className="iframe-header" style={config.headerStyle}>
+          <span className="title">{config.wrap.title}</span>
+          {config.wrap.linkType === 'input' ? <Search id={config.uuid} placeholder="璇疯緭鍏ュ湴鍧�" onChange={this.inputUrl} onSearch={this.enterUrl} enterButton="纭畾"/> : null}
+        </div> : null}
+        <div className="iframe-wrap" style={{height: config.wrap.height}}>
+          {linkUrl ? <iframe title="mk" className="iframe" src={linkUrl} frameBorder="0"></iframe> : <Empty description={false}/>}
+        </div>
+      </div>
+    )
+  }
+}
+
+export default NormalIframe
\ No newline at end of file
diff --git a/src/tabviews/custom/components/iframe/index.scss b/src/tabviews/custom/components/iframe/index.scss
new file mode 100644
index 0000000..c46589b
--- /dev/null
+++ b/src/tabviews/custom/components/iframe/index.scss
@@ -0,0 +1,63 @@
+.menu-iframe-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 50px;
+
+  .iframe-header {
+    position: relative;
+    height: 45px;
+    padding-right: 8px;
+    border-bottom: 1px solid #e8e8e8;
+    overflow: hidden;
+    letter-spacing: 0px;
+    line-height: 45px;
+    box-sizing: content-box;
+  
+    .title {
+      text-decoration: inherit;
+      font-weight: inherit;
+      font-style: inherit;
+      float: left;
+      line-height: inherit;
+      margin-left: 10px;
+      position: relative;
+      z-index: 1;
+      min-height: 45px;
+      min-width: 30px;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+    }
+
+    .ant-input-search {
+      margin-top: 5px;
+      width: 40%;
+      max-width: 400px;
+      float: right;
+    }
+  }
+
+  .ant-empty {
+    position: absolute;
+    top: calc(50% - 34px);
+    left: calc(50% - 92px);
+
+    .ant-empty-image {
+      height: 60px;
+    }
+  }
+
+  .iframe-wrap {
+    position: relative;
+    .iframe {
+      width: 100%;
+      height: 100%;
+      vertical-align: top;
+      margin-top: 0px;
+    }
+  }
+}
diff --git a/src/tabviews/custom/components/interfaces/interItem/index.jsx b/src/tabviews/custom/components/interfaces/interItem/index.jsx
index 9253bd9..cffd75b 100644
--- a/src/tabviews/custom/components/interfaces/interItem/index.jsx
+++ b/src/tabviews/custom/components/interfaces/interItem/index.jsx
@@ -1,6 +1,6 @@
 import {Component} from 'react'
 import PropTypes from 'prop-types'
-import { notification } from 'antd'
+import { notification, Modal } from 'antd'
 
 import Api from '@/api'
 import UtilsDM from '@/utils/utils-datamanage.js'
@@ -64,10 +64,11 @@
     let result = await Api.genericInterface(param)
     if (result.status) {
       this.loading = false
-      let _data = { $$empty: true }
+      let _data = { $$empty: true, $$uuid: '' }
 
       if (result.data && result.data[0]) {
         _data = result.data[0]
+        _data.$$uuid = _data[config.setting.primaryKey] || ''
       }
       
       _data.$$loaded = true
@@ -75,14 +76,22 @@
       window.GLOB.CacheData.set(config.uuid, _data)
 
       MKEmitter.emit('mkPublicData', config.uuid, _data)
+      MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid, _data)
     } else {
       this.loading = false
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/share/normalTable/index.jsx b/src/tabviews/custom/components/share/normalTable/index.jsx
index 0e98c31..09611a8 100644
--- a/src/tabviews/custom/components/share/normalTable/index.jsx
+++ b/src/tabviews/custom/components/share/normalTable/index.jsx
@@ -15,8 +15,9 @@
 import './index.scss'
 
 const { Paragraph } = Typography
-const CardCellComponent = asyncComponent(() => import('@/tabviews/custom/components/card/cardcellList'))
+const Video = asyncComponent(() => import('@/components/video'))
 const MkPicture = asyncComponent(() => import('@/components/mkPicture'))
+const CardCellComponent = asyncComponent(() => import('@/tabviews/custom/components/card/cardcellList'))
 const PicRadio = {
   '4:3': '75%', '3:2': '66.67%', '16:9': '56.25%', '2:1': '50%', '3:1': '33.33%', '4:1': '25%',
   '5:1': '20%', '6:1': '16.67%', '7:1': '14.29%', '8:1': '12.5%', '9:1': '11.11%',
@@ -24,7 +25,7 @@
 }
 class BodyRow extends React.Component {
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props.data), fromJS(nextProps.data)) || !is(fromJS(this.props.className), fromJS(nextProps.className))
+    return !is(fromJS(this.props.data), fromJS(nextProps.data)) || this.props.className !== nextProps.className
   }
 
   render() {
@@ -259,6 +260,14 @@
           ))}
         </div>
       )
+    } else if (col.type === 'video') {
+      let url = record[col.field] || ''
+
+      resProps.children = (
+        <div className="video-wrap">
+          {url ? <Video card={col} value={url}/> : null}
+        </div>
+      )
     } else if (col.type === 'textarea') {
       let content = ''
       if (record[col.field] !== undefined) {
@@ -395,12 +404,12 @@
     columns: PropTypes.array,        // 琛ㄦ牸鍒�
     lineMarks: PropTypes.array,      // 琛屾爣璁�
     fields: PropTypes.array,         // 缁勪欢瀛楁闆�
-    ContainerId: PropTypes.any,      // 鏍囩椤靛灞侷d
     data: PropTypes.any,             // 琛ㄦ牸鏁版嵁
     total: PropTypes.any,            // 鎬绘暟
     loading: PropTypes.bool,         // 琛ㄦ牸鍔犺浇涓�
     refreshdata: PropTypes.func,     // 琛ㄦ牸涓帓搴忓垪銆侀〉鐮佺殑鍙樺寲鏃跺埛鏂�
     chgSelectData: PropTypes.func,   // 鏁版嵁鍒囨崲
+    autoMatic: PropTypes.any
   }
 
   state = {
@@ -434,52 +443,69 @@
       }
     }
 
-    let getColumns = (cols) => {
-      return cols.map(item => {
-        let cell = null
-  
-        if (item.type === 'colspan') {
-          cell = { title: item.label, align: item.Align }
-          cell.children = getColumns(item.subcols)
-        } else {
-          if (item.rowspan === 'true') {
-            rowspans.push(item.field)
-          }
-          if (item.type === 'index') {
-            item.field = '$Index'
-            item.type = 'text'
-          } else if (_format && !Math.floor(Math.random() * radio)) {
-            item.blur = true
-          }
-  
-          if (item.marks && item.marks.length === 0) {
-            item.marks = ''
-          }
+    let _columns = []
 
-          if (item.field) {
-            orderfields[item.uuid] = item.field
-          }
-
-          cell = {
-            align: item.Align,
-            dataIndex: item.uuid,
-            title: item.label,
-            sorter: item.field && item.IsSort === 'true',
-            width: item.Width || 120,
-            onCell: record => ({
-              record,
-              col: item,
-              config: item.type === 'custom' || item.type === 'action' ? {setting, columns: fields} : null,
-              triggerLink: this.triggerLink
-            })
-          }
-        }
+    if (setting.tableMode !== 'fast') {
+      let getColumns = (cols) => {
+        return cols.map(item => {
+          let cell = null
+    
+          if (item.type === 'colspan') {
+            cell = { title: item.label, align: item.Align }
+            cell.children = getColumns(item.subcols)
+          } else {
+            if (item.rowspan === 'true') {
+              rowspans.push(item.field)
+            }
+            if (item.type === 'index') {
+              item.field = '$Index'
+              item.type = 'text'
+            } else if (_format && !Math.floor(Math.random() * radio)) {
+              item.blur = true
+            }
+    
+            if (item.marks && item.marks.length === 0) {
+              item.marks = ''
+            }
   
-        return cell
+            if (item.field) {
+              orderfields[item.uuid] = item.field
+            }
+  
+            cell = {
+              align: item.Align,
+              dataIndex: item.uuid,
+              title: item.label,
+              sorter: item.field && item.IsSort === 'true',
+              width: item.Width || 120,
+              onCell: record => ({
+                record,
+                col: item,
+                config: item.type === 'custom' || item.type === 'action' ? {setting, columns: fields} : null,
+                triggerLink: this.triggerLink
+              })
+            }
+          }
+    
+          return cell
+        })
+      }
+      _columns = getColumns(columns)
+    } else {
+      let fields = []
+      columns.forEach(item => {
+        if (!item.field || fields.includes(item.field)) return
+        fields.push(item.field)
+
+        _columns.push({
+          align: item.Align,
+          dataIndex: item.field,
+          title: item.label,
+          sorter: item.IsSort === 'true',
+          width: item.Width || 120
+        })
       })
     }
-
-    let _columns = getColumns(columns)
 
     if (rowspans.length === 0) {
       rowspans = null
@@ -493,13 +519,6 @@
       }
       return uuid.join('')
     }) ()
-
-    // if (setting.borderColor) { // 杈规棰滆壊
-    //   let style = `#${tableId} table, #${tableId} tr, #${tableId} th, #${tableId} td {border-color: ${setting.borderColor}}`
-    //   let ele = document.createElement('style')
-    //   ele.innerHTML = style
-    //   document.getElementsByTagName('head')[0].appendChild(ele)
-    // }
 
     let size = (setting.pageSize || 10) + ''
     let pageOptions = ['10', '25', '50', '100', '500', '1000']
@@ -530,9 +549,17 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('mkCheckTopLine', this.mkCheckTopLine)
+    const { autoMatic } = this.props
+
     MKEmitter.addListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.addListener('resetTable', this.resetTable)
+
+    if (autoMatic === true) {
+      MKEmitter.addListener('autoQueryData', this.autoQueryData)
+      MKEmitter.addListener('autoSelectData', this.autoSelectData)
+    }
+
+    MKEmitter.addListener('mkCheckTopLine', this.mkCheckTopLine)
   }
 
   /**
@@ -542,9 +569,50 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('mkCheckTopLine', this.mkCheckTopLine)
     MKEmitter.removeListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.removeListener('resetTable', this.resetTable)
+    MKEmitter.removeListener('autoQueryData', this.autoQueryData)
+    MKEmitter.removeListener('autoSelectData', this.autoSelectData)
+    MKEmitter.removeListener('mkCheckTopLine', this.mkCheckTopLine)
+  }
+
+  autoSelectData = (id, index) => {
+    if (id !== this.props.MenuID) return
+
+    const { pageSize, pageIndex } = this.state
+
+    let i = index - (pageIndex - 1) * pageSize - 1
+
+    if (this.props.data[i]) {
+      this.changeRow(this.props.data[i], i)
+      MKEmitter.emit('autoTransSelectData', this.props.MenuID, this.props.data[i])
+    } else {
+      MKEmitter.emit('autoMaticOver', this.props.MenuID)
+    }
+  }
+
+  autoQueryData = (id, index) => {
+    if (id !== this.props.MenuID) return
+
+    const { total } = this.props
+    const { pageSize } = this.state
+
+    if (index !== 1 && (!total || index > total)) {
+      MKEmitter.emit('autoMaticOver', this.props.MenuID)
+      return
+    }
+
+    console.clear()
+
+    let pageIndex = Math.ceil(index / pageSize)
+
+    this.setState({
+      pageIndex: pageIndex,
+      selectedRowKeys: [],
+      activeIndex: null
+    })
+
+    this.props.refreshdata({pageIndex})
   }
 
   mkCheckTopLine = (menuId, id) => {
@@ -856,11 +924,14 @@
       }
     }
 
-    const components = {
+    let components = {
       body: {
-        row: BodyRow,
-        cell: BodyCell
+        row: BodyRow
       }
+    }
+
+    if (setting.tableMode !== 'fast') {
+      components.body.cell = BodyCell
     }
 
     // 鏁版嵁鏀惰捣鏃讹紝杩囨护宸查�夋暟鎹�
@@ -899,7 +970,6 @@
         }
         <Table
           components={components}
-          // style={setting.style}
           size={setting.size || 'middle'}
           bordered={setting.bordered !== 'false'}
           rowSelection={rowSelection}
@@ -909,7 +979,7 @@
           scroll={{ x: '100%', y: height }}
           onRow={(record, index) => {
             return {
-              lineMarks,
+              lineMarks: setting.tableMode !== 'fast' ? lineMarks : null,
               data: record,
               className: index === activeIndex ? ' mk-row-active ' : '',
               onClick: () => {this.changeRow(record, index)},
diff --git a/src/tabviews/custom/components/share/normalTable/index.scss b/src/tabviews/custom/components/share/normalTable/index.scss
index 697e60e..a339616 100644
--- a/src/tabviews/custom/components/share/normalTable/index.scss
+++ b/src/tabviews/custom/components/share/normalTable/index.scss
@@ -100,6 +100,11 @@
           position: relative;
           margin: 2px;
         }
+        .video-wrap {
+          .video-react-playback-rate {
+            display: none;
+          }
+        }
         .action-col {
           .ant-btn > .anticon + span {
             margin-left: 3px;
diff --git a/src/tabviews/custom/components/share/normalheader/index.scss b/src/tabviews/custom/components/share/normalheader/index.scss
index e961d6e..8816fb1 100644
--- a/src/tabviews/custom/components/share/normalheader/index.scss
+++ b/src/tabviews/custom/components/share/normalheader/index.scss
@@ -6,6 +6,7 @@
   overflow: hidden;
   letter-spacing: 0px;
   line-height: 45px;
+  box-sizing: content-box;
 
   .title {
     text-decoration: inherit;
diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx
index cf08b7d..2df9942 100644
--- a/src/tabviews/custom/components/share/tabtransfer/index.jsx
+++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -36,6 +36,7 @@
 const Balcony = asyncComponent(() => import('@/tabviews/custom/components/card/balcony'))
 const TimeLine = asyncComponent(() => import('@/tabviews/custom/components/timeline/normal-timeline'))
 const CustomChart = asyncComponent(() => import('@/tabviews/custom/components/chart/custom-chart'))
+const MkBaseTable = asyncComponent(() => import('@/tabviews/custom/components/table/base-table'))
 
 class TabTransfer extends Component {
   static propTpyes = {
@@ -167,27 +168,51 @@
     if (!config || !config.components || config.components.length === 0) return (<Empty description={false} />)
 
     return config.components.map(item => {
-      if (item.type === 'bar' || item.type === 'line') {
+      let style = null
+
+      if (item.style && item.style.clear === 'left') {
+        style = {clear: 'left'}
+      }
+
+      if (item.type === 'card' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DataCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'card' && item.subtype === 'propcard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <PropCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalTable config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'bar' || item.type === 'line') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvBarAndLine data={data} config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'pie') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvPie data={data} config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'dashboard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'scatter') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
@@ -198,115 +223,103 @@
           BID = BData.$BID || ''
         }
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <MainSearch config={item} BID={BID} refreshdata={this.resetSearch} />
           </Col>
         )
       } else if (item.type === 'tabs') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvTabs config={item} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'card' && item.subtype === 'datacard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <DataCard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'card' && item.subtype === 'propcard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <PropCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'carousel' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CarouselDataCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'carousel' && item.subtype === 'propcard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CarouselPropCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
-      } else if (item.type === 'table' && item.subtype === 'tablecard') {
+      } else if (item.type === 'card' && item.subtype === 'tablecard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TableCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
-      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+      } else if (item.type === 'table' && item.subtype === 'basetable') {
         return (
-          <Col span={item.width} key={item.uuid}>
-            <NormalTable config={item} data={data} mainSearch={mainSearch}/>
+          <Col span={item.width} style={style} key={item.uuid}>
+            <MkBaseTable config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'table' && item.subtype === 'editable') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <EditTable config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'group' && item.subtype === 'normalgroup') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalGroup config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'form' && item.subtype === 'simpleform') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'form' && item.subtype === 'stepform') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <StepForm config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'form' && item.subtype === 'tabform') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TabForm config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'tree') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalTree config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'editor') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <BraftEditor config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'code') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <SandBox config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'balcony') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <Balcony config={item} data={data}/>
           </Col>
         )
       } else if (item.type === 'timeline') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TimeLine config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'chart') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CustomChart config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
diff --git a/src/tabviews/custom/components/table/base-table/index.jsx b/src/tabviews/custom/components/table/base-table/index.jsx
new file mode 100644
index 0000000..c4753cc
--- /dev/null
+++ b/src/tabviews/custom/components/table/base-table/index.jsx
@@ -0,0 +1,616 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { notification, Modal } from 'antd'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import asyncComponent from '@/utils/asyncComponent'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+// 閫氱敤缁勪欢
+const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
+const MainAction = asyncComponent(() => import('@/tabviews/zshare/actionList'))
+const MainTable = asyncComponent(() => import('@/tabviews/custom/components/share/normalTable'))
+
+class MkBaseTable extends Component {
+  static propTpyes = {
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+  }
+
+  state = {
+    BID: '',              // 涓婄骇ID
+    BData: '',            // 涓婄骇缁勪欢琛屾暟鎹�
+    config: {},           // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷寜閽�佹悳绱€�佹樉绀哄垪銆佹爣绛剧瓑
+    actions: null,        // 鎸夐挳闆�
+    columns: null,        // 鏄剧ず鍒�
+    arr_field: '',        // 鏌ヨ瀛楁闆�
+    setting: null,        // 椤甸潰鍏ㄥ眬璁剧疆锛氭暟鎹簮銆佹寜閽強鏄剧ず鍒楀浐瀹氥�佷富閿瓑
+    data: [],             // 鍒楄〃鏁版嵁闆�
+    selectedData: [],     // 宸查�夎〃鏍兼暟鎹�
+    total: 0,             // 鎬绘暟
+    loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
+    pageIndex: 1,         // 椤电爜
+    pageSize: 10,         // 姣忛〉鏁版嵁鏉℃暟
+    orderBy: '',          // 鎺掑簭
+    search: '',           // 鎼滅储鏉′欢鏁扮粍锛屼娇鐢ㄦ椂闇�鍒嗗満鏅鐞�
+    statFValue: []        // 鍚堣鍊�
+  }
+
+  /**
+   * @description 鍒濆鍖栧鐞�
+   */
+  UNSAFE_componentWillMount () {
+    let _config = fromJS(this.props.config).toJS()
+    let _cols = new Map()
+
+    let BID = ''
+    let BData = ''
+
+    if (_config.setting.supModule) {
+      BData = window.GLOB.CacheData.get(_config.setting.supModule)
+    } else {
+      BData = window.GLOB.CacheData.get(_config.$pageId)
+    }
+    if (BData) {
+      BID = BData.$BID || ''
+    }
+
+    if (_config.wrap.controlField) {
+      if (_config.wrap.controlVal) {
+        _config.wrap.controlVal = _config.wrap.controlVal.split(',')
+      } else {
+        _config.wrap.controlVal = ['']
+      }
+    }
+
+    let setting = {..._config.setting, ..._config.wrap}
+
+    if (setting.selected !== 'always' && setting.selected !== 'init') {
+      setting.selected = 'false'
+    } else {
+      setting.orisel = true
+    }
+
+    _config.columns.forEach(item => {
+      if (item.type !== 'number') return
+      _cols.set(item.field, item)
+    })
+
+    _config.cols.forEach(column => {
+      if (column.type === 'action') {
+        column.operations = column.elements
+      }
+    })
+
+    _config.style = _config.style || {}
+
+    this.setState({
+      pageSize: setting.pageSize || 10,
+      BID: BID || '',
+      BData: BData || '',
+      config: _config,
+      setting: setting,
+      actions: _config.action,
+      columns: _config.cols,
+      arr_field: _config.columns.map(col => col.field).join(','),
+      search: Utils.initMainSearch(_config.search) // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
+    }, () => {
+      if (_config.setting.onload === 'true') {
+        setTimeout(() => {
+          this.loadmaindata()
+          this.getStatFieldsValue()
+        }, _config.setting.delay || 0)
+      }
+    })
+  }
+
+  /**
+   * @description 涓昏〃鏁版嵁鍔犺浇
+   * @param { Boolean } reset  琛ㄦ牸鏄惁閲嶇疆
+   * @param { String }  repage 琛ㄦ牸鏄惁閲嶇疆椤电爜
+   */
+  async loadmaindata (reset, repage, id) {
+    const { mainSearch } = this.props
+    const { setting, config, arr_field, search, orderBy, BID, pageIndex, pageSize, BData } = this.state
+
+    if (setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: [],
+        selectedData: [],
+        total: 0
+      })
+      
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '') // 骞挎挱鏁版嵁鍒囨崲
+      reset && MKEmitter.emit('resetTable', config.uuid, repage) // 鍒楄〃閲嶇疆
+      if (setting.$hasSyncModule) {
+        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+      }
+      return
+    }
+
+    let searches = fromJS(search).toJS()
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    let requireFields = searches.filter(item => item.required && item.value === '')
+    if (requireFields.length > 0) {
+      return
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = orderBy || setting.order
+    let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      if ((setting.selected !== 'false' || (setting.orisel && id)) && result.data && result.data.length > 0) {
+        setTimeout(() => {
+          MKEmitter.emit('mkCheckTopLine', config.uuid, id)
+        }, 200)
+        if (setting.selected === 'init') {
+          this.setState({setting: {...setting, selected: 'false'}})
+        }
+      } else {
+        MKEmitter.emit('resetSelectLine', config.uuid, '', '') // 骞挎挱鏁版嵁鍒囨崲
+        if (setting.$hasSyncModule) {
+          MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+        }
+      }
+      
+      reset && MKEmitter.emit('resetTable', config.uuid, repage) // 鍒楄〃閲嶇疆
+
+      let start = 1
+      if (setting.laypage) {
+        start = pageSize * (pageIndex - 1) + 1
+      }
+
+      this.setState({
+        data: result.data.map((item, index) => {
+          item.key = index
+          item.$$uuid = item[setting.primaryKey] || ''
+          item.$$key = '' + item.key + item.$$uuid
+          item.$$BID = BID || ''
+          item.$$BData = BData || ''
+          item.$Index = start + index + ''
+
+          if (config.absFields) {
+            config.absFields.forEach(f => {
+              if (!isNaN(item[f])) {
+                item[f] = Math.abs(item[f])
+              }
+            })
+          }
+          if (setting.controlField) {
+            if (setting.controlVal.includes(item[setting.controlField])) {
+              item.$disabled = true
+            }
+          }
+          
+          return item
+        }),
+        selectedData: [],
+        total: result.total,
+        loading: false
+      })
+
+      if (config.autoMatic) {
+        if (result.data && result.data.length > 0) {
+          MKEmitter.emit('autoGetData', config.MenuID)
+        } else {
+          MKEmitter.emit('autoMaticOver', config.MenuID)
+        }
+      }
+    } else {
+      this.setState({
+        loading: false
+      })
+
+      if (config.autoMatic) {
+        MKEmitter.emit('autoMaticError', config.MenuID)
+      }
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    }
+  }
+
+  /**
+   * @description 鑾峰彇鍗曡鏁版嵁
+   */ 
+  async loadmainLinedata (id) {
+    const { mainSearch } = this.props
+    const { setting, config, arr_field, search, orderBy, BID, pageIndex, pageSize, BData } = this.state
+
+    let searches = fromJS(search).toJS()
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = orderBy || setting.order
+    let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, id)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let data = fromJS(this.state.data).toJS()
+      let selectedData = fromJS(this.state.selectedData).toJS()
+      if (result.data && result.data[0]) {
+        let _data = result.data[0] || {}
+        _data.$$uuid = _data[setting.primaryKey] || ''
+        _data.$$BID = BID || ''
+        _data.$$BData = BData || ''
+
+        if (config.absFields) {
+          config.absFields.forEach(f => {
+            if (!isNaN(_data[f])) {
+              _data[f] = Math.abs(_data[f])
+            }
+          })
+        }
+
+        try {
+          data = data.map(item => {
+            if (item.$$uuid === _data.$$uuid) {
+              _data.key = item.key
+              _data.$$key = '' + item.key + item.$$uuid
+              _data.$Index = item.$Index
+              return _data
+            } else {
+              return item
+            }
+          })
+          selectedData = selectedData.map(item => {
+            if (_data.$$uuid === item.$$uuid) {
+              return _data
+            }
+            return item
+          })
+        } catch (e) {
+          console.warn('鏁版嵁鏌ヨ閿欒')
+        }
+
+        MKEmitter.emit('resetSelectLine', config.uuid, _data.$$uuid || '', _data)
+      }
+
+      this.setState({
+        data,
+        selectedData,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  /**
+   * @description 鑾峰彇鍚堣瀛楁鍊�
+   */
+  getStatFieldsValue = () => {
+    const { mainSearch } = this.props
+    const { setting, config, search, BID, orderBy } = this.state
+
+    if (setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        statFValue: []
+      })
+      return
+    }
+
+    if (config.statFields.length === 0 || setting.interType !== 'system' || !setting.dataresource) return
+
+    let searches = fromJS(search).toJS()
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+    let requireFields = searches.filter(item => item.required && item.value === '')
+    if (requireFields.length > 0) {
+      return
+    }
+
+    let _orderBy = orderBy || setting.order
+    let param = UtilsDM.getStatQueryDataParams(setting, config.statFields, searches, _orderBy, BID)
+
+    Api.genericInterface(param).then(res => {
+      if (res.status) {
+        let _data = res.data[0]
+        let values = []
+
+        if (_data) {
+          config.statFields.forEach(item => {
+            if (_data[item.field] || _data[item.field] === 0) {
+              let val = +_data[item.field]
+              if (isNaN(val)) {
+                val = 0
+              }
+              val = val.toFixed(item.decimal)
+              values.push({label: item.label, value: val})
+            }
+          })
+        }
+        this.setState({
+          statFValue: values
+        })
+      } else {
+        this.setState({
+          statFValue: []
+        })
+        notification.error({
+          top: 92,
+          message: res.message,
+          duration: 10
+        })
+      }
+    })
+  }
+
+  /**
+   * @description 鎼滅储鏉′欢鏀瑰彉鏃讹紝閲嶇疆琛ㄦ牸鏁版嵁
+   * 鍚湁鍒濆涓嶅姞杞界殑椤甸潰锛屼慨鏀硅缃�
+   */
+  refreshbysearch = (searches) => {
+    const { setting } = this.state
+
+    if (setting.onload === 'false') {
+      this.setState({
+        pageIndex: 1,
+        search: searches,
+        setting: {...setting, onload: 'true'}
+      }, () => {
+        this.loadmaindata()
+        this.getStatFieldsValue()
+      })
+    } else {
+      this.setState({
+        pageIndex: 1,
+        search: searches
+      }, () => {
+        this.loadmaindata(true, 'true')
+        this.getStatFieldsValue()
+      })
+    }
+  }
+
+  /**
+   * @description 琛ㄦ牸鏉′欢鏀瑰彉鏃堕噸缃暟鎹紙鍒嗛〉鎴栨帓搴忥級
+   */
+  refreshbytable = (pagination, filters, sorter) => {
+    if (!sorter) { // 鏃犱汉鍊煎畧
+      this.setState({
+        pageIndex: pagination.pageIndex
+      }, () => {
+        this.loadmaindata()
+      })
+    } else {
+      if (sorter.order) {
+        let _chg = {
+          ascend: 'asc',
+          descend: 'desc'
+        }
+        sorter.order = _chg[sorter.order]
+      }
+  
+      this.setState({
+        pageIndex: pagination.current,
+        pageSize: pagination.pageSize,
+        orderBy: (sorter.field && sorter.order) ? `${sorter.field} ${sorter.order}` : ''
+      }, () => {
+        this.loadmaindata()
+      })
+    }
+  }
+
+  /**
+   * @description 琛ㄦ牸鍒锋柊
+   */
+  reloadtable = (btn, id) => {
+    if (!btn || btn.resetPageIndex !== 'false') {
+      this.setState({
+        pageIndex: 1
+      }, () => {
+        this.loadmaindata(true, 'true', id)
+        this.getStatFieldsValue()
+      })
+    } else {
+      this.loadmaindata(true, 'false', id)
+      this.getStatFieldsValue()
+    }
+  }
+
+  /**
+   * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
+   */
+  queryModuleParam = (menuId, callback) => {
+    const { mainSearch } = this.props
+    const { arr_field, config, orderBy, search, setting} = this.state
+
+    if (config.uuid !== menuId) return
+
+    let searches = search ? fromJS(search).toJS() : []
+    if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key.toLowerCase())
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key.toLowerCase())) {
+          searches.push(item)
+        }
+      })
+    }
+
+    callback({
+      arr_field: arr_field,
+      orderBy: orderBy || setting.order,
+      search: searches,
+      menuName: config.name
+    })
+  }
+
+  reloadData = (menuId, id) => {
+    const { config } = this.state
+
+    if (config.uuid !== menuId) return
+
+    if (!id) {
+      this.reloadtable()
+    } else {
+      this.loadmainLinedata(id)
+    }
+  }
+
+  resetParentParam = (MenuID, id, data) => {
+    const { setting } = this.state
+
+    if (!setting.supModule || setting.supModule !== MenuID) return
+    if (id !== this.state.BID || id !== '') {
+      this.setState({
+        pageIndex: 1,
+        BID: id,
+        BData: data
+      }, () => {
+        this.loadmaindata(true, 'true')
+        this.getStatFieldsValue()
+      })
+    }
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn, id, lines) => {
+    const { config, BID } = this.state
+
+    if (config.uuid !== menuId) return
+
+    if (position === 'line') {
+      if (lines && lines.length === 1) {
+        this.loadmainLinedata(lines[0].$$uuid)
+      } else {
+        this.reloadtable(btn, id)
+      }
+    } else if ((position === 'mainline' || position === 'popclose') && config.setting.supModule && BID) { // 鍒锋柊婧愮粍浠舵椂锛岄檮甯﹀埛鏂颁笂绾ц涓庡綋鍓嶇粍浠�
+      MKEmitter.emit('reloadData', config.setting.supModule, BID)
+    } else {
+      this.reloadtable(btn, id)
+    }
+
+    if (position === 'popclose') { // 鎵ц鍚姩寮圭獥鐨勬寜閽墍閫夋嫨鐨勫埛鏂伴」
+      btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
+    }
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    const { config } = this.state
+
+    if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      this.setState({pageIndex: 1}, () => {
+        this.reloadtable()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  render() {
+    const { BID, setting, actions, config, columns, selectedData, BData, data } = this.state
+    
+    return (
+      <div className="custom-base-table" style={config.style}>
+        {config.search.length ?
+          <MainSearch BID={BID} config={config} refreshdata={this.refreshbysearch}/> : null
+        }
+        {actions.length > 0 ? <MainAction
+          BID={BID}
+          setting={setting}
+          actions={actions}
+          BData={BData}
+          columns={config.columns}
+          selectedData={selectedData}
+        /> : <div style={{height: '25px'}}></div>}
+        <div className="main-table-box">
+          <MainTable
+            data={data}
+            setting={setting}
+            columns={columns}
+            MenuID={config.uuid}
+            fields={config.columns}
+            total={this.state.total}
+            autoMatic={config.autoMatic}
+            lineMarks={config.lineMarks}
+            loading={this.state.loading}
+            refreshdata={this.refreshbytable}
+            statFValue={this.state.statFValue}
+            chgSelectData={(selects) => this.setState({selectedData: selects})}
+          />
+        </div>
+      </div>
+    )
+  }
+}
+
+export default MkBaseTable
\ No newline at end of file
diff --git a/src/tabviews/custom/components/table/base-table/index.scss b/src/tabviews/custom/components/table/base-table/index.scss
new file mode 100644
index 0000000..dd90bb5
--- /dev/null
+++ b/src/tabviews/custom/components/table/base-table/index.scss
@@ -0,0 +1,47 @@
+.custom-base-table {
+  position: relative;
+  background-color: #fff;
+
+  .normal-header {
+    margin-bottom: 10px;
+  }
+  .top-search {
+    border-bottom: 1px solid #efefef;
+  }
+  >.button-list.toolbar-button {
+    min-height: 60px;
+    padding-right: 60px;
+    button {
+      margin-right: 0px;
+      margin-bottom: 0px;
+      min-height: 28px;
+      height: auto;
+    }
+  }
+  .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);
+    }
+  }
+  .main-table-box {
+    position: relative;
+    min-height: 150px;
+    .main-pickup {
+      position: absolute;
+      right: 5px;
+      top: -22px;
+      z-index: 2;
+    }
+    >.async-spin {
+      line-height: 150px!important;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/table/edit-table/index.jsx b/src/tabviews/custom/components/table/edit-table/index.jsx
index 631fd32..03baac5 100644
--- a/src/tabviews/custom/components/table/edit-table/index.jsx
+++ b/src/tabviews/custom/components/table/edit-table/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification } from 'antd'
+import { notification, Modal } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -228,6 +228,15 @@
         item.$Index = start + index + ''
         item.$type = 'upt'
         item.$origin = true
+
+        if (config.absFields) {
+          config.absFields.forEach(f => {
+            if (!isNaN(item[f])) {
+              item[f] = Math.abs(item[f])
+            }
+          })
+        }
+
         return item
       })
 
@@ -243,11 +252,18 @@
       this.setState({
         loading: false
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -286,6 +302,14 @@
       _data.$$BData = BData || ''
       _data.$type = 'upt'
       _data.$origin = true
+
+      if (config.absFields) {
+        config.absFields.forEach(f => {
+          if (!isNaN(_data[f])) {
+            _data[f] = Math.abs(_data[f])
+          }
+        })
+      }
 
       try {
         data = data.map(item => {
@@ -550,7 +574,7 @@
   UNSAFE_componentWillReceiveProps(nextProps) {
     const { config } = this.state
 
-    if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({pageIndex: 1}, () => {
         this.reloadtable()
       })
diff --git a/src/tabviews/custom/components/table/normal-table/index.jsx b/src/tabviews/custom/components/table/normal-table/index.jsx
index 8f7ea2a..d95478b 100644
--- a/src/tabviews/custom/components/table/normal-table/index.jsx
+++ b/src/tabviews/custom/components/table/normal-table/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Collapse } from 'antd'
+import { notification, Collapse, Modal } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -100,6 +100,14 @@
         item.$$BID = BID || ''
         item.$$BData = BData || ''
         item.$Index = index + 1 + ''
+
+        if (_config.absFields) {
+          _config.absFields.forEach(f => {
+            if (!isNaN(item[f])) {
+              item[f] = Math.abs(item[f])
+            }
+          })
+        }
 
         if (setting.controlField) {
           if (setting.controlVal.includes(item[setting.controlField])) {
@@ -256,6 +264,14 @@
           item.$$BData = BData || ''
           item.$Index = start + index + ''
 
+          if (config.absFields) {
+            config.absFields.forEach(f => {
+              if (!isNaN(item[f])) {
+                item[f] = Math.abs(item[f])
+              }
+            })
+          }
+
           if (setting.controlField) {
             if (setting.controlVal.includes(item[setting.controlField])) {
               item.$disabled = true
@@ -273,11 +289,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -314,6 +337,15 @@
         _data.$$uuid = _data[setting.primaryKey] || ''
         _data.$$BID = BID || ''
         _data.$$BData = BData || ''
+
+        if (config.absFields) {
+          config.absFields.forEach(f => {
+            if (!isNaN(_data[f])) {
+              _data[f] = Math.abs(_data[f])
+            }
+          })
+        }
+
         try {
           data = data.map(item => {
             if (item.$$uuid === _data.$$uuid) {
@@ -529,6 +561,7 @@
     const { setting } = this.state
 
     if (!setting.supModule || setting.supModule !== MenuID) return
+
     if (id !== this.state.BID || id !== '') {
       this.setState({
         pageIndex: 1,
@@ -584,6 +617,14 @@
           item.$$BData = BData || ''
           item.$Index = index + 1 + ''
 
+          if (config.absFields) {
+            config.absFields.forEach(f => {
+              if (!isNaN(item[f])) {
+                item[f] = Math.abs(item[f])
+              }
+            })
+          }
+
           if (setting.controlField) {
             if (setting.controlVal.includes(item[setting.controlField])) {
               item.$disabled = true
@@ -604,7 +645,7 @@
       }
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({pageIndex: 1}, () => {
         this.reloadtable()
       })
@@ -666,15 +707,15 @@
             {config.search && config.search.length ?
               <MainSearch BID={BID} config={config} refreshdata={this.refreshbysearch}/> : null
             }
-            <MainAction
+            {actions.length > 0 ? <MainAction
               BID={BID}
               setting={setting}
               actions={actions}
               BData={BData}
               columns={config.columns}
               selectedData={selectedData}
-            />
-            <div className={'main-table-box ' + (!actions || actions.length === 0 ? 'no-action' : '')}>
+            /> : <div style={{height: '25px'}}></div>}
+            <div className="main-table-box">
               <MainTable
                 setting={setting}
                 columns={columns}
@@ -695,14 +736,14 @@
           {config.search && config.search.length ?
             <MainSearch BID={BID} config={config} refreshdata={this.refreshbysearch}/> : null
           }
-          <MainAction
+          {actions.length > 0 ? <MainAction
             BID={BID}
             setting={setting}
             actions={actions}
             BData={BData}
             columns={config.columns}
             selectedData={selectedData}
-          />
+          /> : <div style={{height: '15px'}}></div>}
           <div className={'main-table-box ' + (!actions || actions.length === 0 ? 'no-action' : '')}>
             <MainTable
               setting={setting}
diff --git a/src/tabviews/custom/components/table/normal-table/index.scss b/src/tabviews/custom/components/table/normal-table/index.scss
index 3e10f6a..c628e76 100644
--- a/src/tabviews/custom/components/table/normal-table/index.scss
+++ b/src/tabviews/custom/components/table/normal-table/index.scss
@@ -9,8 +9,7 @@
     border-bottom: 1px solid #efefef;
   }
   >.button-list.toolbar-button {
-    padding: 0;
-    line-height: 45px;
+    min-height: 60px;
     padding-right: 60px;
     button {
       margin-right: 0px;
@@ -43,15 +42,6 @@
     }
     >.async-spin {
       line-height: 150px!important;
-    }
-  }
-  .no-action.main-table-box {
-    .main-pickup {
-      position: relative;
-      right: 0px;
-      top: 0px;
-      z-index: 2;
-      float: right;
     }
   }
   .ant-collapse {
diff --git a/src/tabviews/custom/components/tabs/antv-tabs/index.jsx b/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
index 483549b..9b46381 100644
--- a/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
+++ b/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
@@ -144,7 +144,7 @@
     if (!tabs.subtabs.length) return null
 
     return (
-      <div className={'menu-antv-tabs-wrap ' + tabs.setting.tabLabel} style={tabs.style}>
+      <div className={'menu-antv-tabs-wrap ' + (tabs.setting.tabLabel || '')} style={tabs.style}>
         <Tabs defaultActiveKey="1" tabBarStyle={{background: tabs.setting.backgroundColor || 'transparent'}} tabPosition={tabs.setting.position} type={tabs.setting.tabStyle}>
           {tabs.subtabs.map(tab => (
             <TabPane tab={<span id={'tab' + tab.uuid}>{tab.icon ? <MkIcon type={tab.icon} /> : null}{tab.label}</span>} style={{backgroundColor: tab.backgroundColor || 'transparent'}} key={tab.uuid}>
diff --git a/src/tabviews/custom/components/timeline/normal-timeline/index.jsx b/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
index 99f4cda..b189d5d 100644
--- a/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
+++ b/src/tabviews/custom/components/timeline/normal-timeline/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification, Timeline, Empty } from 'antd'
+import { Spin, notification, Timeline, Empty, Modal } from 'antd'
 
 import Api from '@/api'
 import MkIcon from '@/components/mk-icon'
@@ -163,7 +163,7 @@
       })
 
       this.setState({sync: false, data: _data})
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -296,11 +296,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.jsx b/src/tabviews/custom/components/tree/antd-tree/index.jsx
index 41794b6..a5ec5fe 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.jsx
+++ b/src/tabviews/custom/components/tree/antd-tree/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, Empty, notification, Input, Tree } from 'antd'
+import { Spin, Empty, notification, Input, Tree, Modal } from 'antd'
 import { FolderOpenOutlined, FolderOutlined, FileOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
@@ -95,7 +95,7 @@
       this.setState({sync: false, data: _data}, () => {
         this.handleData()
       })
-    } else if (config.setting.syncRefresh && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+    } else if (config.setting.useMSearch && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
       this.setState({}, () => {
         this.loadData()
       })
@@ -197,11 +197,18 @@
         loading: false
       })
       this.timer && this.timer.stop()
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index fe59442..1f29849 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -36,11 +36,12 @@
 const NormalTree = asyncComponent(() => import('./components/tree/antd-tree'))
 const Balcony = asyncComponent(() => import('./components/card/balcony'))
 const SettingComponent = asyncComponent(() => import('@/tabviews/zshare/settingcomponent'))
-const PagemsgComponent = asyncComponent(() => import('@/tabviews/zshare/pageMessage'))
 const CustomChart = asyncComponent(() => import('./components/chart/custom-chart'))
 const TimeLine = asyncComponent(() => import('./components/timeline/normal-timeline'))
 const Voucher = asyncComponent(() => import('./components/module/voucher'))
+const Iframe = asyncComponent(() => import('./components/iframe'))
 const DebugTable = asyncComponent(() => import('@/tabviews/debugtable'))
+const TableNodes = asyncComponent(() => import('@/tabviews/zshare/tablenodes'))
 const MkInterfaces = asyncComponent(() => import('@/tabviews/custom/components/interfaces'))
 
 class CustomPage extends Component {
@@ -49,7 +50,8 @@
     Tab: PropTypes.string,       // 寮圭獥鏍囩
     MenuID: PropTypes.string,    // 鑿滃崟Id
     MenuNo: PropTypes.string,    // 鑿滃崟鍙傛暟
-    MenuName: PropTypes.string   // 鑿滃崟鍚嶇О
+    MenuName: PropTypes.string,  // 鑿滃崟鍚嶇О
+    changeTemp: PropTypes.func
   }
 
   state = {
@@ -93,21 +95,6 @@
         config = ''
       }
 
-      // HS涓嶄娇鐢ㄨ嚜瀹氫箟璁剧疆
-      if (result.LongParamUser && !window.GLOB.mkHS) {
-        try { // 閰嶇疆淇℃伅瑙f瀽
-          let userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
-          if (userConfig) {
-            shortcuts = userConfig.action
-            userConfig.printers.forEach(item => {
-              window.GLOB.UserCacheMap.set(item.parentId + item.uuid, item)
-            })
-          }
-        } catch (e) {
-          console.warn('Parse Failure')
-        }
-      }
-
       // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
       if (!config) {
         this.setState({
@@ -125,6 +112,36 @@
           lostmsg: this.state.dict['main.view.unenabled']
         })
         return
+      }
+
+      // 妯℃澘閿欒
+      if (config.Template !== 'CustomPage') {
+        if (config.Template === 'BaseTable' || config.Template === 'CommonTable') {
+          this.props.changeTemp(MenuID, config.Template)
+        } else {
+          this.setState({
+            viewlost: true,
+            loadingview: false,
+            lostmsg: '鑿滃崟淇℃伅閿欒锛屽彲鑳藉師鍥狅細1銆佸綋鍓嶇敤鎴锋棤鏉冮檺锛�2銆佹墦寮�姝よ彍鍗曠殑鎸夐挳闇�瑕佹洿鏂般��'
+          })
+        }
+        
+        return
+      }
+
+      // HS涓嶄娇鐢ㄨ嚜瀹氫箟璁剧疆
+      if (result.LongParamUser && !window.GLOB.mkHS) {
+        try { // 閰嶇疆淇℃伅瑙f瀽
+          let userConfig = JSON.parse(window.decodeURIComponent(window.atob(result.LongParamUser)))
+          if (userConfig) {
+            shortcuts = userConfig.action
+            userConfig.printers.forEach(item => {
+              window.GLOB.UserCacheMap.set(item.parentId + item.uuid, item)
+            })
+          }
+        } catch (e) {
+          console.warn('Parse Failure')
+        }
       }
 
       // 鏁版嵁缂撳瓨璁剧疆
@@ -172,9 +189,15 @@
         })
       }
 
+      let popview = 'CustomPage'
+
+      if (config.version === 2.0) {
+        popview = 'popview'
+      }
+
       config.interfaces = this.formatInterSetting(config.interfaces, regs)
 
-      config.components = this.filterComponent(config.components, roleId, window.GLOB.mkActions, balMap, skip, param, MenuID, config.interfaces)
+      config.components = this.filterComponent(config.components, roleId, window.GLOB.mkActions, balMap, skip, param, MenuID, config.interfaces, popview)
       
       // 鑾峰彇涓绘悳绱㈡潯浠�
       let mainSearch = []
@@ -282,7 +305,7 @@
     }
   }
 
-  filterComponent = (components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces) => {
+  filterComponent = (components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces, popview) => {
     return components.filter(item => {
       item.$pageId = pageId
       
@@ -368,7 +391,7 @@
         item.subtabs = item.subtabs.map(tab => {
           tab.$pageId = pageId
 
-          tab.components = this.filterComponent(tab.components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces)
+          tab.components = this.filterComponent(tab.components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces, popview)
           return tab
         })
 
@@ -381,7 +404,7 @@
           return false
         }
 
-        item.components = this.filterComponent(item.components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces)
+        item.components = this.filterComponent(item.components, roleId, permAction, balMap, skip, urlparam, pageId, interfaces, popview)
 
         return true
       } else if (['pie', 'bar', 'line', 'dashboard', 'scatter', 'chart'].includes(item.type)) {
@@ -409,24 +432,25 @@
         }
       }
 
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
       // 鎼滅储鏉′欢鍒濆鍖�
       if (item.search && item.search.length > 0) {
         item.search = Utils.initSearchVal(item.search)
       }
 
-      if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
+      if (item.type === 'table') {
         let statFields = []
         let getCols = (cols) => {
           return cols.filter(col => {
-            if (item.subtype !== 'editable') {
-              if (col.blacklist && col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-                return false
-              } else if (col.Hide === 'true') {
-                return false
-              }
-            } else if (col.blacklist && col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-              col.Hide = 'true'
+            if (col.blacklist && col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+              return false
+            } else if (col.Hide === 'true') {
+              return false
             }
+            
             if (col.type === 'number') {
               if (col.sum === 'true' && !statFields.includes(col.field)) {
                 statFields.push(col)
@@ -488,18 +512,16 @@
       }
 
       // 鏉冮檺杩囨护
-      let tabId = this.props.Tab ? this.props.Tab.uuid : '' // 寮圭獥鏍囩鎸夐挳Id
       if (item.action && item.action.length > 0) {
         item.action = item.action.filter(cell => {
-          if (item.hidden === 'true') return false
+          if (cell.hidden === 'true') return false
 
           cell.logLabel = item.$menuname + '-' + cell.label
           cell.ContainerId = this.state.ContainerId
           cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
           cell.$menuId = item.uuid
           cell.$MenuID = this.props.MenuID
-          cell.$tabId = tabId
-          cell.$view = 'CustomPage'
+          cell.$view = popview
           cell.$toolbtn = true
 
           if (!mutil && cell.syncComponentId === item.setting.supModule) {
@@ -519,7 +541,7 @@
         })
       }
 
-      if (item.type === 'card') {
+      if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
         item.subcards && item.subcards.forEach(card => {
           if (card.style.boxShadow) {
             delete card.style.hShadow
@@ -538,8 +560,7 @@
               cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
               cell.$menuId = item.uuid
               cell.$MenuID = this.props.MenuID
-              cell.$tabId = tabId
-              cell.$view = 'CustomPage'
+              cell.$view = popview
 
               if (!mutil && cell.syncComponentId === item.setting.supModule) {
                 cell.syncComponentId = ''
@@ -566,6 +587,9 @@
 
             return cell.eleType !== 'button' || skip || permAction[cell.uuid]
           })
+
+          if (!card.backElements || card.backElements.length === 0) return
+
           card.backElements = card.backElements.filter(cell => {
             if (cell.eleType === 'button') {
               if (cell.hidden === 'true') return false
@@ -576,8 +600,7 @@
               cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
               cell.$menuId = item.uuid
               cell.$MenuID = this.props.MenuID
-              cell.$tabId = tabId
-              cell.$view = 'CustomPage'
+              cell.$view = popview
 
               if (!mutil && cell.syncComponentId === item.setting.supModule) {
                 cell.syncComponentId = ''
@@ -622,8 +645,7 @@
             cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
             cell.$menuId = item.uuid
             cell.$MenuID = this.props.MenuID
-            cell.$tabId = tabId
-            cell.$view = 'CustomPage'
+            cell.$view = popview
 
             if (cell.syncComponentId === item.wrap.supModule) {
               cell.syncComponentId = ''
@@ -646,47 +668,7 @@
 
           return cell.eleType !== 'button' || skip || permAction[cell.uuid]
         })
-      } else if ((item.type === 'table' && item.subtype === 'tablecard') || item.type === 'carousel' || item.type === 'timeline') {
-        item.subcards && item.subcards.forEach(card => {
-          card.elements = card.elements.filter(cell => {
-            if (cell.eleType === 'button') {
-              if (cell.hidden === 'true') return false
-
-              cell.logLabel = item.$menuname + '-' + cell.label
-              cell.Ot = cell.Ot || 'requiredSgl'
-              cell.ContainerId = this.state.ContainerId
-              cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
-              cell.$menuId = item.uuid
-              cell.$MenuID = this.props.MenuID
-              cell.$tabId = tabId
-              cell.$view = 'CustomPage'
-
-              if (cell.syncComponentId === item.setting.supModule) {
-                cell.syncComponentId = ''
-              }
-
-              if (cell.OpenType === 'funcbutton' && cell.funcType === 'print' && cell.verify) { // 鎵撳嵃鏈鸿缃�
-                cell = this.getPrinter(cell, item.uuid)
-              }
-              if (card.btnstyle) { // 鍏煎
-                card.style = card.style || {}
-                card.style = {...card.style, ...card.btnstyle}
-              }
-            } else if (['text', 'number', 'formula'].includes(cell.eleType)) {
-              if (!cell.height) {
-                cell.innerHeight = 'auto'
-              }
-              if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
-                cell.round = Math.pow(10, cell.decimal)
-                if (cell.format === 'percent') {
-                  cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
-                }
-              }
-            }
-            return cell.eleType !== 'button' || skip || permAction[cell.uuid]
-          })
-        })
-      } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
+      } else if (item.type === 'table') {
         item.cols = item.cols.filter(col => {
           if (col.type !== 'action') return true
           col.elements = col.elements.filter(cell => {
@@ -698,8 +680,7 @@
             cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
             cell.$menuId = item.uuid
             cell.$MenuID = this.props.MenuID
-            cell.$tabId = tabId
-            cell.$view = 'CustomPage'
+            cell.$view = popview
 
             if (cell.syncComponentId === item.setting.supModule) {
               cell.syncComponentId = ''
@@ -727,6 +708,7 @@
         item.subcards = item.subcards.map(group => {
           group.subButton.uuid = group.uuid
           group.subButton.$menuId = group.uuid
+          group.subButton.$MenuID = this.props.MenuID
           // group.subButton.$forbid = true // 涓嶅0鏄庢暟鎹簮鍙橀噺
           group.subButton.OpenType = 'formSubmit'
           group.subButton.execError = 'never'
@@ -841,22 +823,21 @@
         component = {...component, ...inherit}
         return component
       } else if (component.wrap && component.wrap.datatype === 'public') {
+        component.setting.useMSearch = false
+
         return component
-      }
-
-      if (component.setting) {
-        component.setting.useMSearch = component.setting.useMSearch === 'true'
-        component.setting.syncRefresh = (component.setting.useMSearch && component.setting.syncRefresh === 'true')
-      }
-
-      if (component.wrap && component.wrap.datatype === 'static') {
+      } else if (component.wrap && component.wrap.datatype === 'static') {
         component.format = ''
         component.setting = component.setting || {}
         component.setting.useMSearch = false
-        component.setting.syncRefresh = false
+
+        return component
+      } else if (!component.setting || !component.format) {
+        return component
       }
 
-      if (!component.setting || !component.format) return component  // 1銆佷笉浣跨敤绯荤粺鍑芥暟鏃讹紱2銆� 娌℃湁鍔ㄦ�佹暟鎹�  鏁版嵁鏍煎紡 array 鎴� object
+      component.setting.useMSearch = component.setting.useMSearch === 'true'
+
       if (component.setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
         component.setting.sync = 'false'
         component.setting.laypage = component.setting.laypage === 'true'
@@ -929,9 +910,10 @@
         }
       } else if (params) {
         component.setting.sync = 'false'
-        component.setting.delay = delay
-        delay += 20
       }
+      
+      component.setting.delay = delay
+      delay += 20
 
       if (balMap.has(component.uuid)) {
         component.setting.$hasSyncModule = true
@@ -1100,6 +1082,15 @@
   }
 
   reloadview = () => {
+    window.GLOB.CacheData.delete(this.props.MenuID)
+    
+    if (this.state.config) {
+      this.deleteCache(this.state.config.components)
+      this.state.config.interfaces.forEach(m => {
+        window.GLOB.CacheData.delete(m.uuid)
+      })
+    }
+
     this.setState({
       BID: '',              // 椤甸潰璺宠浆鏃舵惡甯D
       loadingview: true,    // 椤甸潰鍔犺浇涓�
@@ -1125,148 +1116,160 @@
     if (!config || !config.components) return
 
     return config.components.map(item => {
-      if (item.type === 'bar' || item.type === 'line') {
+      let style = null
+
+      if (item.style && item.style.clear === 'left') {
+        style = {clear: 'left'}
+      }
+
+      if (item.type === 'card' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvBarAndLine config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'pie') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvPie config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'scatter') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'dashboard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'simpleform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'stepform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <StepForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'form' && item.subtype === 'tabform') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <TabForm config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'search') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <MainSearch config={item} BID={BID} refreshdata={this.resetSearch} />
-          </Col>
-        )
-      } else if (item.type === 'tabs') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <AntvTabs config={item} mainSearch={mainSearch} />
-          </Col>
-        )
-      } else if (item.type === 'card' && item.subtype === 'datacard') {
-        return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <DataCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'propcard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <PropCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalTable config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'bar' || item.type === 'line') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvBarAndLine config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'pie') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvPie config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'scatter') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'dashboard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'simpleform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <StepForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TabForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'search') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <MainSearch config={item} BID={BID} refreshdata={this.resetSearch} />
+          </Col>
+        )
+      } else if (item.type === 'tabs') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvTabs config={item} mainSearch={mainSearch} />
           </Col>
         )
       } else if (item.type === 'balcony') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <Balcony config={item} data={data}/>
           </Col>
         )
       } else if (item.type === 'timeline') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TimeLine config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'carousel' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CarouselDataCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'carousel' && item.subtype === 'propcard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CarouselPropCard config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
-      } else if (item.type === 'table' && item.subtype === 'tablecard') {
+      } else if (item.type === 'card' && item.subtype === 'tablecard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TableCard config={item} data={data} mainSearch={mainSearch}/>
-          </Col>
-        )
-      } else if (item.type === 'table' && item.subtype === 'normaltable') {
-        return (
-          <Col span={item.width} key={item.uuid}>
-            <NormalTable config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'table' && item.subtype === 'editable') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <EditTable config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'group' && item.subtype === 'normalgroup') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalGroup config={item} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'editor') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <BraftEditor config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'tree') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalTree config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'code') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <SandBox config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'chart') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <CustomChart config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else if (item.type === 'module' && item.subtype === 'voucher') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <Voucher config={item}/>
+          </Col>
+        )
+      } else if (item.type === 'iframe') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <Iframe config={item} data={data} mainSearch={mainSearch}/>
           </Col>
         )
       } else {
@@ -1284,7 +1287,7 @@
         <Row className="component-wrap">{this.getComponents()}</Row>
         {config && config.interfaces.length > 0 ? <MkInterfaces BID={BID} interfaces={config.interfaces}/> : null}
         {config && window.GLOB.breakpoint ? <DebugTable /> : null}
-        {!window.GLOB.mkHS && window.GLOB.systemType !== 'production' ? <PagemsgComponent menu={{MenuName: this.props.MenuName, MenuNo: this.props.MenuNo}} config={config} dict={this.state.dict} /> : null}
+        {!window.GLOB.mkHS && window.GLOB.systemType !== 'production' ? <TableNodes config={config} /> : null}
         {!window.GLOB.mkHS && config ? <SettingComponent config={config} dict={this.state.dict} shortcuts={shortcuts || []}/> : null}
         {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
       </div>
diff --git a/src/tabviews/custom/popview/index.jsx b/src/tabviews/custom/popview/index.jsx
new file mode 100644
index 0000000..a4fbaba
--- /dev/null
+++ b/src/tabviews/custom/popview/index.jsx
@@ -0,0 +1,1016 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { notification, Spin, Row, Col } from 'antd'
+
+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 { getStructuredParams, getStructDefaultParam } from '@/utils/utils-datamanage.js'
+import asyncComponent from '@/utils/asyncComponent'
+import NotFount from '@/components/404'
+import './index.scss'
+
+// 閫氱敤缁勪欢
+const AntvBarAndLine = asyncComponent(() => import('../components/chart/antv-bar-line'))
+const AntvPie = asyncComponent(() => import('../components/chart/antv-pie'))
+const AntvTabs = asyncComponent(() => import('../components/tabs/antv-tabs'))
+const AntvDashboard = asyncComponent(() => import('../components/chart/antv-dashboard'))
+const AntvScatter = asyncComponent(() => import('../components/chart/antv-scatter'))
+const DataCard = asyncComponent(() => import('../components/card/data-card'))
+const PropCard = asyncComponent(() => import('../components/card/prop-card'))
+const SimpleForm = asyncComponent(() => import('../components/form/simple-form'))
+const StepForm = asyncComponent(() => import('../components/form/step-form'))
+const TabForm = asyncComponent(() => import('../components/form/tab-form'))
+const CarouselDataCard = asyncComponent(() => import('../components/carousel/data-card'))
+const CarouselPropCard = asyncComponent(() => import('../components/carousel/prop-card'))
+const TableCard = asyncComponent(() => import('../components/card/table-card'))
+const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
+const NormalTable = asyncComponent(() => import('../components/table/normal-table'))
+const BaseTable = asyncComponent(() => import('../components/table/base-table'))
+const EditTable = asyncComponent(() => import('../components/table/edit-table'))
+const NormalGroup = asyncComponent(() => import('../components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('../components/editor/braft-editor'))
+const SandBox = asyncComponent(() => import('../components/code/sand-box'))
+const NormalTree = asyncComponent(() => import('../components/tree/antd-tree'))
+const Balcony = asyncComponent(() => import('../components/card/balcony'))
+const CustomChart = asyncComponent(() => import('../components/chart/custom-chart'))
+const TimeLine = asyncComponent(() => import('../components/timeline/normal-timeline'))
+const Voucher = asyncComponent(() => import('../components/module/voucher'))
+const Iframe = asyncComponent(() => import('../components/iframe'))
+
+class CustomPage extends Component {
+  static propTpyes = {
+    param: PropTypes.any,        // 鍏朵粬椤甸潰浼犻�掔殑鍙傛暟
+    Tab: PropTypes.string,       // 寮圭獥鏍囩
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    BID: '',              // 椤甸潰璺宠浆鏃舵惡甯D
+    viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
+    lostmsg: '',          // 椤甸潰涓㈠け鏃剁殑鎻愮ず淇℃伅
+    config: null,         // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷粍浠剁瓑
+    mainSearch: null,     // 涓绘悳绱�
+    data: null,           // 鍒楄〃鏁版嵁闆�
+    loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
+  }
+
+  /**
+   * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
+   */
+  async loadconfig () {
+    const { Tab } = this.props
+
+    let config = Tab.config || ''
+
+    if (config) {
+      try { // 閰嶇疆淇℃伅瑙f瀽
+        config = JSON.stringify(config)
+        config = config.replace(/@mywebsite@\//ig, window.GLOB.baseurl)
+        config = JSON.parse(config)
+      } catch (e) {
+        console.warn('Parse Failure')
+        config = ''
+      }
+    }
+
+    // 椤甸潰閰嶇疆瑙f瀽閿欒鏃舵彁绀�
+    if (!config) {
+      this.setState({
+        viewlost: true
+      })
+      return
+    }
+
+    // 椤甸潰鏈惎鐢ㄦ椂锛屾樉绀烘湭鍚敤椤甸潰
+    if (!config.enabled) {
+      this.setState({
+        viewlost: true,
+        lostmsg: this.state.dict['main.view.unenabled']
+      })
+      return
+    }
+
+    // 鏁版嵁缂撳瓨璁剧疆
+    if (config.cacheUseful === 'true') {
+      if (!['day', 'hour'].includes(config.timeUnit)) {
+        config.timeUnit = 'day'
+      }
+      config.cacheTime = config.cacheTime || 1
+    }
+
+    // 鏉冮檺杩囨护
+    let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
+    let balMap = new Map()
+    let param = this.props.param || {} // url鍙傛暟
+
+    window.GLOB.CacheData.set(Tab.uuid, param)
+
+    let userName = sessionStorage.getItem('User_Name') || ''
+    let fullName = sessionStorage.getItem('Full_Name') || ''
+
+    if (sessionStorage.getItem('isEditState') === 'true') {
+      userName = sessionStorage.getItem('CloudUserName') || ''
+      fullName = sessionStorage.getItem('CloudFullName') || ''
+    }
+
+    let regs = [
+      { reg: /@userName@/ig, value: `'${userName}'` },
+      { reg: /@fullName@/ig, value: `'${fullName}'` }
+    ]
+    
+    if (window.GLOB.externalDatabase !== null) {
+      regs.push({
+        reg: /@db@/ig,
+        value: window.GLOB.externalDatabase
+      })
+    }
+
+    config.components = this.filterComponent(config.components, roleId, balMap, param, Tab)
+    
+    // 鑾峰彇涓绘悳绱㈡潯浠�
+    let mainSearch = []
+    config.components.forEach(component => {
+      if (component.type !== 'search') return
+
+      component.search = component.search.map(item => {
+        item.oriInitval = item.initval
+        if (['text', 'select', 'link'].includes(item.type) && param.$searchkey === item.field) {
+          item.initval = param.$searchval
+        }
+
+        return item
+      })
+
+      mainSearch = Utils.initMainSearch(component.search)
+    })
+
+    let params = []
+    let BID = param.$BID || ''
+    let inherit = {}
+
+    if (config.cacheUseful === 'true') { // 缂撳瓨缁ф壙
+      inherit.cacheUseful = config.cacheUseful
+      inherit.timeUnit = config.timeUnit
+      inherit.cacheTime = config.cacheTime
+    }
+
+    config.components = this.formatSetting(config.components, params, mainSearch, inherit, regs, balMap)
+
+    if ([...balMap.keys()].length > 0) {
+      config.components = this.filterBalcony(config.components, balMap)
+    }
+
+    this.setState({
+      BID: BID,
+      config,
+      mainSearch
+    }, () => {
+      if (params.length > 0) {
+        this.loadmaindata(params)
+      }
+    })
+  }
+
+  filterComponent = (components, roleId, balMap, urlparam, Tab) => {
+    return components.filter(item => {
+      item.$pageId = Tab.uuid
+      
+      if (item.style && item.style.boxShadow) {
+        delete item.style.hShadow
+        delete item.style.vShadow
+        delete item.style.shadowBlur
+        delete item.style.shadowColor
+      }
+
+      item.$menuname = (Tab.label || '') + '-' + (item.name || '')
+
+      if (item.type === 'tabs') {
+        if (
+          item.setting.blacklist && item.setting.blacklist.length > 0 &&
+          item.setting.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+        ) {
+          return false
+        }
+
+        item.subtabs = item.subtabs.filter(tab => {
+          if (
+            tab.blacklist && tab.blacklist.length > 0 &&
+            tab.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+          ) {
+            return false
+          } else if (tab.hide === 'true') {
+            return false
+          }
+          return true
+        })
+
+        if (item.setting.supModule) {
+          let pid = item.setting.supModule.pop()
+          item.setting.supModule = pid || ''
+
+          if (item.setting.supModule) {
+            item.setting.controlField = item.setting.controlField.toLowerCase()
+  
+            if (item.setting.supModule === 'preview') {
+              item.setting.supModule = ''
+              let val = ''
+              Object.keys(urlparam).forEach(key => {
+                if (key.toLowerCase() === item.setting.controlField) {
+                  val = urlparam[key]
+                }
+              })
+  
+              item.subtabs = item.subtabs.filter(tab => {
+                if (tab.controlVal === val) {
+                  return false
+                } else if (/,/ig.test(tab.controlVal)) {
+                  return !tab.controlVal.split(',').includes(val)
+                }
+      
+                return true
+              })
+            }
+          }
+        }
+
+        if (item.setting.selectField) {
+          item.setting.selectField = item.setting.selectField.toLowerCase()
+
+          let val = ''
+          Object.keys(urlparam).forEach(key => {
+            if (key.toLowerCase() === item.setting.selectField) {
+              val = urlparam[key]
+            }
+          })
+
+          let activeKey = ''
+
+          item.subtabs.forEach(tab => {
+            if (!activeKey && tab.selectVal === val) {
+              activeKey = tab.uuid
+            }
+          })
+
+          item.activeKey = activeKey
+        }
+
+        item.subtabs = item.subtabs.map(tab => {
+          tab.$pageId = Tab.uuid
+
+          tab.components = this.filterComponent(tab.components, roleId, balMap, urlparam, Tab)
+          return tab
+        })
+
+        return true
+      } else if (item.type === 'group') {
+        if (
+          item.setting.blacklist && item.setting.blacklist.length > 0 &&
+          item.setting.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+        ) {
+          return false
+        }
+
+        item.components = this.filterComponent(item.components, roleId, balMap, urlparam, Tab)
+
+        return true
+      } else if (['pie', 'bar', 'line', 'dashboard', 'scatter', 'chart'].includes(item.type)) {
+        if (
+          item.plot.blacklist && item.plot.blacklist.length > 0 &&
+          item.plot.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+        ) {
+          return false
+        }
+      } else if (item.wrap) {
+        if (
+          item.wrap.blacklist && item.wrap.blacklist.length > 0 &&
+          item.wrap.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+        ) {
+          return false
+        }
+      }
+
+      // 鎼滅储鏉′欢鍒濆鍖�
+      if (item.search && item.search.length > 0) {
+        item.search = Utils.initSearchVal(item.search)
+      }
+
+      if (item.type === 'table') {
+        let statFields = []
+        let getCols = (cols) => {
+          return cols.filter(col => {
+            if (col.blacklist && col.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+              return false
+            } else if (col.Hide === 'true') {
+              return false
+            }
+            
+            if (col.type === 'number') {
+              if (col.sum === 'true' && !statFields.includes(col.field)) {
+                statFields.push(col)
+              }
+              if (typeof(col.decimal) === 'number') {
+                col.round = Math.pow(10, col.decimal)
+                if (col.format === 'percent') {
+                  col.decimal = col.decimal > 2 ? col.decimal - 2 : 0
+                }
+              }
+            } else if (col.type === 'colspan') {
+              col.subcols = getCols(col.subcols || [])
+              if (col.subcols.length === 0) {
+                return false
+              }
+            } else if (col.type === 'custom') {
+              col.elements = col.elements.map(cell => {
+                if (['text', 'number', 'formula'].includes(cell.eleType)) {
+                  if (!cell.height) {
+                    cell.innerHeight = 'auto'
+                  }
+                  if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
+                    cell.round = Math.pow(10, cell.decimal)
+                    if (cell.format === 'percent') {
+                      cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
+                    }
+                  }
+                }
+                return cell
+              })
+            }
+      
+            if (col.linkmenu && col.linkmenu.length > 0) {
+              let menu_id = col.linkmenu.pop()
+              col.linkThdMenu = window.GLOB.mkThdMenus.filter(m => m.MenuID === menu_id)[0] || ''
+            } else {
+              col.linkThdMenu = ''
+            }
+
+            return true
+          })
+        }
+        
+        item.cols = getCols(item.cols)
+        item.statFields = statFields
+      }
+
+      let mutil = false
+      if (item.wrap && item.wrap.supType === 'multi') { // 鏁版嵁鍗″涓婄骇缁勪欢
+        mutil = true
+        item.setting.supModule = item.supNodes[0].componentId
+      } else if (item.setting && item.setting.supModule) {
+        let pid = item.setting.supModule.pop()
+        if (pid && pid !== 'empty') {
+          item.setting.supModule = pid
+        } else {
+          item.setting.supModule = ''
+        }
+      }
+
+      // 鏉冮檺杩囨护
+      if (item.action && item.action.length > 0) {
+        item.action = item.action.filter(cell => {
+          if (cell.hidden === 'true' || ['popview', 'funcbutton'].includes(cell.OpenType)) return false
+
+          cell.logLabel = item.$menuname + '-' + cell.label
+          cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+          cell.$menuId = item.uuid
+          cell.$tabId = Tab.uuid
+          cell.$toolbtn = true
+
+          if (!mutil && cell.syncComponentId === item.setting.supModule) {
+            cell.syncComponentId = ''
+          }
+
+          if (cell.btnstyle) { // 鍏煎
+            cell.style = cell.style || {}
+            cell.style = {...cell.style, ...cell.btnstyle}
+          }
+
+          return true
+        })
+      }
+
+      if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
+        item.subcards && item.subcards.forEach(card => {
+          if (card.style.boxShadow) {
+            delete card.style.hShadow
+            delete card.style.vShadow
+            delete card.style.shadowBlur
+            delete card.style.shadowColor
+          }
+
+          card.elements = card.elements.filter(cell => {
+            if (cell.eleType === 'button') {
+              if (cell.hidden === 'true' || ['popview', 'funcbutton'].includes(cell.OpenType)) return false
+
+              cell.logLabel = item.$menuname + '-' + cell.label
+              cell.Ot = cell.Ot || 'requiredSgl'
+              cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+              cell.$menuId = item.uuid
+              cell.$tabId = Tab.uuid
+
+              if (!mutil && cell.syncComponentId === item.setting.supModule) {
+                cell.syncComponentId = ''
+              }
+              if (card.btnstyle) { // 鍏煎
+                card.style = card.style || {}
+                card.style = {...card.style, ...card.btnstyle}
+              }
+            } else if (['text', 'number', 'formula'].includes(cell.eleType)) {
+              if (!cell.height) {
+                cell.innerHeight = 'auto'
+              }
+              if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
+                cell.round = Math.pow(10, cell.decimal)
+                if (cell.format === 'percent') {
+                  cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
+                }
+              }
+            }
+
+            return true
+          })
+
+          if (!card.backElements || card.backElements.length === 0) return
+
+          card.backElements = card.backElements.filter(cell => {
+            if (cell.eleType === 'button') {
+              if (cell.hidden === 'true' || ['popview', 'funcbutton'].includes(cell.OpenType)) return false
+
+              cell.logLabel = item.$menuname + '-' + cell.label
+              cell.Ot = cell.Ot || 'requiredSgl'
+              cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+              cell.$menuId = item.uuid
+              cell.$tabId = Tab.uuid
+
+              if (!mutil && cell.syncComponentId === item.setting.supModule) {
+                cell.syncComponentId = ''
+              }
+
+              if (card.btnstyle) { // 鍏煎
+                card.style = card.style || {}
+                card.style = {...card.style, ...card.btnstyle}
+              }
+            } else if (['text', 'number', 'formula'].includes(cell.eleType)) {
+              if (!cell.height) {
+                cell.innerHeight = 'auto'
+              }
+              if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
+                cell.round = Math.pow(10, cell.decimal)
+                if (cell.format === 'percent') {
+                  cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
+                }
+              }
+            }
+
+            return true
+          })
+        })
+      } else if (item.type === 'balcony') {
+        if (item.wrap.linkType === 'sync') {
+          item.wrap.syncModuleId = item.wrap.syncModule.pop()
+          balMap.set(item.wrap.syncModuleId, true)
+        } else if (item.wrap.linkType === 'sup') {
+          item.wrap.supModule = item.wrap.supModule.pop()
+          item.setting.supModule = item.wrap.supModule
+        }
+        item.elements = item.elements.filter(cell => {
+          if (cell.eleType === 'button') {
+            if (cell.hidden === 'true' || ['popview', 'funcbutton'].includes(cell.OpenType)) return false
+
+            cell.logLabel = item.$menuname + '-' + cell.label
+            cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+            cell.$menuId = item.uuid
+            cell.$tabId = Tab.uuid
+
+            if (cell.syncComponentId === item.wrap.supModule) {
+              cell.syncComponentId = ''
+            }
+          } else if (['text', 'number', 'formula'].includes(cell.eleType)) {
+            if (!cell.height) {
+              cell.innerHeight = 'auto'
+            }
+            if (cell.eleType === 'number' && typeof(cell.decimal) === 'number') {
+              cell.round = Math.pow(10, cell.decimal)
+              if (cell.format === 'percent') {
+                cell.decimal = cell.decimal > 2 ? cell.decimal - 2 : 0
+              }
+            }
+          }
+
+          return true
+        })
+      } else if (item.type === 'table') {
+        item.cols = item.cols.filter(col => {
+          if (col.type !== 'action') return true
+          col.elements = col.elements.filter(cell => {
+            if (cell.hidden === 'true' || ['popview', 'funcbutton'].includes(cell.OpenType)) return false
+            
+            cell.logLabel = item.$menuname + '-' + cell.label
+            cell.Ot = cell.Ot || 'requiredSgl'
+            cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
+            cell.$menuId = item.uuid
+            cell.$tabId = Tab.uuid
+
+            if (cell.syncComponentId === item.setting.supModule) {
+              cell.syncComponentId = ''
+            }
+
+            if (cell.btnstyle) { // 鍏煎
+              cell.style = cell.style || {}
+              cell.style = {...cell.style, ...cell.btnstyle}
+            }
+
+            return true
+          })
+          return col.elements.length !== 0
+        })
+
+        if (item.subtype === 'editable') {
+          item.submit.logLabel = item.$menuname + '-鎻愪氦'
+          item.submit.$menuId = item.uuid
+        }
+      } else if (item.type === 'form') {
+        item.subcards = item.subcards.map(group => {
+          group.subButton.uuid = group.uuid
+          group.subButton.$menuId = group.uuid
+          group.subButton.$MenuID = Tab.$MenuID
+          group.subButton.OpenType = 'formSubmit'
+          group.subButton.execError = 'never'
+          group.subButton.logLabel = item.$menuname + '-' + group.subButton.label
+
+          if (!group.subButton.Ot) {
+            group.subButton.Ot = item.wrap.datatype === 'static' ? 'notRequired' : 'requiredSgl'
+          }
+
+          group.subButton.syncComponentId = group.subButton.syncComponent ? group.subButton.syncComponent.pop() : ''
+
+          if (group.subButton.syncComponentId === item.setting.supModule) {
+            group.subButton.syncComponentId = ''
+          }
+
+          group.fields = group.fields.map(cell => {
+            // 鏁版嵁婧恠ql璇彞锛岄澶勭悊锛屾潈闄愰粦鍚嶅崟瀛楁璁剧疆涓洪殣钘忚〃鍗�
+            if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
+              let _option = Utils.getSelectQueryOptions(cell)
+      
+              cell.data_sql = Utils.formatOptions(_option.sql)
+              cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
+              cell.arr_field = _option.field
+            }
+      
+            // 瀛楁鏉冮檺榛戝悕鍗�
+            if (!cell.blacklist || !roleId || cell.blacklist.length === 0) return cell
+            if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+              cell.hidden = 'true'
+            }
+      
+            return cell
+          })
+
+          return group
+        })
+      }
+      
+      return true
+    })
+  }
+
+  filterBalcony = (components, balMap) => {
+    return components.filter(item => {
+      if (item.type === 'tabs') {
+        item.subtabs = item.subtabs.map(tab => {
+          tab.components = this.filterBalcony(tab.components, balMap)
+          return tab
+        })
+      } else if (item.type === 'group') {
+        item.components = this.filterBalcony(item.components, balMap)
+      }
+
+      if (item.type === 'balcony' && item.wrap.linkType === 'sync') {
+        let conf = balMap.get(item.wrap.syncModuleId)
+
+        if (!conf || conf === true) {
+          return false
+        }
+        
+        item.syncConfig = {
+          uuid: conf.uuid,
+          wrap: conf.wrap,
+          setting: conf.setting,
+          columns: conf.columns
+        }
+
+        if (item.wrap.checkAll === 'show') {
+          if (conf.subtype === 'datacard' && conf.wrap.cardType !== 'checkbox') {
+            item.wrap.checkAll = 'hidden'
+          } else if (conf.subtype === 'normaltable' && conf.wrap.tableType !== 'checkbox') {
+            item.wrap.checkAll = 'hidden'
+          }
+        }
+      }
+      
+      return true
+    })
+  }
+
+  getPrinter = (item, parentId) => {
+    let _item = window.GLOB.UserCacheMap.get(parentId + item.uuid)
+
+    if (_item) {
+      item.printer = _item.printer || ''
+      item.verify.defaultPrinter = _item.printer || ''
+      if (item.verify.printerTypeList && _item.printerList) {
+        item.verify.printerTypeList = item.verify.printerTypeList.map(cell => {
+          cell.printer = _item.printerList[cell.Value] || ''
+
+          return cell
+        })
+      }
+    }
+
+    return item
+  }
+
+  // 鏍煎紡鍖栭粯璁よ缃�
+  formatSetting = (components, params, mainSearch, inherit, regs, balMap) => {
+    let delay = 20
+    return components.map(component => {
+      if (component.type === 'tabs') {
+        component.subtabs = component.subtabs.map(tab => {
+          tab.components = this.formatSetting(tab.components, null, null, inherit, regs, balMap)
+          tab = {...tab, ...inherit}
+          return tab
+        })
+        return component
+      } else if (component.type === 'group') {
+        component.components = this.formatSetting(component.components, null, null, inherit, regs, balMap)
+        component = {...component, ...inherit}
+        return component
+      } else if (component.wrap && component.wrap.datatype === 'public') {
+        component.setting.useMSearch = false
+
+        return component
+      } else if (component.wrap && component.wrap.datatype === 'static') {
+        component.format = ''
+        component.setting = component.setting || {}
+        component.setting.useMSearch = false
+
+        return component
+      } else if (!component.setting || !component.format) {
+        return component
+      }
+
+      component.setting.useMSearch = component.setting.useMSearch === 'true'
+
+      if (component.setting.interType !== 'system') { // 涓嶄娇鐢ㄧ郴缁熷嚱鏁版椂
+        component.setting.sync = 'false'
+        component.setting.laypage = component.setting.laypage === 'true'
+        return component
+      }
+
+      let _customScript = ''
+      component.scripts && component.scripts.forEach(script => {
+        if (script.status !== 'false') {
+          _customScript += `
+          ${script.sql}
+          `
+        }
+      })
+      delete component.scripts
+      component.setting.$name = component.$menuname || ''
+      component.setting.execute = component.setting.execute !== 'false'  // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
+      component.setting.laypage = component.setting.laypage === 'true'   // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
+
+      if (!component.setting.execute) {
+        component.setting.dataresource = ''
+      }
+      if (/\s/.test(component.setting.dataresource)) {
+        component.setting.dataresource = '(' + component.setting.dataresource + ') tb'
+      }
+  
+      if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+        component.setting.dataresource = component.setting.dataresource.replace(/\$@/ig, '/*').replace(/@datam@/ig, '\'Y\'')
+        component.setting.dataresource = component.setting.dataresource.replace(/@\$/ig, '*/')
+        _customScript = _customScript.replace(/\$@/ig, '/*').replace(/@datam@/ig, '\'Y\'')
+        _customScript = _customScript.replace(/@\$/ig, '*/')
+      } else {
+        component.setting.dataresource = component.setting.dataresource.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+        _customScript = _customScript.replace(/@\$|\$@/ig, '').replace(/@datam@/ig, '\'\'')
+      }
+
+      regs.forEach(cell => {
+        component.setting.dataresource = component.setting.dataresource.replace(cell.reg, cell.value)
+        _customScript = _customScript.replace(cell.reg, cell.value)
+      })
+
+      component.setting.customScript = _customScript // 鏁寸悊鍚庤嚜瀹氫箟鑴氭湰
+
+      // dataName 绯荤粺鐢熸垚鐨勬暟鎹簮鍚嶇О
+      if (component.setting.sync === 'true') {
+        component.dataName = Utils.getdataName()
+      }
+
+      // floor    缁勪欢鐨勫眰绾�
+      // pageable 鏄惁鍒嗛〉锛岀粍浠跺睘鎬э紝涓嶅垎椤电殑缁勪欢鎵嶅彲浠ョ粺涓�鏌ヨ
+      if (params && (!component.pageable || (component.pageable && !component.setting.laypage)) && component.setting.onload === 'true' && component.setting.sync === 'true') {
+        let searchlist = []
+        if (component.search && component.search.length > 0) {
+          searchlist = Utils.initMainSearch(component.search)
+        }
+        if (component.setting.useMSearch) {
+          let keys = searchlist.map(item => item.key)
+          mainSearch.forEach(item => {
+            if (!keys.includes(item.key)) {
+              searchlist.push(item)
+            }
+          })
+        }
+
+        if (searchlist.filter(item => item.required && item.value === '').length > 0) {
+          component.setting.sync = 'false'
+          component.setting.onload = 'false'
+        } else {
+          params.push(getStructDefaultParam(component, searchlist, params.length === 0))
+        }
+      } else if (params) {
+        component.setting.sync = 'false'
+      }
+
+      component.setting.delay = delay
+      delay += 20
+
+      if (balMap.has(component.uuid)) {
+        component.setting.$hasSyncModule = true
+        balMap.set(component.uuid, component)
+      }
+
+      return component
+    })
+  }
+
+  /**
+   * @description 涓昏〃鏁版嵁鍔犺浇
+   */ 
+  loadmaindata = (params) => {
+    let param = getStructuredParams(params, this.state.config, this.state.BID || '')
+
+    this.setState({loading: true})
+
+    Api.genericInterface(param).then(result => {
+      if (result.status) {
+        delete result.status
+        delete result.message
+        delete result.ErrMesg
+        delete result.ErrCode
+
+        this.setState({
+          data: result,
+          loading: false
+        })
+      } else {
+        this.setState({
+          data: '',
+          loading: false
+        })
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+    })
+  }
+
+  UNSAFE_componentWillMount () {
+    // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
+    this.loadconfig()
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+
+    window.GLOB.CacheData.delete(this.props.Tab.uuid)
+    if (this.state.config) {
+      this.deleteCache(this.state.config.components)
+    }
+  }
+
+  deleteCache = (components) => {
+    components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          this.deleteCache(tab.components)
+        })
+      } else if (item.type === 'group') {
+        this.deleteCache(item.components)
+      } else {
+        window.GLOB.CacheData.delete(item.uuid)
+      }
+    })
+  }
+
+  resetSearch = (search) => {
+    this.setState({mainSearch: null}, () => {
+      this.setState({mainSearch: search})
+    })
+  }
+
+  getComponents = () => {
+    const { config, BID, data, mainSearch } = this.state
+
+    if (!config) return
+
+    return config.components.map(item => {
+      let style = null
+
+      if (item.style && item.style.clear === 'left') {
+        style = {clear: 'left'}
+      }
+
+      if (item.type === 'card' && item.subtype === 'datacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <DataCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'card' && item.subtype === 'propcard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <PropCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'table' && item.subtype === 'basetable') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <BaseTable config={item}/>
+          </Col>
+        )
+      } else if (item.type === 'bar' || item.type === 'line') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvBarAndLine config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'pie') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvPie config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'scatter') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvScatter config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'dashboard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvDashboard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'simpleform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <SimpleForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <StepForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TabForm config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'search') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <MainSearch config={item} BID={BID} refreshdata={this.resetSearch} />
+          </Col>
+        )
+      } else if (item.type === 'tabs') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <AntvTabs config={item} mainSearch={mainSearch} />
+          </Col>
+        )
+      } else if (item.type === 'balcony') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <Balcony config={item} data={data}/>
+          </Col>
+        )
+      } else if (item.type === 'timeline') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TimeLine config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'carousel' && item.subtype === 'datacard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <CarouselDataCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'carousel' && item.subtype === 'propcard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <CarouselPropCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'card' && item.subtype === 'tablecard') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <TableCard config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalTable config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'table' && item.subtype === 'editable') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <EditTable config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'group' && item.subtype === 'normalgroup') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalGroup config={item} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'editor') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <BraftEditor config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'tree') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <NormalTree config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'code') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <SandBox config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'chart') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <CustomChart config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else if (item.type === 'module' && item.subtype === 'voucher') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <Voucher config={item}/>
+          </Col>
+        )
+      } else if (item.type === 'iframe') {
+        return (
+          <Col span={item.width} style={style} key={item.uuid}>
+            <Iframe config={item} data={data} mainSearch={mainSearch}/>
+          </Col>
+        )
+      } else {
+        return null
+      }
+    })
+  }
+
+  render() {
+    const { viewlost, config, loading } = this.state
+
+    return (
+      <div className={'pop-page-wrap ' + (loading ? 'loading' : '')} style={config ? config.style : null}>
+        {loading ? <Spin className="view-spin" size="large" /> : null}
+        <Row className="component-wrap">{this.getComponents()}</Row>
+        {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
+      </div>
+    )
+  }
+}
+
+export default CustomPage
\ No newline at end of file
diff --git a/src/tabviews/custom/popview/index.scss b/src/tabviews/custom/popview/index.scss
new file mode 100644
index 0000000..6938042
--- /dev/null
+++ b/src/tabviews/custom/popview/index.scss
@@ -0,0 +1,60 @@
+.pop-page-wrap {
+  position: relative;
+  min-height: calc(100vh - 94px);
+  height: 100%;
+  padding-top: 16px;
+  padding-bottom: 80px;
+  padding-left: 16px;
+  padding-right: 16px;
+  background-size: 100%;
+  
+  .component-wrap >.ant-col {
+    min-height: 0;
+  }
+  .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% - 16px);
+    top: calc(50vh - 70px);
+  }
+  
+  .common-table-copy {
+    position: fixed;
+    z-index: 2;
+    bottom: 65px;
+    right: 30px;
+    width: 40px;
+    height: 40px;
+  }
+  .ant-btn-link:hover {
+    opacity: 0.8;
+  }
+  .button-list.toolbar-button {
+    button {
+      height: auto;
+      min-height: 28px;
+    }
+  }
+}
+.pop-page-wrap.loading {
+  .ant-spin-spinning:not(.view-spin) {
+    display: none;
+  }
+}
diff --git a/src/tabviews/debugtable/index.jsx b/src/tabviews/debugtable/index.jsx
index 3dcaf14..afcae04 100644
--- a/src/tabviews/debugtable/index.jsx
+++ b/src/tabviews/debugtable/index.jsx
@@ -26,6 +26,7 @@
     setting: {
       interType: 'system',
       execute: true,
+      laypage: true,
       dataresource: '(select * from s_debug_value_log where createuserid=@userid@) tb'
     },
     arr_field: 'ID,Sort,CDefine1,CDefine2,CDefine3,CDefine4,CDefine5,CDefine6,CDefine7,createdate'
@@ -41,7 +42,7 @@
       loading: true
     })
 
-    let param = UtilsDM.getQueryDataParams(setting, arr_field, [], 'sort', 1, 9999, '')
+    let param = UtilsDM.getQueryDataParams(setting, arr_field, [], 'sort', 1, 50, '')
 
     let result = await Api.genericInterface(param)
 
diff --git a/src/tabviews/formtab/index.jsx b/src/tabviews/formtab/index.jsx
index 0b54f01..d5dab3d 100644
--- a/src/tabviews/formtab/index.jsx
+++ b/src/tabviews/formtab/index.jsx
@@ -109,6 +109,7 @@
             if (tab.supMenu === 'mainTable') {
               tab.supMenu = MenuID
             }
+            tab.ContainerId = this.state.ContainerId
             return window.GLOB.mkActions[tab.linkTab]
           })
         })
@@ -118,6 +119,7 @@
             if (tab.supMenu === 'mainTable') {
               tab.supMenu = MenuID
             }
+            tab.ContainerId = this.state.ContainerId
             return true
           })
         })
@@ -654,7 +656,6 @@
                         Tab={_tab}
                         MenuID={_tab.linkTab}
                         SupMenuID={this.props.MenuID}
-                        ContainerId={this.state.ContainerId}
                         BID={this.state.BIDs[_tab.supMenu] || ''}
                         BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                       />
diff --git a/src/tabviews/home/defaulthome/index.scss b/src/tabviews/home/defaulthome/index.scss
index ec0d0d0..93c83b7 100644
--- a/src/tabviews/home/defaulthome/index.scss
+++ b/src/tabviews/home/defaulthome/index.scss
@@ -14,6 +14,7 @@
       padding-left: 12px;
       padding-right: 12px;
       margin-bottom: 24px;
+      flex: 1;
       .ant-card {
         box-sizing: border-box;
         margin: 0;
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index 278430f..51ebb7b 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Spin, Switch, Row, Col, Tabs} from 'antd'
+import { notification, Spin, Switch, Row, Col, Tabs, Modal } from 'antd'
 
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -33,7 +33,6 @@
     MenuID: PropTypes.string,        // 鑿滃崟Id
     SupMenuID: PropTypes.string,     // 涓婄骇鑿滃崟Id
     mainSearch: PropTypes.any,       // 涓昏〃鎼滅储鏉′欢
-    ContainerId: PropTypes.any,      // 涓夌骇鑿滃崟Container(html) ID
   }
 
   state = {
@@ -99,6 +98,7 @@
         config.setting.$name = Tab.label
         config.setting.foreignKey = Tab.foreignKey || ''
         config.setting.supModule = Tab.supMenu || ''
+        config.setting.ContainerId = Tab.ContainerId || ''
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -407,7 +407,7 @@
         statFValue: [],
         total: 0
       })
-      MKEmitter.emit('changeTableLine', this.props.ContainerId, this.props.Tab.uuid, '', '')
+      MKEmitter.emit('changeTableLine', this.props.Tab.ContainerId, this.props.Tab.uuid, '', '')
       return
     } else if (window.GLOB.systemType === 'production' && setting.interType === 'custom' && !setting.proInterface) {
       notification.warning({
@@ -421,7 +421,7 @@
     this.setState({
       selectedData: []
     })
-    MKEmitter.emit('changeTableLine', this.props.ContainerId, this.props.Tab.uuid, '', '')
+    MKEmitter.emit('changeTableLine', this.props.Tab.ContainerId, this.props.Tab.uuid, '', '')
 
     if (setting.interType === 'custom' && loadCustomApi) {
       if (setting.execTime === 'once') {
@@ -668,11 +668,17 @@
         loading: false
       })
 
-      notification.error({
-        top: 92,
-        message: prex + result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: prex + result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: prex + result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -1025,7 +1031,6 @@
                       columns={columns}
                       MenuID={this.props.MenuID}
                       selectedData={selectedData}
-                      ContainerId={this.props.ContainerId}
                     />
                   </div>
                   <div className="subtable-box">
@@ -1045,7 +1050,6 @@
                       MenuID={this.props.MenuID}
                       loading={this.state.loading}
                       statFValue={this.state.statFValue}
-                      ContainerId={this.props.ContainerId}
                       refreshdata={this.refreshbytable}
                       chgSelectData={this.changeSelectedData}
                     />
@@ -1065,7 +1069,6 @@
                     MenuID={this.props.MenuID}
                     loading={this.state.loading}
                     tableId={this.props.Tab.uuid}
-                    ContainerId={this.props.ContainerId}
                   />
                 </Col>
               )
@@ -1094,7 +1097,6 @@
               columns={columns}
               MenuID={this.props.MenuID}
               selectedData={selectedData}
-              ContainerId={this.props.ContainerId}
             />
           </div>
           <div className="subtable-box">
@@ -1114,7 +1116,6 @@
               MenuID={this.props.MenuID}
               loading={this.state.loading}
               statFValue={this.state.statFValue}
-              ContainerId={this.props.ContainerId}
               refreshdata={this.refreshbytable}
               chgSelectData={this.changeSelectedData}
             />
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index 6a11de2..9ef2fed 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Spin} from 'antd'
+import { notification, Spin, Modal } from 'antd'
 
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -600,11 +600,17 @@
       this.setState({
         loading: false
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
diff --git a/src/tabviews/treepage/index.jsx b/src/tabviews/treepage/index.jsx
index a173c8a..11e2f0b 100644
--- a/src/tabviews/treepage/index.jsx
+++ b/src/tabviews/treepage/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { notification, Spin, Tabs, Tree, Row, Col, Card, Input, Empty } from 'antd'
+import { notification, Spin, Tabs, Tree, Row, Col, Card, Input, Empty, Modal } from 'antd'
 import { FolderOpenOutlined, FolderOutlined, FileOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
@@ -122,6 +122,8 @@
           if (tab.supMenu === 'mainTable') {
             tab.supMenu = MenuID
           }
+          tab.ContainerId = this.state.ContainerId
+
           return window.GLOB.mkActions[tab.linkTab]}
         )
       })
@@ -321,11 +323,17 @@
         treeNodes: [],
         treedata: []
       })
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
+      if (result.ErrCode === 'N') {
+        Modal.error({
+          title: result.message,
+        })
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
     }
   }
 
@@ -619,7 +627,6 @@
                           MenuID={_tab.linkTab}
                           mainSearch={null}
                           SupMenuID={this.props.MenuID}
-                          ContainerId={this.state.ContainerId}
                           BID={this.state.BIDs[_tab.supMenu] || ''}
                           BData={this.state.BIDs[_tab.supMenu + 'data'] || ''}
                         /> : null}
diff --git a/src/tabviews/zshare/actionList/asyncButtonComponent.jsx b/src/tabviews/zshare/actionList/asyncButtonComponent.jsx
index 30d70ed..008bedb 100644
--- a/src/tabviews/zshare/actionList/asyncButtonComponent.jsx
+++ b/src/tabviews/zshare/actionList/asyncButtonComponent.jsx
@@ -21,7 +21,12 @@
       this.setState({component})
     }
 
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
       const btn = this.props.btn || {}
diff --git a/src/tabviews/zshare/actionList/excelInbutton/index.jsx b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
index 0c497ea..97a19f4 100644
--- a/src/tabviews/zshare/actionList/excelInbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -421,9 +421,17 @@
             }
           }
         } else {
-          if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-            param.rduri = window.GLOB.mainSystemApi
-          } else if (btn.sysInterface !== 'true') {
+          if (btn.sysInterface === 'true') {
+            if (window.GLOB.mainSystemApi) {
+              param.rduri = window.GLOB.mainSystemApi
+            }
+          } else if (btn.sysInterface === 'external') {
+            if (window.GLOB.systemType === 'production') {
+              param.$token = btn.exProInterface || ''
+            } else {
+              param.$token = btn.exInterface || ''
+            }
+          } else {
             if (window.GLOB.systemType === 'production' && btn.proInterface) {
               param.rduri = btn.proInterface
             } else {
diff --git a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
index 80a9e9d..f658b86 100644
--- a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -251,9 +251,17 @@
             }
           }
         } else {
-          if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-            param.rduri = window.GLOB.mainSystemApi
-          } else if (btn.sysInterface !== 'true') {
+          if (btn.sysInterface === 'true') {
+            if (window.GLOB.mainSystemApi) {
+              param.rduri = window.GLOB.mainSystemApi
+            }
+          } else if (btn.sysInterface === 'external') {
+            if (window.GLOB.systemType === 'production') {
+              param.$token = btn.exProInterface || ''
+            } else {
+              param.$token = btn.exInterface || ''
+            }
+          } else {
             if (window.GLOB.systemType === 'production' && btn.proInterface) {
               param.rduri = btn.proInterface
             } else {
@@ -303,9 +311,17 @@
                 }
               }
             } else {
-              if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-                res.rduri = window.GLOB.mainSystemApi
-              } else if (btn.sysInterface !== 'true') {
+              if (btn.sysInterface === 'true') {
+                if (window.GLOB.mainSystemApi) {
+                  res.rduri = window.GLOB.mainSystemApi
+                }
+              } else if (btn.sysInterface === 'external') {
+                if (window.GLOB.systemType === 'production') {
+                  res.$token = btn.exProInterface || ''
+                } else {
+                  res.$token = btn.exInterface || ''
+                }
+              } else {
                 if (window.GLOB.systemType === 'production' && btn.proInterface) {
                   res.rduri = btn.proInterface
                 } else {
@@ -378,9 +394,17 @@
             }
           }
         } else {
-          if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-            res.rduri = window.GLOB.mainSystemApi
-          } else if (btn.sysInterface !== 'true') {
+          if (btn.sysInterface === 'true') {
+            if (window.GLOB.mainSystemApi) {
+              res.rduri = window.GLOB.mainSystemApi
+            }
+          } else if (btn.sysInterface === 'external') {
+            if (window.GLOB.systemType === 'production') {
+              res.$token = btn.exProInterface || ''
+            } else {
+              res.$token = btn.exInterface || ''
+            }
+          } else {
             if (window.GLOB.systemType === 'production' && btn.proInterface) {
               res.rduri = btn.proInterface
             } else {
@@ -456,9 +480,17 @@
           }
         }
       } else {
-        if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-          param.rduri = window.GLOB.mainSystemApi
-        } else if (btn.sysInterface !== 'true') {
+        if (btn.sysInterface === 'true') {
+          if (window.GLOB.mainSystemApi) {
+            param.rduri = window.GLOB.mainSystemApi
+          }
+        } else if (btn.sysInterface === 'external') {
+          if (window.GLOB.systemType === 'production') {
+            param.$token = btn.exProInterface || ''
+          } else {
+            param.$token = btn.exInterface || ''
+          }
+        } else {
           if (window.GLOB.systemType === 'production' && btn.proInterface) {
             param.rduri = btn.proInterface
           } else {
diff --git a/src/tabviews/zshare/actionList/index.jsx b/src/tabviews/zshare/actionList/index.jsx
index 6760ac3..7dd0d4b 100644
--- a/src/tabviews/zshare/actionList/index.jsx
+++ b/src/tabviews/zshare/actionList/index.jsx
@@ -27,7 +27,6 @@
     actions: PropTypes.array,         // 鎸夐挳缁�
     columns: PropTypes.array,         // 鏄剧ず鍒�
     setting: PropTypes.any,           // 椤甸潰閫氱敤璁剧疆
-    ContainerId: PropTypes.any        // tab椤甸潰ID锛岀敤浜庡脊绐楁帶鍒�
   }
 
   state = {}
@@ -37,7 +36,7 @@
   }
 
   getButtonList = (actions) => {
-    const { BID, BData, MenuID, columns, setting, ContainerId, selectedData, lock } = this.props
+    const { BID, BData, MenuID, columns, setting, selectedData, lock } = this.props
     return actions.map(item => {
       if (['exec', 'prompt', 'pop'].includes(item.OpenType)) {
         return (
@@ -50,7 +49,6 @@
             BData={BData}
             setting={setting}
             columns={columns}
-            ContainerId={ContainerId}
             selectedData={selectedData}
           />
         )
@@ -99,6 +97,7 @@
             show={item.show || 'actionList'}
             disabled={lock || false}
             btn={item}
+            BID={BID}
             BData={BData}
             MenuID={MenuID}
             selectedData={selectedData}
@@ -140,7 +139,6 @@
               btn={item}
               BData={BData}
               setting={setting}
-              ContainerId={ContainerId}
               selectedData={selectedData}
             />
           )
diff --git a/src/tabviews/zshare/actionList/normalbutton/index.jsx b/src/tabviews/zshare/actionList/normalbutton/index.jsx
index ff1ef81..3e58939 100644
--- a/src/tabviews/zshare/actionList/normalbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -27,7 +27,6 @@
     btn: PropTypes.object,            // 鎸夐挳
     columns: PropTypes.any,           // 瀛楁鍒�
     setting: PropTypes.any,           // 椤甸潰閫氱敤璁剧疆
-    ContainerId: PropTypes.any,       // tab椤甸潰ID锛岀敤浜庡脊绐楁帶鍒�
     disabled: PropTypes.any,          // 琛屾寜閽鐢�
   }
 
@@ -1633,9 +1632,17 @@
         result.open_key = Utils.encryptOpenKey(result.secretkey, result.timestamp)
       }
     } else {
-      if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-        result.rduri = window.GLOB.mainSystemApi
-      } else if (btn.sysInterface !== 'true') {
+      if (btn.sysInterface === 'true') {
+        if (window.GLOB.mainSystemApi) {
+          result.rduri = window.GLOB.mainSystemApi
+        }
+      } else if (btn.sysInterface === 'external') {
+        if (window.GLOB.systemType === 'production') {
+          result.$token = btn.exProInterface || ''
+        } else {
+          result.$token = btn.exInterface || ''
+        }
+      } else {
         if (window.GLOB.systemType === 'production' && btn.proInterface) {
           result.rduri = btn.proInterface
         } else {
@@ -1905,16 +1912,22 @@
       node && node.scrollIntoView({behavior: 'smooth', block: 'center', inline: 'nearest'})
     }
 
-    if (btn.MenuID && Array.isArray(btn.openmenu) && btn.openmenu.length > 0) {
-      let newtab = {
-        MenuID: btn.MenuID,
-        MenuName: btn.MenuName,
-        MenuNo: btn.MenuNo,
-        type: btn.tabType,
-        param: {
-          $BID: id
+    if (btn.openmenu && Array.isArray(btn.openmenu) && btn.openmenu.length > 0) {
+      let menuId = btn.openmenu.slice(-1)[0]
+      let newtab = window.GLOB.mkThdMenus.filter(m => m.MenuID === menuId)[0]
+
+      if (!newtab && btn.MenuID) {
+        newtab = {
+          MenuID: btn.MenuID,
+          MenuName: btn.MenuName,
+          MenuNo: btn.MenuNo,
+          type: btn.tabType
         }
+      } else if (!newtab) {
+        return
       }
+
+      newtab = {...newtab, param: { $BID: id }}
 
       if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
         MKEmitter.emit('modifyTabs', newtab, 'replace')
@@ -2534,7 +2547,7 @@
    * @description 鏄剧ず妯℃�佹
    */
   getModels = () => {
-    const { setting, BID, btn, BData } = this.props
+    const { BID, btn, BData } = this.props
     const { btnconfig, visible } = this.state
 
     if (!btnconfig || !btnconfig.setting) return null
@@ -2583,12 +2596,9 @@
     } else {
       let container = document.body
 
-      if (
-        (setting.tabType === 'main' && btnconfig.setting.container === 'tab' && this.props.ContainerId) ||
-        (btnconfig.setting.container === 'tab' && btn.ContainerId)
-      ) {
+      if (btnconfig.setting.container === 'tab' && btn.ContainerId) {
         width = btnconfig.setting.width > 100 ? btnconfig.setting.width : btnconfig.setting.width + '%'
-        container = () => document.getElementById(this.props.ContainerId || btn.ContainerId)
+        container = () => document.getElementById(btn.ContainerId)
       }
       return (
         <Modal
diff --git a/src/tabviews/zshare/actionList/popupbutton/index.jsx b/src/tabviews/zshare/actionList/popupbutton/index.jsx
index 6456eb3..4449ae5 100644
--- a/src/tabviews/zshare/actionList/popupbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -12,6 +12,7 @@
 
 const SubTabTable = asyncSpinComponent(() => import('@/tabviews/subtabtable'))
 const CustomPage = asyncSpinComponent(() => import('@/tabviews/custom'))
+const PopView = asyncSpinComponent(() => import('@/tabviews/custom/popview'))
 
 class PopupButton extends Component {
   static propTpyes = {
@@ -244,7 +245,7 @@
         ratio = ratio + 'vw'
       }
       return <Modal
-        className={'popview-modal ' + (btn.$view === 'CustomPage' ? 'custom-popview' : '')}
+        wrapClassName={'popview-modal ' + (!btn.$view ? '' : 'custom-popview')}
         title={btn.label}
         width={ratio}
         maskClosable={btn.clickouter === 'close'}
@@ -255,14 +256,9 @@
         ]}
         destroyOnClose
       >
-        {btn.$view !== 'CustomPage' ? <SubTabTable
-          Tab={btn}
-          MenuID={btn.linkTab}
-          SupMenuID={this.props.MenuID}
-          BID={popData ? primaryId : this.props.BID}
-          BData={popData || this.props.BData}
-        /> : null}
+        {!btn.$view ? <SubTabTable Tab={btn} MenuID={btn.linkTab} SupMenuID={this.props.MenuID} BID={popData ? primaryId : this.props.BID} BData={popData || this.props.BData}/> : null}
         {btn.$view === 'CustomPage' ? <CustomPage Tab={btn} MenuID={btn.uuid} MenuName={btn.label} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
+        {btn.$view === 'popview' ? <PopView Tab={btn} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
       </Modal>
     } else {
       let height = '100vh'
@@ -288,7 +284,7 @@
       return (
         <Drawer
           title={btn.label}
-          className={btn.$view === 'CustomPage' ? 'custom-drawer-popview' : 'table-drawer-popview'}
+          className={!btn.$view ? 'table-drawer-popview' : 'custom-drawer-popview' }
           width={width}
           height={height}
           maskClosable={btn.clickouter === 'close'}
@@ -297,14 +293,9 @@
           placement={btn.placement || 'right'}
           destroyOnClose
         >
-          {btn.$view !== 'CustomPage' ? <SubTabTable
-            Tab={btn}
-            MenuID={btn.linkTab}
-            SupMenuID={this.props.MenuID}
-            BID={popData ? primaryId : this.props.BID}
-            BData={popData || this.props.BData}
-          /> : null}
+          {!btn.$view ? <SubTabTable Tab={btn} MenuID={btn.linkTab} SupMenuID={this.props.MenuID} BID={popData ? primaryId : this.props.BID} BData={popData || this.props.BData}/> : null}
           {btn.$view === 'CustomPage' ? <CustomPage Tab={btn} MenuName={btn.label} MenuID={btn.uuid} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
+          {btn.$view === 'popview' ? <PopView Tab={btn} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
           <div className="close-drawer">
             <Button onClick={this.popclose}>
               鍏抽棴
diff --git a/src/tabviews/zshare/actionList/popupbutton/index.scss b/src/tabviews/zshare/actionList/popupbutton/index.scss
index 23b691d..72a6a3c 100644
--- a/src/tabviews/zshare/actionList/popupbutton/index.scss
+++ b/src/tabviews/zshare/actionList/popupbutton/index.scss
@@ -4,6 +4,9 @@
     .custom-page-wrap {
       min-height: 200px;
     }
+    .pop-page-wrap {
+      min-height: 200px;
+    }
   }
 }
 .table-drawer-popview {
@@ -43,6 +46,9 @@
       .custom-page-wrap {
         min-height: calc(100vh - 110px);
       }
+      .pop-page-wrap {
+        min-height: calc(100vh - 110px);
+      }
     }
   }
 }
diff --git a/src/tabviews/zshare/actionList/printbutton/index.jsx b/src/tabviews/zshare/actionList/printbutton/index.jsx
index f9fc037..9caedab 100644
--- a/src/tabviews/zshare/actionList/printbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -27,7 +27,6 @@
     MenuID: PropTypes.string,         // 鑿滃崟ID
     btn: PropTypes.object,            // 鎸夐挳
     setting: PropTypes.any,           // 椤甸潰閫氱敤璁剧疆
-    ContainerId: PropTypes.any,       // tab椤甸潰ID锛岀敤浜庡脊绐楁帶鍒�
     disabled: PropTypes.any,          // 琛屾寜閽鐢�
   }
 
@@ -835,9 +834,17 @@
           }
         }
       } else {
-        if (btn.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-          res.rduri = window.GLOB.mainSystemApi
-        } else if (btn.sysInterface !== 'true') {
+        if (btn.sysInterface === 'true') {
+          if (window.GLOB.mainSystemApi) {
+            res.rduri = window.GLOB.mainSystemApi
+          }
+        } else if (btn.sysInterface === 'external') {
+          if (window.GLOB.systemType === 'production') {
+            res.$token = btn.exProInterface || ''
+          } else {
+            res.$token = btn.exInterface || ''
+          }
+        } else {
           if (window.GLOB.systemType === 'production' && btn.proInterface) {
             res.rduri = btn.proInterface
           } else {
@@ -1658,7 +1665,7 @@
    * @description 鏄剧ず妯℃�佹
    */
   getModels = () => {
-    const { setting, BID, btn } = this.props
+    const { BID, btn } = this.props
     const { btnconfig } = this.state
 
     if (!this.state.visible || !btnconfig || !btnconfig.setting) return null
@@ -1668,12 +1675,9 @@
     let clickouter = false
     let container = document.body
 
-    if (
-      (setting.tabType === 'main' && btnconfig.setting.container === 'tab' && this.props.ContainerId) ||
-      (btnconfig.setting.container === 'tab' && btn.ContainerId)
-    ) {
+    if (btnconfig.setting.container === 'tab' && btn.ContainerId) {
       width = btnconfig.setting.width > 100 ? btnconfig.setting.width : btnconfig.setting.width + '%'
-      container = () => document.getElementById(this.props.ContainerId || btn.ContainerId)
+      container = () => document.getElementById(btn.ContainerId)
     }
 
     if (btnconfig.setting.clickouter === 'close') {
diff --git a/src/tabviews/zshare/actionList/tabbutton/index.jsx b/src/tabviews/zshare/actionList/tabbutton/index.jsx
index ba8c96a..8809c85 100644
--- a/src/tabviews/zshare/actionList/tabbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -12,6 +12,7 @@
 class TabButton extends Component {
   static propTpyes = {
     BData: PropTypes.any,             // 涓昏〃鏁版嵁
+    BID: PropTypes.any,
     MenuID: PropTypes.string,         // 鑿滃崟ID
     btn: PropTypes.object,            // 鎸夐挳
     selectedData: PropTypes.any,      // 瀛愯〃涓�夋嫨鏁版嵁
@@ -108,7 +109,7 @@
    * @description 瑙﹀彂鎸夐挳鎿嶄綔
    */
   actionTrigger = (triggerId, record, type) => {
-    const { btn, selectedData } = this.props
+    const { btn, selectedData, BID } = this.props
     const { disabled } = this.state
 
     if (disabled) return
@@ -134,8 +135,8 @@
 
     if (btn.Ot === 'requiredSgl') {
       primaryId = data[0].$$uuid || ''
-    } else if (btn.Ot === 'notRequired' && data[0]) {
-      primaryId = data[0].$$BID || ''
+    } else if (btn.Ot === 'notRequired' && BID) {
+      primaryId = BID
     }
 
     let newtab = {}
@@ -153,8 +154,8 @@
           primaryId: primaryId
         }
       }
-    } else if (btn.MenuID || btn.linkmenu) {
-      let menuId = btn.MenuID || btn.linkmenu.slice(-1)[0]
+    } else if (btn.linkmenu && btn.linkmenu.length > 0) {
+      let menuId = btn.linkmenu.slice(-1)[0]
       let menu = window.GLOB.mkThdMenus.filter(m => m.MenuID === menuId)[0]
 
       if (!menu && btn.MenuName && btn.tabType) {
diff --git a/src/tabviews/zshare/cardcomponent/asyncButtonComponent.jsx b/src/tabviews/zshare/cardcomponent/asyncButtonComponent.jsx
index 8b21804..6c66591 100644
--- a/src/tabviews/zshare/cardcomponent/asyncButtonComponent.jsx
+++ b/src/tabviews/zshare/cardcomponent/asyncButtonComponent.jsx
@@ -21,6 +21,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
       const btn = this.props.btn || {}
diff --git a/src/tabviews/zshare/cardcomponent/index.jsx b/src/tabviews/zshare/cardcomponent/index.jsx
index 5fd8517..3bcfd1a 100644
--- a/src/tabviews/zshare/cardcomponent/index.jsx
+++ b/src/tabviews/zshare/cardcomponent/index.jsx
@@ -146,7 +146,7 @@
    * @description 鑾峰彇鎸夐挳鍏冪礌
    */
   getActionList = (actions) => {
-    const { BData, setting, columns, ContainerId, data, MenuID } = this.props
+    const { BData, setting, columns, data, MenuID } = this.props
     
     return actions.map(item => {
       if (['exec', 'prompt', 'pop'].includes(item.OpenType)) {
@@ -159,7 +159,6 @@
             setting={setting}
             columns={columns}
             selectedData={[data]}
-            ContainerId={ContainerId}
           />
         )
       } else if (item.OpenType === 'popview') {
@@ -210,7 +209,6 @@
               BData={BData}
               setting={setting}
               selectedData={[data]}
-              ContainerId={ContainerId}
             />
           )
         }
@@ -497,7 +495,6 @@
               setting={this.props.setting}
               columns={this.props.columns}
               selectedData={[]}
-              ContainerId={this.props.ContainerId}
             />
           </div>
         </Card> : null}
@@ -513,7 +510,6 @@
     MenuID: PropTypes.string,         // 鑿滃崟ID
     config: PropTypes.object,         // 椤甸潰閰嶇疆淇℃伅
     columns: PropTypes.array,         // 鏄剧ず鍒�
-    ContainerId: PropTypes.any,       // tab椤甸潰ID锛岀敤浜庡脊绐楁帶鍒�
     plot: PropTypes.object,
     tableId: PropTypes.string,
     loading: PropTypes.bool,
@@ -745,11 +741,11 @@
 
     this.setState({selectKey: data.key})
 
-    MKEmitter.emit('changeTableLine', this.props.ContainerId, this.props.tableId, _id, data)
+    MKEmitter.emit('changeTableLine', config.setting.ContainerId, this.props.tableId, _id, data)
   }
 
   render() {
-    const { plot, data, loading, BID, BData, MenuID, config, columns, ContainerId } = this.props
+    const { plot, data, loading, BID, BData, MenuID, config, columns } = this.props
     const { card, colMap, selectKey, actionList } = this.state
     
     return (
@@ -800,7 +796,6 @@
               columns={columns}
               selectKey={selectKey}
               setting={config.setting}
-              ContainerId={ContainerId}
               switchCard={this.switchCard}
             />
           ))
@@ -815,7 +810,6 @@
             BData={BData}
             MenuID={MenuID}
             setting={config.setting}
-            ContainerId={ContainerId}
             switchCard={() => {}}
           /> : null
         }
diff --git a/src/tabviews/zshare/chartcomponent/asyncButtonComponent.jsx b/src/tabviews/zshare/chartcomponent/asyncButtonComponent.jsx
index 5fb9c1a..f596832 100644
--- a/src/tabviews/zshare/chartcomponent/asyncButtonComponent.jsx
+++ b/src/tabviews/zshare/chartcomponent/asyncButtonComponent.jsx
@@ -21,7 +21,12 @@
       this.setState({component})
     }
 
-    // <Button className="loading-skeleton" disabled={true}></Button> // 楠ㄦ灦鎸夐挳
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+    
     render() {
       const C = this.state.component
       const btn = this.props.btn || {}
diff --git a/src/tabviews/zshare/mutilform/mkColor/index.jsx b/src/tabviews/zshare/mutilform/mkColor/index.jsx
index d783e56..562708b 100644
--- a/src/tabviews/zshare/mutilform/mkColor/index.jsx
+++ b/src/tabviews/zshare/mutilform/mkColor/index.jsx
@@ -29,9 +29,17 @@
   handleChange = (color) => {
     let _color = `rgba(${ color.rgb.r }, ${ color.rgb.g }, ${ color.rgb.b }, ${ color.rgb.a })`
 
-    this.setState({ color: _color }, () => {
-      this.props.onChange(_color)
-    })
+    if (!this.props.config.isHex) {
+      this.setState({ color: _color }, () => {
+        this.props.onChange(_color)
+      })
+    } else {
+      let _hex = color.hex === 'transparent' ? '#ffffff' : color.hex
+
+      this.setState({ color: _hex }, () => {
+        this.props.onChange(_hex)
+      })
+    }
   }
 
   componentWillUnmount () {
@@ -42,6 +50,7 @@
 
   render() {
     const { color } = this.state
+
     return (
       <div className="color-sketch-block">
         <Popover content={
diff --git a/src/tabviews/zshare/normalTable/index.jsx b/src/tabviews/zshare/normalTable/index.jsx
index e01df0a..1a19866 100644
--- a/src/tabviews/zshare/normalTable/index.jsx
+++ b/src/tabviews/zshare/normalTable/index.jsx
@@ -48,7 +48,6 @@
     pickup: PropTypes.any,           // 鏁版嵁鏀惰捣
     columns: PropTypes.array,        // 琛ㄦ牸鍒�
     fields: PropTypes.array,         // 缁勪欢瀛楁闆�
-    ContainerId: PropTypes.any,      // 鏍囩椤靛灞侷d
     BData: PropTypes.any,            // 涓昏〃鏁版嵁
     data: PropTypes.any,             // 琛ㄦ牸鏁版嵁
     total: PropTypes.any,            // 鎬绘暟
@@ -758,7 +757,6 @@
                   BData={this.props.BData}
                   setting={this.props.setting}
                   columns={this.props.fields || this.props.columns}
-                  ContainerId={this.props.ContainerId}
                 />
               )
             } else if (btn.OpenType === 'popview') {
@@ -780,6 +778,7 @@
                   btn={btn}
                   disabled={record.$disabled}
                   selectedData={[record]}
+                  BID={record.$$BID}
                   BData={this.props.BData}
                   MenuID={this.props.MenuID}
                 />
@@ -1120,9 +1119,9 @@
   }
 
   changedata = (index) => {
-    const { data, setting, tableId, ContainerId } = this.props
+    const { data, setting, tableId } = this.props
 
-    if (!tableId || !ContainerId) return
+    if (!tableId || !setting.ContainerId) return
 
     let _id = ''
     let _data = ''
@@ -1132,7 +1131,7 @@
       _data = data[index] || ''
     }
 
-    MKEmitter.emit('changeTableLine', ContainerId, tableId, _id, _data)
+    MKEmitter.emit('changeTableLine', setting.ContainerId, tableId, _id, _data)
   }
 
   resetTable = (id, repage) => {
diff --git a/src/tabviews/zshare/pageMessage/index.jsx b/src/tabviews/zshare/pageMessage/index.jsx
index 9c22f35..60db458 100644
--- a/src/tabviews/zshare/pageMessage/index.jsx
+++ b/src/tabviews/zshare/pageMessage/index.jsx
@@ -142,7 +142,7 @@
         })
       }
 
-      if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+      if (item.type === 'card') {
         item.subcards.forEach(card => {
           card.elements && card.elements.forEach(cell => {
             if (cell.eleType === 'button') {
@@ -157,7 +157,7 @@
             }
           })
         })
-      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+      } else if (item.type === 'table') {
         item.cols.forEach(col => {
           if (col.type !== 'action') return
           col.elements && col.elements.forEach(cell => {
diff --git a/src/tabviews/zshare/settingcomponent/index.jsx b/src/tabviews/zshare/settingcomponent/index.jsx
index ce4540b..2f137ee 100644
--- a/src/tabviews/zshare/settingcomponent/index.jsx
+++ b/src/tabviews/zshare/settingcomponent/index.jsx
@@ -41,7 +41,7 @@
 
     if (config.Template === 'CommonTable' || config.Template === 'TreePage') {
       this.getPageConfig()
-    } else if (config.Template === 'CustomPage') {
+    } else if (config.Template === 'CustomPage' || config.Template === 'BaseTable') {
       this.getCustomPageConfig()
     } else {
       notification.warning({
@@ -82,7 +82,7 @@
           if (cell.OpenType === 'funcbutton' && cell.funcType === 'print') {
 
             cell.$port = cell.verify ? cell.verify.linkUrl : ''
-            if (cell.verify && cell.verify.printerTypeList) {
+            if (cell.verify && cell.verify.printerTypeList && cell.verify.printerTypeList.length > 0) {
               cell.verify.printerTypeList = cell.verify.printerTypeList.map(_cell => {
                 _cell.uuid = _cell.uuid || _cell.key
                 _cell.parentId = cell.uuid
@@ -98,7 +98,7 @@
           _comp.action.push({...cell, ...(userConfig[cell.uuid] || {})})
         })
         
-        if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+        if (item.type === 'card') {
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
               if (cell.eleType !== 'button') return
@@ -106,7 +106,7 @@
               if (cell.OpenType === 'funcbutton' && cell.funcType === 'print') {
 
                 cell.$port = cell.verify ? cell.verify.linkUrl : ''
-                if (cell.verify && cell.verify.printerTypeList) {
+                if (cell.verify && cell.verify.printerTypeList && cell.verify.printerTypeList.length > 0) {
                   cell.verify.printerTypeList = cell.verify.printerTypeList.map(_cell => {
                     _cell.uuid = _cell.uuid || _cell.key
                     _cell.parentId = cell.uuid
@@ -119,7 +119,7 @@
                 printbtns.push(cell)
               }
 
-              _comp.action.push({...cell, ...(userConfig[cell.uuid] || {})})
+              _comp.action.push({...cell, $line: true, ...(userConfig[cell.uuid] || {})})
             })
             card.backElements && card.backElements.forEach(cell => {
               if (cell.eleType !== 'button') return
@@ -127,7 +127,7 @@
               if (cell.OpenType === 'funcbutton' && cell.funcType === 'print') {
 
                 cell.$port = cell.verify ? cell.verify.linkUrl : ''
-                if (cell.verify && cell.verify.printerTypeList) {
+                if (cell.verify && cell.verify.printerTypeList && cell.verify.printerTypeList.length > 0) {
                   cell.verify.printerTypeList = cell.verify.printerTypeList.map(_cell => {
                     _cell.uuid = _cell.uuid || _cell.key
                     _cell.parentId = cell.uuid
@@ -140,10 +140,10 @@
                 printbtns.push(cell)
               }
 
-              _comp.action.push({...cell, ...(userConfig[cell.uuid] || {})})
+              _comp.action.push({...cell, $line: true, ...(userConfig[cell.uuid] || {})})
             })
           })
-        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        } else if (item.type === 'table') {
           item.cols && item.cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements && col.elements.forEach(cell => {
@@ -151,7 +151,7 @@
               if (cell.OpenType === 'funcbutton' && cell.funcType === 'print') {
 
                 cell.$port = cell.verify ? cell.verify.linkUrl : ''
-                if (cell.verify && cell.verify.printerTypeList) {
+                if (cell.verify && cell.verify.printerTypeList && cell.verify.printerTypeList.length > 0) {
                   cell.verify.printerTypeList = cell.verify.printerTypeList.map(_cell => {
                     _cell.uuid = _cell.uuid || _cell.key
                     _cell.parentId = cell.uuid
@@ -164,7 +164,7 @@
                 printbtns.push(cell)
               }
 
-              _comp.action.push({...cell, ...(userConfig[cell.uuid] || {})})
+              _comp.action.push({...cell, $line: true, ...(userConfig[cell.uuid] || {})})
             })
           })
         } 
@@ -269,7 +269,7 @@
 
               item.printer = _item ? (_item.printer || '') : ''
               item.$port = item.verify ? item.verify.linkUrl : ''
-              if (item.verify && item.verify.printerTypeList) {
+              if (item.verify && item.verify.printerTypeList && item.verify.printerTypeList.length > 0) {
                 item.verify.printerTypeList = item.verify.printerTypeList.map(cell => {
                   cell.uuid = cell.uuid || cell.key
                   cell.parentId = item.uuid
@@ -424,7 +424,7 @@
 
     components.forEach(com => {
       com.action.forEach(item => {
-        if (item.shortcut && item.shortcut.length > 0) {
+        if (item.shortcut && item.shortcut.length > 0 && !item.$line) {
           _LongParam.action.push({uuid: item.uuid, parentId: com.uuid, shortcut: item.shortcut, $shortcut: item.shortcut.join('+')})
         }
         if (item.funcType === 'print' && (item.printer || item.verify.printerTypeList)) {
@@ -531,6 +531,7 @@
           ]}
           destroyOnClose
         >
+          <div className="tip">娉細琛岀骇鎸夐挳蹇嵎閿缃棤鏁堛��</div>
           {components && components.length > 0 ? components.map(item => (
             <div key={item.uuid}>
               <p className="component-title">{item.title}</p>
diff --git a/src/tabviews/zshare/settingcomponent/index.scss b/src/tabviews/zshare/settingcomponent/index.scss
index 34e6780..b19cb86 100644
--- a/src/tabviews/zshare/settingcomponent/index.scss
+++ b/src/tabviews/zshare/settingcomponent/index.scss
@@ -29,6 +29,12 @@
       padding-top: 0px;
       position: relative;
 
+      .tip {
+        float: right;
+        margin-top: 10px;
+        color: #b6b6b6;
+      }
+
       .ant-spin {
         position: absolute;
         left: calc(50% - 22px);
diff --git a/src/tabviews/zshare/tablenodes/index.jsx b/src/tabviews/zshare/tablenodes/index.jsx
new file mode 100644
index 0000000..8a728df
--- /dev/null
+++ b/src/tabviews/zshare/tablenodes/index.jsx
@@ -0,0 +1,451 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal, Button, notification, Spin, Input, Typography } from 'antd'
+
+import Api from '@/api'
+import G6 from "@antv/g6"
+import Utils from '@/utils/utils.js'
+import options from '@/store/options.js'
+import './index.scss'
+
+const { Search } = Input
+const { Paragraph } = Typography
+
+class TableNodes extends Component {
+  static propTpyes = {
+    config: PropTypes.object
+  }
+
+  state = {
+    debug: sessionStorage.getItem('debug') === 'true',
+    visible: false,
+    loading: false,
+    nodes: null,
+    empty: false
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  getTbs = (config) => {
+    let tbs = []
+    let ptbs = []
+
+    let traversal = (components) => {
+      components.forEach(item => {
+        if (item.$tables) {
+          ptbs.push(...item.$tables)
+          item.$tables.forEach(tb => {
+            tbs.push({
+              label: item.name,
+              table: tb,
+              color: '#5AD8A6',
+              id: Utils.getuuid(),
+              direction: 'left'
+            })
+          })
+        }
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            traversal(tab.components)
+          })
+        } else if (item.type === 'group') {
+          traversal(item.components)
+        }
+      })
+    }
+
+    if (config.Template === 'BaseTable') {
+      config.components.forEach(item => {
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            if (tab.components[0].$tables) {
+              ptbs.push(...tab.components[0].$tables)
+              tab.components[0].$tables.forEach(tb => {
+                tbs.push({
+                  label: tab.label,
+                  table: tb,
+                  color: '#5AD8A6',
+                  id: Utils.getuuid(),
+                  direction: 'left'
+                })
+              })
+            }
+          })
+        } else if (item.$tables) {
+          ptbs.push(...item.$tables)
+          item.$tables.forEach(tb => {
+            tbs.push({
+              label: '涓昏〃',
+              table: tb,
+              color: '#5AD8A6',
+              id: Utils.getuuid(),
+              direction: 'left'
+            })
+          })
+        }
+      })
+    } else {
+      traversal(config.components)
+
+      if (config.interfaces) {
+        config.interfaces.forEach(item => {
+          if (item.$tables) {
+            ptbs.push(...item.$tables)
+            item.$tables.forEach(tb => {
+              tbs.push({
+                label: item.name,
+                table: tb,
+                color: '#5AD8A6',
+                id: Utils.getuuid(),
+                direction: 'left'
+              })
+            })
+          }
+        })
+      }
+    }
+
+    return {tbs, ptbs}
+  }
+
+  trigger = () => {
+    const { config } = this.props
+
+    if (!config) return
+    if (config.Template === 'CustomPage' && config.version !== 2.0) {
+      notification.warning({
+        top: 92,
+        message: '璇峰崌绾у綋鍓嶈彍鍗曪紒',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({visible: true, loading: true, empty: false}, () => {
+      let param = {
+        func: 's_get_menus_tb_list_cloud_sso',
+        TypeCharOne: '',
+        typename: '',
+        MenuID: config.uuid
+      }
+
+      Api.getSystemConfig(param).then(result => {
+        if (!result.status) {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+
+          this.setState({empty: true, loading: false})
+
+          return
+        }
+
+        let data = {
+          label: config.MenuName || '绌�',
+          id: config.uuid,
+          MenuID: config.MenuID,
+          children: []
+        }
+
+        let { tbs, ptbs } = this.getTbs(config)
+
+        ptbs = Array.from(new Set(ptbs))
+        ptbs.sort()
+        if (ptbs.length && sessionStorage.getItem('mk_tb_names')) {
+          let names = sessionStorage.getItem('mk_tb_names')
+          ptbs = ptbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1)
+        }
+
+        if (ptbs.length) {
+          ptbs.forEach((item, i) => {
+            let cell = {
+              label: item,
+              name: item.toLowerCase(),
+              id: 'par' + i,
+              direction: 'left',
+              color: '#5AD8A6',
+              children: []
+            }
+
+            tbs.forEach(t => {
+              if (t.table === item) {
+                cell.children.push(t)
+              }
+            })
+
+            data.children.push(cell)
+          })
+        }
+
+        if (result.tb_list) {
+          result.tb_list.sort((a, b) => a.tbname > b.tbname ? 1 : -1)
+          result.tb_list.forEach((item, i) => {
+            let cell = {
+              label: item.tbname,
+              name: item.tbname.toLowerCase(),
+              id: 'table' + i,
+              direction: 'right',
+              color: '#1890ff',
+              children: []
+            }
+
+            if (item[item.tbname]) {
+              item[item.tbname].forEach((m, i) => {
+                if (m.debug_url) {
+                  let _param = JSON.parse(window.decodeURIComponent(window.atob(m.debug_url)))
+                  let label = _param.MenuName
+
+                  if (_param && _param.type === 'app') {
+                    label += ` (${_param.kei_no} | ${_param.typename}${param.lang !== 'zh-CN' ? ' | ' + param.lang : ''})`
+                  }
+
+                  cell.children.push({
+                    label: label,
+                    id: item.tbname + 'menu' + i,
+                    direction: 'right',
+                    color: '#1890ff',
+                    param: _param
+                  })
+                }
+              })
+            }
+
+            data.children.push(cell)
+          })
+        }
+        
+        if (data.children.length === 0) {
+          this.setState({empty: true, loading: false})
+        } else {
+          this.setState({loading: false, nodes: data})
+          this.getForks(data)
+        }
+      })
+    })
+  }
+
+  resetNodes = (key) => {
+    let data = fromJS(this.state.nodes).toJS()
+    key = key ? key.toLowerCase() : ''
+
+    data.children.forEach(cell => {
+      cell.fontcolor = ''
+      if (!cell.name) return
+      if (key && cell.name.indexOf(key) > -1) {
+        cell.fontcolor = 'orange'
+      }
+    })
+
+    let _element = document.getElementById('mkTableNode')
+    if (_element) {
+      _element.innerHTML = ''
+    }
+
+    this.setState({}, () => {
+      this.getForks(data)
+    })
+  }
+
+  getForks = (data) => {
+    const { Util } = G6
+    
+    G6.registerNode(
+      'dice-mind-map-root', {
+        jsx: (cfg) => {
+          const width = Util.getTextSize(cfg.label, 14)[0] + 12;
+          const stroke = cfg.style.stroke || '#096dd9';
+    
+          return `
+          <group>
+            <rect draggable="true" style={{width: ${width}, height: 30, stroke: ${stroke}, radius: 4}} keyshape>
+              <text style={{ fontSize: 14, marginLeft: 6, marginTop: 6 }}>${cfg.label}</text>
+            </rect>
+          </group>
+        `;
+        },
+        getAnchorPoints() {
+          return [
+            [0, 0.5],
+            [1, 0.5],
+          ];
+        },
+      },
+      'single-node',
+    );
+
+    G6.registerNode(
+      'dice-mind-map-leaf', {
+        jsx: (cfg) => {
+          const width = Util.getTextSize(cfg.label, 12)[0] + 24;
+          const color = cfg.color;
+    
+          return `
+          <group>
+            <rect draggable="true" style={{width: ${width}, height: 26, fill: 'transparent' }}>
+              <text style={{ fontSize: 12, fill: ${cfg.fontcolor ? cfg.fontcolor : 'black'}, marginLeft: 12, marginTop: 6 }}>${cfg.label}</text>
+            </rect>
+            <rect style={{ fill: ${color}, width: ${width}, height: 2, x: 0, y: 32 }} />
+          </group>
+        `;
+        },
+        getAnchorPoints() {
+          return [
+            [0, 0.965],
+            [1, 0.965],
+          ];
+        },
+      },
+      'single-node',
+    );
+    G6.registerBehavior('scroll-canvas', {
+      getEvents: function getEvents() {
+        return {
+          wheel: 'onWheel',
+        };
+      },
+    
+      onWheel: function onWheel(ev) {
+        const {
+          graph
+        } = this;
+        if (!graph) {
+          return;
+        }
+        if (ev.ctrlKey) {
+          const canvas = graph.get('canvas');
+          const point = canvas.getPointByClient(ev.clientX, ev.clientY);
+          let ratio = graph.getZoom();
+          if (ev.wheelDelta > 0) {
+            ratio += ratio * 0.05;
+          } else {
+            ratio *= ratio * 0.05;
+          }
+          graph.zoomTo(ratio, {
+            x: point.x,
+            y: point.y,
+          });
+        } else {
+          const x = ev.deltaX || ev.movementX;
+          const y = ev.deltaY || ev.movementY || (-ev.wheelDelta * 125) / 3;
+          graph.translate(-x, -y);
+        }
+        ev.preventDefault();
+      },
+    });
+    
+    const dataTransform = (data) => {
+      const changeData = (d, level = 0, color) => {
+        const data = {
+          ...d,
+        };
+        switch (level) {
+          case 0:
+            data.type = 'dice-mind-map-root';
+            break;
+          default:
+            data.type = 'dice-mind-map-leaf';
+            break;
+        }
+    
+        data.hover = false;
+    
+        if (color) {
+          data.color = color;
+        }
+    
+        if (d.children) {
+          data.children = d.children.map((child) => changeData(child, level + 1, data.color));
+        }
+        return data;
+      };
+      return changeData(data);
+    };
+
+    const tree = new G6.TreeGraph({
+      container: 'mkTableNode',
+      width: this.wrap.offsetWidth,
+      height: this.wrap.offsetHeight,
+      fitView: true,
+      fitViewPadding: [10, 20],
+      layout: {
+        type: 'mindmap',
+        direction: 'H',
+        getHeight: () => {
+          return 16;
+        },
+        getWidth: (node) => {
+          return node.level === 0 ?
+            Util.getTextSize(node.label, 16)[0] + 12 :
+            Util.getTextSize(node.label, 12)[0];
+        },
+        getVGap: () => {
+          return 10;
+        },
+        getHGap: () => {
+          return 60;
+        },
+        getSide: (node) => {
+          return node.data.direction;
+        },
+      },
+      defaultEdge: {
+        type: 'cubic-horizontal',
+        style: {
+          lineWidth: 2,
+        },
+      },
+      minZoom: 0.5,
+      modes: {
+        default: ['drag-canvas', 'zoom-canvas'],
+      },
+    });
+    
+    tree.data(dataTransform(data));
+    
+    tree.render();
+  }
+
+  render() {
+    const { config } = this.props
+    const { visible, loading, empty, debug } = this.state
+
+    return (
+      <div className={'page-message-wrap ' + (debug && options.sysType !== 'cloud' ? 'exist' : '')}>
+        {debug && options.sysType !== 'cloud' ? <Button
+          icon="fork"
+          shape="circle"
+          className="page-message"
+          onClick={this.trigger}
+        /> : null}
+        <Modal
+          title=""
+          wrapClassName="view-table-modal"
+          visible={visible}
+          width={'90vw'}
+          closable={false}
+          maskClosable={false}
+          footer={[]}
+          destroyOnClose
+        >
+          <div className="header">琛ㄥ叧绯诲浘锛坽config && config.MenuNo ? <Paragraph copyable>{config.MenuNo}</Paragraph> : ''}锛�</div>
+          <Search className="tb-search" placeholder="璇疯緭鍏ヨ〃鍚�" onSearch={value => this.resetNodes(value)} enterButton />
+          <div className="wrap">
+            {loading ? <Spin size="large" /> : null}
+            {empty ? <div className="empty">鏈煡璇㈠埌琛ㄥ悕淇℃伅銆�</div> : null}
+            <div className="mountNode" id="mkTableNode" ref={ref => this.wrap = ref}></div>
+          </div>
+          <div className="footer">
+            <Button key="cancel" onClick={() => { this.setState({ visible: false })}}>鍏抽棴</Button>
+          </div>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default TableNodes
\ No newline at end of file
diff --git a/src/tabviews/zshare/tablenodes/index.scss b/src/tabviews/zshare/tablenodes/index.scss
new file mode 100644
index 0000000..e320023
--- /dev/null
+++ b/src/tabviews/zshare/tablenodes/index.scss
@@ -0,0 +1,74 @@
+.page-message-wrap {
+  .page-message {
+    position: fixed;
+    z-index: 2;
+    bottom: 55px;
+    right: 20px;
+    width: 40px;
+    height: 40px;
+  }
+}
+
+.view-table-modal {
+  .ant-modal {
+    top: 30px;
+  }
+  .ant-modal-body {
+    padding: 20px 50px 20px;
+    user-select: none;
+  }
+  .ant-modal-footer {
+    display: none;
+  }
+  .wrap {
+    position: relative;
+    height: calc(100vh - 175px);
+    margin: 10px 0px;
+    overflow: hidden;
+
+    .mountNode {
+      height: 100%;
+    }
+
+    .ant-spin {
+      position: absolute;
+      top: calc(50% - 16px);
+      left: calc(50% - 16px);
+    }
+    .empty {
+      position: relative;
+      text-align: center;
+      color: #959595;
+      padding-top: 200px;
+    }
+    .empty + .mountNode {
+      opacity: 0;
+    }
+  }
+  .header {
+    font-weight: 500;
+    text-align: center;
+    font-size: 18px;
+    white-space: nowrap;
+
+    .ant-typography {
+      margin-bottom: 0px;
+      color: #1890ff;
+      display: inline-block;
+      .ant-typography-copy {
+        font-size: 14px;
+      }
+    }
+  }
+
+  .footer {
+    text-align: center;
+  }
+  .tb-search {
+    position: absolute;
+    width: 200px;
+    z-index: 1;
+    right: 40px;
+    top: 16px;
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/zshare/topSearch/index.jsx b/src/tabviews/zshare/topSearch/index.jsx
index 857e9f1..86f124f 100644
--- a/src/tabviews/zshare/topSearch/index.jsx
+++ b/src/tabviews/zshare/topSearch/index.jsx
@@ -81,7 +81,7 @@
       if (config.type === 'search') {
         _setting.float = config.wrap.float || 'left'
         _setting.style = config.style
-      } else if (config.type === 'table' && (config.subtype === 'normaltable' || config.subtype === 'editable')) {
+      } else if (config.type === 'table') {
         _setting.float = 'left'
       } else {
         _setting.float = 'right'
diff --git a/src/templates/calendarconfig/index.jsx b/src/templates/calendarconfig/index.jsx
index 72e72cb..2284223 100644
--- a/src/templates/calendarconfig/index.jsx
+++ b/src/templates/calendarconfig/index.jsx
@@ -52,7 +52,6 @@
     activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
     openEdition: '',         // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
     mockdata: [],            // 娴嬭瘯鏁版嵁
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
   }
 
   /**
@@ -126,10 +125,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -142,7 +154,6 @@
         return false
       }
     }
-    MKEmitter.addListener('modalStatus', this.modalStatus)
   }
 
   getMockData = (year) => {
@@ -245,11 +256,6 @@
       return
     }
     document.onkeydown = () => {}
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   // 椤甸潰杩斿洖
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index 6717638..09683e2 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -28,6 +28,7 @@
 const Versions = asyncComponent(() => import('@/menu/versions'))
 const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
 const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
+const UpdateTable = asyncComponent(() => import('./updatetable'))
 const Unattended = asyncComponent(() => import('@/templates/zshare/unattended'))
 const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
 const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/settingcomponent'))
@@ -61,7 +62,6 @@
     activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
     chartview: null,         // 褰撳墠瑙嗗浘
     openEdition: '',         // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
   }
 
   /**
@@ -163,10 +163,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -179,8 +192,6 @@
         return false
       }
     }
-
-    MKEmitter.addListener('modalStatus', this.modalStatus)
   }
 
   /**
@@ -191,11 +202,6 @@
       return
     }
     document.onkeydown = () => {}
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   /**
@@ -1262,6 +1268,7 @@
                 <Versions MenuId={menu.MenuID} open_edition={openEdition} updateConfig={this.refreshConfig}/>
                 <ReplaceField type="table" config={config} updateConfig={this.updateconfig}/>
                 <EditComponent type="table" options={['search', 'form', 'action', 'columns']} config={this.state.config} MenuID={this.props.menu.MenuID} thawButtons={this.state.thawButtons} refresh={this.editConfig}/>
+                <UpdateTable config={config}/>
                 <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={this.state.config.enabled} onChange={this.onEnabledChange} />
                 <Button type="primary" id="save-config" onClick={this.submitConfig} loading={this.state.menuloading}>淇濆瓨</Button>
                 <Button onClick={this.cancelConfig}>鍏抽棴</Button>
diff --git a/src/templates/comtableconfig/updatetable/index.jsx b/src/templates/comtableconfig/updatetable/index.jsx
new file mode 100644
index 0000000..d947713
--- /dev/null
+++ b/src/templates/comtableconfig/updatetable/index.jsx
@@ -0,0 +1,1123 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal, Button, notification } from 'antd'
+import { ArrowUpOutlined } from '@ant-design/icons'
+import moment from 'moment'
+import md5 from 'md5'
+
+import { colorTransform } from '@/utils/option.js'
+import { getTables } from '@/utils/utils-custom.js'
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+// import './index.scss'
+
+const { confirm } = Modal
+
+class UpdateTable extends Component {
+  static propTpyes = {
+    config: PropTypes.object
+  }
+
+  state = {}
+  delButtons = []
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  trigger = () => {
+    const { config } = this.props
+    const _this = this
+
+    if (!config.enabled) {
+      notification.warning({
+        top: 92,
+        message: '鏈惎鐢ㄨ彍鍗曚笉鍙崌绾э紒',
+        duration: 5
+      })
+      return
+    }
+    
+    confirm({
+      title: '纭畾鍗囩骇褰撳墠鑿滃崟鍚楋紵',
+      content: '',
+      onOk() {
+        return new Promise(resolve => {
+          _this.execUpdate(resolve)
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  execUpdate = (_resolve) => {
+    const { config } = this.props
+    this.delButtons = []
+
+    let _config = {
+      version: 1.0,
+      uuid: config.uuid,
+      MenuID: config.uuid,
+      fstMenuId: config.fstMenuId,
+      parentId: config.ParentId,
+      Template: 'BaseTable',
+      easyCode: config.easyCode,
+      enabled: false,
+      MenuName: config.MenuName,
+      MenuNo: config.MenuNo,
+      OpenType: 'newtab',
+      tables: config.tables || [],
+      urlFields: config.urlFields || [],
+      hidden: config.hidden || 'false',
+      autoMatic: {},
+      Remark: config.Remark || '',
+      components: [],
+      viewType: 'menu',
+      style: { backgroundColor: '#ffffff', paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'},
+    }
+
+    let formActions = []
+    let popActions = []
+    let errors = []
+    let mainTb = {name: '涓昏〃', uuid: Utils.getuuid(), useMSearch: 'false', isMain: true}
+
+    let oldtabs = {
+      mainTable: mainTb.uuid
+    }
+
+    let tbl = this.getTable(config, mainTb, errors, formActions, popActions, oldtabs)
+
+    if (config.autoMatic && config.autoMatic.enable === 'true') {
+      if (tbl.action.filter(item => item.uuid === config.autoMatic.action && (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print'))).length === 0) {
+        _config.autoMatic = {enable: 'false'}
+      } else {
+        _config.autoMatic = {...config.autoMatic}
+      }
+    } else {
+      _config.autoMatic = {enable: 'false'}
+    }
+
+    _config.components.push(tbl)
+
+    let _tbs = []
+    if (config.tabgroups && config.tabgroups.length > 0) {
+      config.tabgroups.forEach(m => {
+        if (!m.sublist || m.sublist.length === 0) return
+
+        let tabs = {
+          uuid: Utils.getuuid(),
+          type: 'tabs',
+          subtype: 'tabletabs',
+          setting: {},
+          style: {},
+          subtabs: m.sublist.map(n => {
+            let tab = { uuid: Utils.getuuid(), label: n.label, icon: n.icon, permission: 'false', components: [
+              {
+                uuid: Utils.getuuid(),
+                linkTab: n.linkTab,
+                name: n.label,
+                useMSearch: n.searchPass === 'true' ? 'true' : 'false',
+                supModule: n.supMenu || '',
+                equalTab: n.equalTab && n.equalTab.length > 0 ? n.equalTab[0] : ''
+              }
+            ]}
+            this.delButtons.push(n.linkTab)
+            oldtabs[n.uuid] = tab.components[0].uuid
+
+            _tbs.push(tab.components[0])
+
+            return tab
+          })
+        }
+        
+        _config.components.push(tabs)
+      })
+    }
+
+    if (_tbs.length > 0) {
+      let defers = _tbs.map((item, i) => {
+        return new Promise((resolve) => {
+          setTimeout(() => {
+            Api.getSystemConfig({
+              func: 'sPC_Get_LongParam',
+              MenuID: item.linkTab
+            }).then(res => {
+              if (res.status) {
+                let _LongParam = ''
+                if (res.LongParam) {
+                  try {
+                    _LongParam = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
+                  } catch (e) {
+                    console.warn('Parse Failure')
+                    _LongParam = ''
+                  }
+                }
+  
+                if (_LongParam) {
+                  _LongParam.uuid = item.linkTab
+                  resolve(_LongParam)
+                } else {
+                  resolve({uuid: item.linkTab, $empty: true})
+                }
+              } else {
+                notification.warning({
+                  top: 92,
+                  message: res.message,
+                  duration: 5
+                })
+                resolve({uuid: item.linkTab, $empty: true})
+              }
+            })
+          }, i * 50)
+        })
+      })
+      Promise.all(defers).then(res => {
+        let menus = {}
+        res.forEach(item => {
+          if (item && !item.$empty) {
+            menus[item.uuid] = item
+          }
+        })
+
+        _config.components = _config.components.map(item => {
+          if (item.type === 'tabs') {
+            item.subtabs = item.subtabs.map(tab => {
+              tab.components[0] = this.getTable(menus[tab.components[0].linkTab], tab.components[0], errors, formActions, popActions, oldtabs)
+
+              return tab
+            })
+          }
+          return item
+        })
+
+        this.setPopView(_resolve, _config, formActions, popActions, errors)
+      })
+    } else {
+      this.setPopView(_resolve, _config, formActions, popActions, errors)
+    }
+  }
+
+  setPopView = (_resolve, _config, formActions, popActions, errors) => {
+    if (popActions.length > 0) {
+      let defers = popActions.map((item, i) => {
+        return new Promise((resolve) => {
+          setTimeout(() => {
+            Api.getSystemConfig({
+              func: 'sPC_Get_LongParam',
+              MenuID: item.linkTab
+            }).then(res => {
+              if (res.status) {
+                let _LongParam = ''
+                if (res.LongParam) {
+                  try {
+                    _LongParam = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
+                  } catch (e) {
+                    console.warn('Parse Failure')
+                    _LongParam = ''
+                  }
+                }
+  
+                if (_LongParam) {
+                  _LongParam.uuid = item.uuid
+                  resolve(_LongParam)
+                } else {
+                  resolve({uuid: item.uuid, $empty: true})
+                }
+              } else {
+                notification.warning({
+                  top: 92,
+                  message: res.message,
+                  duration: 5
+                })
+                resolve({uuid: item.uuid, $empty: true})
+              }
+            })
+          }, i * 50)
+        })
+      })
+      Promise.all(defers).then(res => {
+        let menus = {}
+        res.forEach(item => {
+          if (item && !item.$empty) {
+            menus[item.uuid] = item
+          }
+        })
+
+        _config.components.forEach(item => {
+          if (item.type === 'tabs') {
+            item.subtabs.forEach(tab => {
+              tab.components[0].action.forEach(btn => {
+                if (btn.OpenType === 'popview') {
+                  if (menus[btn.uuid]) {
+                    let mainTb = {name: '涓昏〃', uuid: Utils.getuuid(), useMSearch: 'false'}
+
+                    btn.config = {
+                      uuid: btn.uuid,
+                      MenuID: btn.uuid,
+                      ParentId: tab.components[0].uuid,
+                      enabled: false,
+                      MenuName: btn.label,
+                      tables: _config.tables || [],
+                      Template: 'BaseTable',
+                      components: [this.getTable(menus[btn.uuid], mainTb, errors, formActions)],
+                      viewType: 'popview',
+                      style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+                    }
+                  } else {
+                    errors.push(tab.label + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                  }
+                }
+              })
+              tab.components[0].cols.forEach(col => {
+                if (col.type !== 'action') return
+                col.elements.forEach(btn => {
+                  if (btn.OpenType === 'popview') {
+                    if (menus[btn.uuid]) {
+                      let mainTb = {name: '涓昏〃', uuid: Utils.getuuid(), useMSearch: 'false'}
+  
+                      btn.config = {
+                        uuid: btn.uuid,
+                        MenuID: btn.uuid,
+                        ParentId: tab.components[0].uuid,
+                        enabled: false,
+                        MenuName: btn.label,
+                        tables: _config.tables || [],
+                        Template: 'BaseTable',
+                        components: [this.getTable(menus[btn.uuid], mainTb, errors, formActions)],
+                        viewType: 'popview',
+                        style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+                      }
+                    } else {
+                      errors.push(tab.label + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                    }
+                  }
+                })
+              })
+            })
+          } else {
+            item.action.forEach(btn => {
+              if (btn.OpenType === 'popview') {
+                if (menus[btn.uuid]) {
+                  let mainTb = {name: '涓昏〃', uuid: Utils.getuuid(), useMSearch: 'false'}
+
+                  btn.config = {
+                    uuid: btn.uuid,
+                    MenuID: btn.uuid,
+                    ParentId: item.uuid,
+                    enabled: false,
+                    MenuName: btn.label,
+                    tables: _config.tables || [],
+                    Template: 'BaseTable',
+                    components: [this.getTable(menus[btn.uuid], mainTb, errors, formActions)],
+                    viewType: 'popview',
+                    style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+                  }
+                } else {
+                  errors.push('涓昏〃涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                }
+              }
+            })
+            item.cols.forEach(col => {
+              if (col.type !== 'action') return
+              col.elements.forEach(btn => {
+                if (btn.OpenType === 'popview') {
+                  if (menus[btn.uuid]) {
+                    let mainTb = {name: '涓昏〃', uuid: Utils.getuuid(), useMSearch: 'false'}
+  
+                    btn.config = {
+                      uuid: btn.uuid,
+                      MenuID: btn.uuid,
+                      ParentId: item.uuid,
+                      enabled: false,
+                      MenuName: btn.label,
+                      tables: _config.tables || [],
+                      Template: 'BaseTable',
+                      components: [this.getTable(menus[btn.uuid], mainTb, errors, formActions)],
+                      viewType: 'popview',
+                      style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+                    }
+                  } else {
+                    errors.push('涓昏〃涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                  }
+                }
+              })
+            })
+          }
+        })
+
+        this.setPopForm(_resolve, _config, formActions, errors)
+      })
+    } else {
+      this.setPopForm(_resolve, _config, formActions, errors)
+    }
+  }
+
+  setPopForm = (_resolve, _config, formActions, errors) => {
+    if (formActions.length > 0) {
+      let defers = formActions.map((item, i) => {
+        return new Promise((resolve) => {
+          setTimeout(() => {
+            Api.getSystemConfig({
+              func: 'sPC_Get_LongParam',
+              MenuID: item.origin
+            }).then(res => {
+              if (res.status) {
+                let _LongParam = ''
+                if (res.LongParam) {
+                  try {
+                    _LongParam = JSON.parse(window.decodeURIComponent(window.atob(res.LongParam)))
+                  } catch (e) {
+                    console.warn('Parse Failure')
+                    _LongParam = ''
+                  }
+                }
+  
+                if (_LongParam) {
+                  _LongParam.uuid = item.uuid
+                  resolve(_LongParam)
+                } else {
+                  resolve({uuid: item.uuid, $empty: true})
+                }
+              } else {
+                notification.warning({
+                  top: 92,
+                  message: res.message,
+                  duration: 5
+                })
+                resolve({uuid: item.uuid, $empty: true})
+              }
+            })
+          }, i * 50)
+        })
+      })
+      Promise.all(defers).then(res => {
+        let menus = {}
+        res.forEach(item => {
+          if (item && !item.$empty) {
+            menus[item.uuid] = item
+          }
+        })
+
+        _config.components.forEach(item => {
+          if (item.type === 'tabs') {
+            item.subtabs.forEach(tab => {
+              tab.components[0].action.forEach(btn => {
+                if (btn.OpenType === 'popview' && btn.config && btn.config.components[0]) {
+                  this.setTbForm(btn.config.components[0], menus, errors, tab.components[0].name + '-' + btn.label)
+                } else if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+                  if (menus[btn.uuid]) {
+                    btn.modal = {
+                      setting: menus[btn.uuid].setting,
+                      tables: [],
+                      groups: [],
+                      fields: menus[btn.uuid].fields || []
+                    }
+                  } else {
+                    errors.push(tab.label + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                  }
+                }
+              })
+              tab.components[0].cols.forEach(col => {
+                if (col.type !== 'action') return
+                col.elements.forEach(btn => {
+                  if (btn.OpenType === 'popview' && btn.config && btn.config.components[0]) {
+                    this.setTbForm(btn.config.components[0], menus, errors, tab.components[0].name + '-' + btn.label)
+                  } else if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+                    if (menus[btn.uuid]) {
+                      btn.modal = {
+                        setting: menus[btn.uuid].setting,
+                        tables: [],
+                        groups: [],
+                        fields: menus[btn.uuid].fields || []
+                      }
+                    } else {
+                      errors.push(tab.label + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                    }
+                  }
+                })
+              })
+            })
+          } else {
+            item.action.forEach(btn => {
+              if (btn.OpenType === 'popview' && btn.config && btn.config.components[0]) {
+                this.setTbForm(btn.config.components[0], menus, errors, item.name + '-' + btn.label)
+              } else if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+                if (menus[btn.uuid]) {
+                  btn.modal = {
+                    setting: menus[btn.uuid].setting,
+                    tables: [],
+                    groups: [],
+                    fields: menus[btn.uuid].fields || []
+                  }
+                } else {
+                  errors.push('涓昏〃涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                }
+              }
+            })
+            item.cols.forEach(col => {
+              if (col.type !== 'action') return
+              col.elements.forEach(btn => {
+                if (btn.OpenType === 'popview' && btn.config && btn.config.components[0]) {
+                  this.setTbForm(btn.config.components[0], menus, errors, item.name + '-' + btn.label)
+                } else if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+                  if (menus[btn.uuid]) {
+                    btn.modal = {
+                      setting: menus[btn.uuid].setting,
+                      tables: [],
+                      groups: [],
+                      fields: menus[btn.uuid].fields || []
+                    }
+                  } else {
+                    errors.push('涓昏〃涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+                  }
+                }
+              })
+            })
+          }
+        })
+
+        this.saveConfig(_resolve, _config, errors)
+      })
+    } else {
+      this.saveConfig(_resolve, _config, errors)
+    }
+  }
+
+  setTbForm = (item, menus, errors, name) => {
+    item.action.forEach(btn => {
+      if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+        if (menus[btn.uuid]) {
+          btn.modal = {
+            setting: menus[btn.uuid].setting,
+            tables: [],
+            groups: [],
+            fields: menus[btn.uuid].fields || []
+          }
+        } else {
+          errors.push(name + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+        }
+      }
+    })
+    item.cols.forEach(col => {
+      if (col.type !== 'action') return
+      col.elements.forEach(btn => {
+        if (btn.OpenType === 'pop' || (btn.OpenType === 'funcbutton' && btn.funcType === 'print' && btn.execMode === 'pop')) {
+          if (menus[btn.uuid]) {
+            btn.modal = {
+              setting: menus[btn.uuid].setting,
+              tables: [],
+              groups: [],
+              fields: menus[btn.uuid].fields || []
+            }
+          } else {
+            errors.push(name + '涓寜閽��' + btn.label + '銆嬮厤缃俊鎭涪澶�')
+          }
+        }
+      })
+    })
+  }
+
+  saveConfig = (_resolve, _config, errors) => {
+    let err = errors.join('锛�')
+    let _this = this
+
+    if (err) {
+      _resolve()
+      confirm({
+        title: err + '锛岀‘瀹氬崌绾у綋鍓嶈彍鍗曞悧锛�',
+        content: '',
+        onOk() {
+          return new Promise(resolve => {
+            _this.saveNewMenu(resolve, _config)
+          })
+        },
+        onCancel() {}
+      })
+    } else {
+      this.saveNewMenu(_resolve, _config)
+    }
+  }
+
+  saveNewMenu = (_resolve, _config) => {
+    _config.components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components[0].action.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.config) {
+              btn.config.$tables = getTables(btn.config.components[0])
+            }
+          })
+          tab.components[0].cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              if (btn.OpenType === 'popview' && btn.config) {
+                btn.config.$tables = getTables(btn.config.components[0])
+              }
+            })
+          })
+
+          tab.components[0].$tables = getTables(tab.components[0])
+        })
+      } else {
+        item.action.forEach(btn => {
+          if (btn.OpenType === 'popview' && btn.config) {
+            btn.config.$tables = getTables(btn.config.components[0])
+          }
+        })
+        item.cols.forEach(col => {
+          if (col.type !== 'action') return
+          col.elements.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.config) {
+              btn.config.$tables = getTables(btn.config.components[0])
+            }
+          })
+        })
+
+        item.$tables = getTables(item)
+      }
+    })
+
+    let tbs = []
+    _config.components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          if (tab.components[0].$tables) {
+            tbs.push(...tab.components[0].$tables)
+          }
+
+          tab.components[0].errors = []
+          let columns = tab.components[0].columns.map(c => c.field)
+          if (tab.components[0].setting.interType === 'system' && tab.components[0].setting.execute !== 'false' && !tab.components[0].setting.dataresource) {
+            tab.components[0].errors.push({ level: 0, detail: '鏈缃暟鎹簮锛�'})
+          } else if (tab.components[0].setting.interType === 'system' && tab.components[0].setting.execute === 'false' && tab.components[0].scripts.filter(script => script.status !== 'false').length === 0) {
+            tab.components[0].errors.push({ level: 0, detail: '鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+          } else if (!tab.components[0].setting.primaryKey) {
+            tab.components[0].errors.push({ level: 0, detail: '鏈缃富閿紒'})
+          } else if (!columns.includes(tab.components[0].setting.primaryKey)) {
+            tab.components[0].errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
+          } else if (!tab.components[0].setting.supModule) {
+            tab.components[0].errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+          }
+        })
+      } else {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+
+        item.errors = []
+        let columns = item.columns.map(c => c.field)
+        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
+          item.errors.push({ level: 0, detail: '鏈缃暟鎹簮锛�'})
+        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.filter(script => script.status !== 'false').length === 0) {
+          item.errors.push({ level: 0, detail: '鏁版嵁婧愪腑鏃犲彲鐢ㄨ剼鏈紒'})
+        } else if (!item.setting.primaryKey) {
+          item.errors.push({ level: 0, detail: '鏈缃富閿紒'})
+        } else if (!columns.includes(item.setting.primaryKey)) {
+          item.errors.push({ level: 0, detail: '涓婚敭宸插け鏁堬紒'})
+        } else if (!item.setting.supModule) {
+          item.errors.push({ level: 0, detail: '鏈缃笂绾х粍浠讹紒'})
+        }
+      }
+    })
+
+    this.submitConfig(_resolve, _config, tbs)
+  }
+
+  submitConfig = (_resolve, config, tbs) => {
+    let arr = []
+    tbs = tbs.filter(tb => {
+      let _tb = tb.toLowerCase()
+
+      if (arr.includes(_tb)) return false
+      arr.push(_tb)
+
+      return true
+    })
+    tbs.sort()
+    if (tbs.length && sessionStorage.getItem('mk_tb_names')) {
+      let names = sessionStorage.getItem('mk_tb_names')
+      tbs = tbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1)
+    }
+    tbs = tbs.map(tb => `'${tb}'`).join(';')
+
+    let key = md5(config.uuid + tbs.toLowerCase())
+
+    let urlparam = {
+      FstId: config.fstMenuId,
+      ParentId: config.parentId,
+      MenuID: config.uuid,
+      MenuName: config.MenuName,
+      MenuNo: config.MenuNo,
+      EasyCode: config.easyCode,
+      type: 'admin',
+      OpenType: config.OpenType,
+      PageParam: {Template: 'BaseTable', OpenType: 'newtab', hidden: config.hidden},
+      MenuType: 'BaseTable'
+    }
+    let url = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+    config.tbkey = key
+
+    let param = {
+      func: 'sPC_TrdMenu_AddUpt',
+      FstID: config.fstMenuId || '',
+      SndID: config.parentId,
+      ParentID: config.parentId,
+      MenuID: config.uuid,
+      MenuNo: config.MenuNo || '',
+      EasyCode: config.easyCode || '',
+      Template: 'BaseTable',
+      MenuName: config.MenuName || '',
+      PageParam: JSON.stringify({Template: 'BaseTable', OpenType: config.OpenType, hidden: config.hidden}),
+      open_edition: '',
+      debug_md5: key,
+      debug_url: url,
+      debug_list: window.btoa(tbs),
+      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config)))
+    }
+
+    param.LText = Utils.formatOptions(param.LText)
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+
+    let save = false
+
+    new Promise(resolve => {
+      Api.getSystemConfig(param).then(res => {
+        resolve(res)
+      })
+    }).then(res => { // 鍒犻櫎鍘熻彍鍗�
+      if (!res || !res.status) return res
+
+      save = true
+      localStorage.setItem('menuUpdate', new Date().getTime() + ',' + config.uuid)
+
+      if (this.delButtons.length === 0) return res
+
+      return Api.getSystemConfig({
+        func: 'sPC_MainMenu_Del',
+        up_type: 'Y',
+        MenuID: this.delButtons.join(',')
+      })
+    }).then(res => {
+      if (!res) return
+
+      _resolve()
+
+      if (res.status) {
+        notification.success({
+          top: 92,
+          message: '鍗囩骇鎴愬姛',
+          duration: 2
+        })
+
+        delete urlparam.type
+        delete urlparam.MenuType
+
+        let _param = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+
+        setTimeout(() => {
+          window.history.replaceState(null, null, window.location.href.split('#')[0] + `#/tabledesign/${_param}`)
+          window.location.reload()
+        }, 2000)
+      } else {
+        Modal.warning({
+          title: res.message,
+          okText: '鐭ラ亾浜�',
+          onOk: () => {
+            if (save) {
+              delete urlparam.type
+              delete urlparam.MenuType
+
+              let _param = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+
+              window.history.replaceState(null, null, window.location.href.split('#')[0] + `#/tabledesign/${_param}`)
+              window.location.reload()
+            } else {
+              window.location.reload()
+            }
+          }
+        })
+      }
+    })
+  }
+
+  getTable = (config, newCon, errors, formActions, popActions, oldtabs) => { 
+    let _card = {
+      uuid: newCon.uuid,
+      type: 'table',
+      name: newCon.name,
+      format: 'array',
+      pageable: true,
+      switchable: true,
+      search: [],
+      action: [],
+      subtype: 'basetable',
+      setting: { useMSearch: newCon.useMSearch },
+      wrap: {},
+      style: {},
+      headerStyle: {},
+      columns: [],
+      cols: [],
+      scripts: []
+    }
+
+    if (!config) {
+      errors.push(newCon.name + '閰嶇疆淇℃伅涓㈠け')
+      return _card
+    }
+
+    if (newCon.supModule && oldtabs[newCon.supModule]) {
+      _card.setting.supModule = [oldtabs[newCon.supModule]]
+    } else {
+      _card.setting.supModule = ['empty']
+    }
+
+    _card.search = config.search.map(item => {
+      item.uuid = Utils.getuuid()
+      return item
+    })
+
+    let _cols = {}
+    let _colspan = []
+    let lineMarks = []
+    config.columns.forEach(col => {
+      if (col.type === 'colspan') {
+        _colspan.push(...col.subfield.split(', '))
+      }
+      if (!col.field) return
+
+      let _col = fromJS(col).toJS()
+
+      if (_col.marks) {
+        _col.marks = []
+        col.marks.forEach(m => {
+          if (m.signType === 'card') return
+
+          let field = []
+          field.push(m.field)
+          field.push(m.contrastType)
+
+          if (m.contrastField) {
+            field.push(m.contrastField)
+          }
+          let color = '#1890ff'
+          if (m.color && m.color[1]) {
+            color = colorTransform[m.color[1]]
+          }
+          let signType = ['font']
+          if (m.signType === 'background') {
+            signType = ['background']
+          } else if (m.signType === 'icon') {
+            if (m.position === 'back') {
+              signType = ['iconback']
+            } else {
+              signType = ['iconfront']
+            }
+            signType.push(m.iconType)
+            signType.push(m.icon)
+          }
+
+          if (m.signType === 'line') {
+            lineMarks.push({
+              $index: lineMarks.length + 1,
+              field: field,
+              color: color,
+              contrastValue: m.contrastValue || '',
+              match: m.match,
+              signType: ['background']
+            })
+          } else {
+            _col.marks.push({
+              $index: _col.marks.length + 1,
+              field: field,
+              color: color,
+              contrastValue: m.contrastValue || '',
+              match: m.match,
+              signType: signType
+            })
+          }
+        })
+      }
+
+      _cols[col.field] = _col
+    })
+
+    config.columns.forEach(col => {
+      if (col.type === 'index') {
+        _card.cols.push(fromJS(col).toJS())
+      } else if (col.field && _cols[col.field]) {
+        let _col = fromJS(_cols[col.field]).toJS()
+
+        if (_colspan.includes(col.field)) {
+          _col.Hide = 'true'
+        }
+
+        _card.cols.push(_col)
+      }
+
+      if (col.type === 'colspan') {
+        let ucol = {
+          Align: col.Align,
+          Hide: col.Hide,
+          label: col.label,
+          marks: [],
+          isSub: false,
+          uuid: col.uuid,
+          blacklist: []
+        }
+        if (col.unfold === 'true') {
+          ucol.type = 'colspan'
+          ucol.subcols = []
+
+          col.subfield.split(', ').forEach(sub => {
+            if (_cols[sub]) {
+              let _col = fromJS(_cols[sub]).toJS()
+              _col.Hide = 'false'
+              _col.isSub = true
+              _col.uuid = Utils.getuuid()
+
+              ucol.subcols.push(_col)
+            }
+          })
+
+          if (ucol.subcols.length > 0) {
+            _card.cols.push(ucol)
+          }
+        } else {
+          ucol.Width = 120
+          ucol.type = 'custom'
+          ucol.elements = []
+          ucol.style = {paddingTop: '12px', paddingLeft: '8px', paddingBottom: '12px', paddingRight: '8px'}
+
+          col.subfield.split(', ').forEach(sub => {
+            if (_cols[sub]) {
+              let _col = {
+                copyable: 'false',
+                datatype: 'dynamic',
+                eleType: _cols[sub].type !== 'number' ? 'text' : 'number',
+                field: sub,
+                height: 1,
+                innerHeight: 21,
+                marks: _cols[sub].marks || null,
+                noValue: 'show',
+                style: {},
+                width: 24,
+                uuid: Utils.getuuid()
+              }
+
+              ucol.elements.push(_col)
+            }
+          })
+
+          if (ucol.elements.length > 0) {
+            _card.cols.push(ucol)
+          }
+        }
+      }
+    })
+
+    _card.lineMarks = lineMarks
+    _card.columns = []
+    _card.absFields = []
+    _card.cols.forEach(col => {
+      if (!col.field) return
+      if (['text', 'picture', 'video', 'textarea'].includes(col.type)) {
+        _card.columns.push({
+          datatype: `Nvarchar(${col.fieldlength || 50})`,
+          field: col.field,
+          fieldlength: col.fieldlength || 50,
+          label: col.label,
+          type: 'text',
+          uuid: col.uuid
+        })
+      } else if (col.type === 'link') {
+        _card.columns.push({
+          datatype: `Nvarchar(${col.fieldlength || 50})`,
+          field: col.field,
+          fieldlength: col.fieldlength || 50,
+          label: col.label,
+          type: 'text',
+          uuid: col.uuid
+        })
+        if (col.nameField) {
+          _card.columns.push({
+            datatype: `Nvarchar(${col.fieldlength || 50})`,
+            field: col.nameField,
+            fieldlength: col.fieldlength || 50,
+            label: col.label + '(鍚嶇О)',
+            type: 'text',
+            uuid: col.uuid + 'name'
+          })
+        }
+      } else if (col.type === 'number') {
+        _card.columns.push({
+          datatype: `Decimal(18,${col.decimal || 0})`,
+          field: col.field,
+          decimal: col.decimal || 0,
+          label: col.label,
+          type: 'number',
+          uuid: col.uuid
+        })
+
+        if (col.format === 'abs') {
+          _card.absFields.push(col.field)
+        }
+      }
+    })
+
+    if (_card.absFields.length) {
+      _card.absFields = Array.from(new Set(_card.absFields))
+    } else {
+      _card.absFields = null
+    }
+
+    let colbtns = []
+    let colors = { primary: '#1890ff', yellow: '#c49f47', orange: 'orange', danger: '#ff4d4f', green: '#26C281', dgreen: '#32c5d2', purple: '#8E44AD', cyan: '#13c2c2', gray: '#666666', default: 'rgba(0, 0, 0, 0.65)' }
+
+    let uuids = {}
+    config.action.forEach(btn => {
+      let _c = btn.class ? btn.class.replace('border-', '') : ''
+      let color = colors[_c] || '#1890ff'
+      let _btn = fromJS(btn).toJS()
+      // _btn.uuid = Utils.getuuid()
+      delete _btn.position
+      delete _btn.linkTab
+
+      if (_btn.intertype === 'inner' && !_btn.innerFunc) {
+        _btn.intertype = 'system'
+      }
+      if (_btn.funcType === 'print' && _btn.execMode) {
+        _btn.OpenType = 'funcbutton'
+      } else if (_btn.OpenType === 'blank') {
+        _btn.OpenType = 'tab'
+      }
+
+      this.delButtons.push(btn.uuid)
+
+      _btn.show = 'button'
+
+      if (_btn.execSuccess === 'equaltab') {
+        _btn.execSuccess = 'grid'
+        _btn.syncComponent = []
+
+        if (newCon.equalTab && oldtabs && oldtabs[newCon.equalTab]) {
+          _btn.syncComponent.push(oldtabs[newCon.equalTab])
+        }
+      } else if (_btn.execSuccess === 'maingrid') {
+        _btn.execSuccess = 'mainline'
+      }
+
+      if (_btn.execError === 'equaltab') {
+        _btn.execError = 'grid'
+      } else if (_btn.execError === 'maingrid') {
+        _btn.execError = 'mainline'
+      }
+
+      if (_btn.popClose === 'equaltab') {
+        _btn.popClose = 'grid'
+        _btn.syncComponent = []
+
+        if (newCon.equalTab && oldtabs && oldtabs[newCon.equalTab]) {
+          _btn.syncComponent.push(oldtabs[newCon.equalTab])
+        }
+      } else if (_btn.popClose === 'maingrid') {
+        _btn.popClose = 'mainline'
+      }
+
+      if (_btn.OpenType === 'pop' || (_btn.OpenType === 'funcbutton' && _btn.funcType === 'print' && _btn.execMode === 'pop')) {
+
+        formActions.push({origin: btn.uuid, uuid: _btn.uuid, name: newCon.name, label: btn.label})
+      } else if (_btn.OpenType === 'popview') {
+        if (!popActions) return
+
+        popActions.push({origin: btn.uuid, linkTab: btn.linkTab || '', uuid: _btn.uuid, name: newCon.name, label: btn.label})
+      } else if (_btn.OpenType === 'tab') {
+        if (btn.tabTemplate === 'FormTab' || !btn.linkmenu || btn.linkmenu.length !== 3) {
+
+          errors.push(newCon.name + '涓寜閽��' + btn.label + '銆嬩笉鍦ㄦ敮鎸�')
+          return
+        }
+      }
+
+      uuids[btn.uuid] = true
+
+      if (_btn.tabType === 'CommonTable' || _btn.tabType === 'SubTable') {
+        _btn.tabType = 'BaseTable'
+      }
+
+      if (btn.position === 'grid') {
+        _btn.eleType = 'button'
+        _btn.width = 24
+        _btn.style = {color: color, backgroundColor: 'transparent', borderColor: 'transparent'}
+        colbtns.push(_btn)
+      } else {
+        if (btn.class === 'default') {
+          _btn.style = {color: 'rgba(0, 0, 0, 0.65)', backgroundColor: '#fff', borderColor: '#d9d9d9', marginRight: '15px'}
+        } else if (btn.class.indexOf('border') > -1) {
+          _btn.style = {color: color, backgroundColor: '#fff', borderColor: color, marginRight: '15px'}
+        } else {
+          _btn.style = {color: '#fff', backgroundColor: color, borderColor: color, marginRight: '15px'}
+        }
+        _card.action.push(_btn)
+      }
+    })
+
+    if (colbtns.length > 0) {
+      _card.cols.push({
+        Align: 'left',
+        label: '鎿嶄綔',
+        marks: [],
+        isSub: false,
+        uuid: Utils.getuuid(),
+        type: 'action',
+        Width: 120,
+        elements: colbtns,
+        style: {paddingTop: '12px', paddingLeft: '8px', paddingBottom: '12px', paddingRight: '8px'}
+      })
+    }
+
+    let sets = ['tableName', 'interType', 'sysInterface', 'innerFunc', 'interface', 'proInterface', 'outerFunc', 'dataresource', ['queryType', 'query'], 'primaryKey', 'order', 'execute', ['laypage', 'true'], ['pageSize', 10], ['onload', 'true']]
+    let wraps = ['tableType', ['bordered', 'true'], 'actionfixed', ['size', 'middle'], ['selected', 'false'], ['tableMode', 'compatible'], ['mask', 'show'], ['borderColor', '#e8e8e8'], 'height', 'controlField', 'controlVal']
+
+    _card.scripts = config.setting.scripts || []
+
+    sets.forEach(n => {
+      if (n === 'interType') {
+        if (!['system', 'inner', 'outer'].includes(config.setting.interType)) {
+          _card.setting.interType = 'system'
+        } else if (config.setting.interType === 'inner' && !config.setting.innerFunc) {
+          _card.setting.interType = 'system'
+        } else {
+          _card.setting.interType = config.setting.interType
+        }
+      } else if (typeof(n) === 'string') {
+        _card.setting[n] = config.setting[n] || ''
+      } else {
+        _card.setting[n[0]] = config.setting[n[0]] || n[1]
+      }
+    })
+    wraps.forEach(w => {
+      if (typeof(w) === 'string') {
+        _card.wrap[w] = config.setting[w] || ''
+      } else {
+        _card.wrap[w[0]] = config.setting[w[0]] || w[1]
+      }
+    })
+
+    _card.wrap.doubleClick = config.setting.doubleClick || ''
+    if (_card.wrap.doubleClick && !uuids[_card.wrap.doubleClick]) {
+      _card.wrap.doubleClick = ''
+    }
+
+    return _card
+  }
+
+  render() {
+    return (
+      <Button className="mk-border-purple" onClick={this.trigger}><ArrowUpOutlined /> 鍗囩骇</Button>
+    )
+  }
+}
+
+export default UpdateTable
\ No newline at end of file
diff --git a/src/views/popdesign/menuform/index.scss b/src/templates/comtableconfig/updatetable/index.scss
similarity index 100%
copy from src/views/popdesign/menuform/index.scss
copy to src/templates/comtableconfig/updatetable/index.scss
diff --git a/src/templates/formtabconfig/index.jsx b/src/templates/formtabconfig/index.jsx
index e32a9bf..ad13b60 100644
--- a/src/templates/formtabconfig/index.jsx
+++ b/src/templates/formtabconfig/index.jsx
@@ -524,6 +524,7 @@
 
           param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
           param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+          param.LText = param.LText.replace(/\n/g, ' ')
 
           param.LText = Utils.formatOptions(param.LText)
           param.secretkey = Utils.encrypt('', param.timestamp)
@@ -1206,7 +1207,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
         
diff --git a/src/templates/formtabconfig/settingform/index.jsx b/src/templates/formtabconfig/settingform/index.jsx
index bc10e56..7330244 100644
--- a/src/templates/formtabconfig/settingform/index.jsx
+++ b/src/templates/formtabconfig/settingform/index.jsx
@@ -251,7 +251,12 @@
             </Form.Item>
           </Col> : null}
           {datatype === 'query' && interType === 'outer' ? <Col span={12}>
-            <Form.Item label="绯荤粺鎺ュ彛">
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍗曠偣鐧诲綍绯荤粺">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                绯荤粺鎺ュ彛
+              </Tooltip>
+            }>
               {getFieldDecorator('sysInterface', {
                 initialValue: setting.sysInterface || 'false'
               })(
diff --git a/src/templates/modalconfig/index.jsx b/src/templates/modalconfig/index.jsx
index 97c1bdd..b9fa418 100644
--- a/src/templates/modalconfig/index.jsx
+++ b/src/templates/modalconfig/index.jsx
@@ -357,6 +357,9 @@
     if (!card.span && standardform && standardform.span) {
       card.span = standardform.span
       card.labelwidth = standardform.labelwidth
+    } else if (!card.span) {
+      card.span = 12
+      card.labelwidth = 33.3
     }
 
     this.setState({
@@ -417,6 +420,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/templates/modalconfig/settingform/index.jsx b/src/templates/modalconfig/settingform/index.jsx
index db471a4..ae2a01e 100644
--- a/src/templates/modalconfig/settingform/index.jsx
+++ b/src/templates/modalconfig/settingform/index.jsx
@@ -19,6 +19,7 @@
     display: this.props.config.setting.display || 'modal',
     placement: this.props.config.setting.placement || 'right',
     appType: sessionStorage.getItem('appType'),
+    viewType: sessionStorage.getItem('editMenuType') || '',
     dialogInput: false
   }
 
@@ -315,7 +316,7 @@
               )}
             </Form.Item>
           </Col> : null}
-          {!this.props.isSubTab && !appType && display === 'modal' ? <Col span={12}>
+          {!this.props.isSubTab && !appType && this.state.viewType !== 'popview' && display === 'modal' ? <Col span={12}>
             <Form.Item label="鎸傝浇瀵硅薄">
               {getFieldDecorator('container', {
                 initialValue: config.setting.container || 'tab'
diff --git a/src/templates/sharecomponent/actioncomponent/index.jsx b/src/templates/sharecomponent/actioncomponent/index.jsx
index 4253f4d..976dd12 100644
--- a/src/templates/sharecomponent/actioncomponent/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/index.jsx
@@ -674,8 +674,6 @@
       profVisible: true,
       card: element
     })
-
-    MKEmitter.emit('modalStatus', '楠岃瘉淇℃伅')
   }
 
   /**
@@ -703,8 +701,6 @@
       }, () => {
         this.props.updateaction({...config, action: _actionlist})
       })
-
-      MKEmitter.emit('modalStatus', false)
     })
   }
 
@@ -950,11 +946,9 @@
             if (this.verifyRef.handleCancel) {
               this.verifyRef.handleCancel().then(() => {
                 this.setState({ profVisible: false })
-                MKEmitter.emit('modalStatus', false)
               })
             } else {
               this.setState({ profVisible: false })
-              MKEmitter.emit('modalStatus', false)
             }
           }}
           destroyOnClose
diff --git a/src/templates/sharecomponent/actioncomponent/index.scss b/src/templates/sharecomponent/actioncomponent/index.scss
index ed09980..605875c 100644
--- a/src/templates/sharecomponent/actioncomponent/index.scss
+++ b/src/templates/sharecomponent/actioncomponent/index.scss
@@ -1,9 +1,9 @@
 .model-table-action-list {
   position: relative;
-  padding: 15px 25px 15px;
-  min-height: 65px;
+  padding: 0px 25px;
 
   > .ant-row {
+    padding-top: 15px;
     min-height: 65px;
   }
   .mk-hidden {
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
index ad293e5..31105a0 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
@@ -75,6 +75,15 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
+        
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
 
         let _quot = values.sql.match(/'{1}/g)
@@ -175,7 +184,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         // 澶栬仈鏁版嵁搴撴浛鎹�
         if (window.GLOB.externalDatabase !== null) {
           param.LText = param.LText.replace(/@db@/ig, window.GLOB.externalDatabase)
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx
index 9d3d7e7..8ba60e3 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/customscript/index.jsx
@@ -63,6 +63,15 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
+        
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
 
         let _quot = values.sql.match(/'{1}/g)
@@ -163,7 +172,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         // 澶栬仈鏁版嵁搴撴浛鎹�
         if (window.GLOB.externalDatabase !== null) {
           param.LText = param.LText.replace(/@db@/ig, window.GLOB.externalDatabase)
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx
index c6f8e4e..cd970a6 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/datasource/index.jsx
@@ -205,7 +205,13 @@
                 </Tooltip>
               }>
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || ''
+                  initialValue: setting.order || '',
+                  rules: [
+                    {
+                      required: true,
+                      message: '璇峰~鍐欐帓搴忔柟寮�!'
+                    }
+                  ]
                 })(<Input placeholder={'ID asc, UID desc'} autoComplete="off" />)}
               </Form.Item>
             </Col> : null}
@@ -225,17 +231,6 @@
                 </Radio.Group>)}
               </Form.Item>
             </Col> : null}
-            {/* {dataType === 'custom' ? <Col span={8}>
-              <Form.Item label="浜嬪姟">
-                {getFieldDecorator('transaction', {
-                  initialValue: setting.transaction || 'false'
-                })(
-                <Radio.Group>
-                  <Radio value="true">浣跨敤</Radio>
-                  <Radio value="false">涓嶄娇鐢�</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col> : null} */}
           </Row>
         </Form>
       </div>
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
index 936a558..798b5b2 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
@@ -234,54 +234,118 @@
       defaultscript = `update ${config.setting.tableName || ''} set idefine5= idefine5+1 ,modifydate=getdate(),cdefine5='宸插鍑�',modifyuserid=@userid@ ${search}`
     }
 
-    let search = []
+    let appType = sessionStorage.getItem('appType')
+    let searches = []
 
-    if (config.setting && config.setting.useMSearch === 'true' && window.GLOB.customMenu) {
+    if (appType === 'mob') {
       let menu = fromJS(window.GLOB.customMenu).toJS()
-      let _search = null
-      let filterComponent = (box) => {
-        box.components.forEach(item => {
-          if (_search) return
+      let ms = null
+      let search = []
+      menu.components.forEach(item => {
+        if (item.type === 'topbar' && item.wrap.type !== 'navbar' && item.search) {
+          ms = item.search
+        } else if (item.type === 'search' && item.wrap.field) {
+          search.push({
+            type: 'text',
+            label: item.wrap.label,
+            field: item.wrap.field,
+            match: item.wrap.match,
+            required: item.wrap.required,
+            value: item.wrap.initval || ''
+          })
+        }
+      })
+
+      if (ms) {
+        if (ms.setting.type === 'search') {
+          search.push({
+            type: 'text',
+            label: '鎼滅储鏍�',
+            field: ms.setting.field,
+            match: ms.setting.match,
+            required: ms.setting.required,
+            value: ms.setting.initval || ''
+          })
+        }
+        ms.fields.forEach(item => {
+          if (item.type === 'range') {
+            item.initval = `${item.minValue},${item.maxValue}`
+          }
+          search.push(item)
+        })
+
+        ms.groups.forEach(group => {
+          if (group.setting.type === 'search') {
+            search.push({
+              type: 'text',
+              label: group.wrap.name,
+              field: group.setting.field,
+              match: group.setting.match,
+              required: group.setting.required,
+              value: group.setting.initval || ''
+            })
+          }
+
+          group.fields.forEach(item => {
+            if (item.type === 'range') {
+              item.initval = `${item.minValue},${item.maxValue}`
+            }
+            search.push(item)
+          })
+        })
+      }
+
+      searches = search
+    } else {
+      let search = []
   
-          if (item.type === 'search') {
-            box.slist = [...box.slist, item.search]
-          } else if (item.uuid === config.uuid) {
-            _search = box.slist.pop()
-          } else if (item.type === 'group') {
-            item.components.forEach(m => {
-              if (m.uuid !== config.uuid) return
+      if (config.setting && config.setting.useMSearch === 'true' && window.GLOB.customMenu) {
+        let menu = fromJS(window.GLOB.customMenu).toJS()
+        let _search = null
+        let filterComponent = (box) => {
+          box.components.forEach(item => {
+            if (_search) return
+    
+            if (item.type === 'search') {
+              box.slist = [...box.slist, item.search]
+            } else if (item.uuid === config.uuid) {
               _search = box.slist.pop()
-            })
-          } else if (item.type === 'tabs') {
-            item.subtabs.forEach(tab => {
-              tab.slist = [...box.slist]
-              filterComponent(tab)
-            })
+            } else if (item.type === 'group') {
+              item.components.forEach(m => {
+                if (m.uuid !== config.uuid) return
+                _search = box.slist.pop()
+              })
+            } else if (item.type === 'tabs') {
+              item.subtabs.forEach(tab => {
+                tab.slist = [...box.slist]
+                filterComponent(tab)
+              })
+            }
+          })
+        }
+        menu.slist = []
+        filterComponent(menu)
+    
+        if (_search) {
+          search = _search
+        } else {
+          menu.components.forEach(item => {
+            if (item.type !== 'search') return
+            search = item.search
+          })
+        }
+      }
+  
+      searches = fromJS(config.search || []).toJS()
+  
+      if (search.length > 0) {
+        let keys = searches.map(item => (item.field ? item.field.toLowerCase() : ''))
+        search.forEach(item => {
+          if (item.field && !keys.includes(item.field.toLowerCase())) {
+            searches.push(item)
           }
         })
       }
-      menu.slist = []
-      filterComponent(menu)
-  
-      if (_search) {
-        search = _search
-      } else {
-        menu.components.forEach(item => {
-          if (item.type !== 'search') return
-          search = item.search
-        })
-      }
-    }
-
-    let searches = fromJS(config.search || []).toJS()
-
-    if (search.length > 0) {
-      let keys = searches.map(item => (item.field ? item.field.toLowerCase() : ''))
-      search.forEach(item => {
-        if (item.field && !keys.includes(item.field.toLowerCase())) {
-          searches.push(item)
-        }
-      })
     }
 
     this.setState({
@@ -679,7 +743,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
         
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx
index f1d391d..72f7c39 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/utils.jsx
@@ -132,6 +132,7 @@
     }
     sql = sql.replace(/\n\s{8}/ig, '\n')
     console.info(sql)
+    sql = sql.replace(/\n/g, ' ')
 
     return sql
   }
diff --git a/src/templates/sharecomponent/fieldscomponent/editcard/index.jsx b/src/templates/sharecomponent/fieldscomponent/editcard/index.jsx
index c6d82e3..f39eecc 100644
--- a/src/templates/sharecomponent/fieldscomponent/editcard/index.jsx
+++ b/src/templates/sharecomponent/fieldscomponent/editcard/index.jsx
@@ -11,18 +11,21 @@
     super(props)
 
     let _type = props.card.type
-    if (props.type === 'columns') {
-      if (_type === 'date' || _type === 'datetime') {
-        _type = 'text'
+
+    if (!props.card.origin) {
+      if (props.type === 'columns') {
+        if (_type !== 'number') {
+          _type = 'text'
+        }
+      } else if (props.type === 'search') {
+        if (_type !== 'select') {
+          _type = 'text'
+        }
+      } else if (props.type === 'form') {
+        if (_type !== 'number') {
+          _type = 'text'
+        }
       }
-    } else if (props.type === 'search') {
-      if (_type === 'number') {
-        _type = 'text'
-      } else if (_type === 'datetime') {
-        _type = 'daterange'
-      }
-    } else if (props.type === 'form') {
-      
     }
 
     this.state = {
@@ -78,14 +81,12 @@
           <Radio.Group onChange={this.changeType} value={card.type} disabled={!card.selected || card.origin}>
             <Radio value="text">text</Radio>
             <Radio value="select">select</Radio>
-            <Radio value="daterange">dateRange</Radio>
           </Radio.Group> : null
         }
         {type === 'columns' ?
           <Radio.Group onChange={this.changeType} value={card.type} disabled={!card.selected || card.origin}>
             <Radio value="text">text</Radio>
             <Radio value="number">number</Radio>
-            <Radio value="picture">picture</Radio>
           </Radio.Group> : null
         }
         {type === 'form' ?
diff --git a/src/templates/sharecomponent/fieldscomponent/index.jsx b/src/templates/sharecomponent/fieldscomponent/index.jsx
index 53642ec..41a9a0a 100644
--- a/src/templates/sharecomponent/fieldscomponent/index.jsx
+++ b/src/templates/sharecomponent/fieldscomponent/index.jsx
@@ -108,9 +108,6 @@
         let initval = ''
         if (item.type === 'select') {
           _match = '='
-        } else if (item.type === 'daterange') {
-          initval = '[30, 0]'
-          _match = 'between'
         } else {
           item.type = 'text'
           _match = 'like'
@@ -140,7 +137,7 @@
           label: item.label,
           field: item.field,
           Hide: 'false',
-          IsSort: item.type === 'picture' ? 'false' : 'true',
+          IsSort: 'true',
           type: item.type,
           Width: item.type === 'number' ? 80 : 120
         }
diff --git a/src/templates/sharecomponent/searchcomponent/index.jsx b/src/templates/sharecomponent/searchcomponent/index.jsx
index 01a9c35..88b50ab 100644
--- a/src/templates/sharecomponent/searchcomponent/index.jsx
+++ b/src/templates/sharecomponent/searchcomponent/index.jsx
@@ -229,6 +229,7 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@\$|\$@/ig, '').replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/\n/g, ' ')
         
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
diff --git a/src/templates/sharecomponent/settingcalcomponent/index.jsx b/src/templates/sharecomponent/settingcalcomponent/index.jsx
index 646acee..d1d7909 100644
--- a/src/templates/sharecomponent/settingcalcomponent/index.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/index.jsx
@@ -5,7 +5,6 @@
 import { SettingOutlined } from '@ant-design/icons'
 
 import VerifyCard from './verifycard'
-import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
 class DataSource extends Component {
@@ -36,7 +35,6 @@
     this.setState({
       visible: true
     })
-    MKEmitter.emit('modalStatus', '鏁版嵁婧�')
   }
 
   verifySubmit = () => {
@@ -65,8 +63,6 @@
 
       this.setState({loading: false, visible: false})
       this.props.updateConfig({...config, ...res})
-
-      MKEmitter.emit('modalStatus', false)
     }, () => {
       this.setState({loading: false})
     })
@@ -87,7 +83,7 @@
           okText="鎻愪氦"
           onOk={this.verifySubmit}
           confirmLoading={loading}
-          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false }) }}
+          onCancel={() => { this.setState({ visible: false }) }}
           destroyOnClose
         >
           <VerifyCard
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
index 3b15e0e..5eb0bce 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
@@ -120,7 +120,7 @@
         _dataresource = '(' + _dataresource + ') tb'
       }
 
-      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
+      _dataresource = `/*system_query*/select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
     }
 
     if (_customScript) {
@@ -138,6 +138,7 @@
     }
     sql = sql.replace(/\n\s{8}/ig, '\n')
     console.info(sql)
+    sql = sql.replace(/\n/g, ' ')
     
     return sql
   }
diff --git a/src/templates/sharecomponent/settingcomponent/index.jsx b/src/templates/sharecomponent/settingcomponent/index.jsx
index 85be0a7..7f47893 100644
--- a/src/templates/sharecomponent/settingcomponent/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/index.jsx
@@ -5,7 +5,6 @@
 import { SettingOutlined } from '@ant-design/icons'
 
 import Utils, { FuncUtils } from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
 import SettingForm from './settingform'
 import CreateFunc from '@/templates/zshare/createfunc'
 import CreateInterface from '@/templates/zshare/createinterface'
@@ -48,8 +47,6 @@
       search: _search,
       menu: menu
     })
-
-    MKEmitter.emit('modalStatus', '鏁版嵁婧�')
   }
 
   /**
@@ -70,8 +67,6 @@
       res.actionfixed = res.actionfixed === 'true'
 
       this.props.updatesetting({...config, setting: res})
-
-      MKEmitter.emit('modalStatus', false)
     }, () => {
       this.setState({
         loading: false
@@ -152,6 +147,16 @@
           setting.dataresource = setting.dataresource.replace(reg, `/*$ex@${m.func_code}-begin*/\n${m.key_sql}\n/*@ex$-end*/`)
         }
       })
+    } else {
+      setting.scripts.forEach(item => {
+        if (item.status === 'false') return
+
+        if (/exec\s/ig.test(item.sql)) {
+          maxScript = 1000
+        } else if (item.sql.length > maxScript) {
+          maxScript = item.sql.length
+        }
+      })
     }
 
     setting.maxScript = maxScript
@@ -185,11 +190,11 @@
           visible={visible}
           width={'75vw'}
           maskClosable={false}
-          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false, loading: false })}}
+          onCancel={() => { this.setState({ visible: false, loading: false })}}
           footer={[
             record && record.interType === 'system' ? <CreateInterface key="interface" loading={this.state.interloading} ref="tableCreatInterface" trigger={this.tableCreatInterface}/> : null,
             record && record.interType === 'inner' ? <CreateFunc key="create" ref="funcCreatComponent" trigger={this.tableCreatFunc}/> : null,
-            <Button key="cancel" onClick={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false, loading: false }) }}>鍙栨秷</Button>,
+            <Button key="cancel" onClick={() => { this.setState({ visible: false, loading: false }) }}>鍙栨秷</Button>,
             <Button key="confirm" type="primary" loading={this.state.loading} onClick={this.settingSave}>纭畾</Button>
           ]}
           destroyOnClose
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
index a0a1c65..83a70ae 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
@@ -260,7 +260,12 @@
             </Form.Item>
           </Col>
           {interType === 'outer' ? <Col span={8}>
-            <Form.Item label="绯荤粺鎺ュ彛">
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍗曠偣鐧诲綍绯荤粺">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                绯荤粺鎺ュ彛
+              </Tooltip>
+            }>
               {getFieldDecorator('sysInterface', {
                 initialValue: sysInterface,
                 rules: [
@@ -490,17 +495,6 @@
               </Radio.Group>)}
             </Form.Item>
           </Col>
-          {/* <Col span={8}>
-            <Form.Item label="浜嬪姟">
-              {getFieldDecorator('transaction', {
-                initialValue: setting.transaction || 'false'
-              })(
-              <Radio.Group>
-                <Radio value="true">浣跨敤</Radio>
-                <Radio value="false">涓嶄娇鐢�</Radio>
-              </Radio.Group>)}
-            </Form.Item>
-          </Col> */}
           <Col span={8}>
             <Form.Item label={
               <Tooltip placement="topLeft" title="浣跨敤鎬ラ�熸ā寮忔椂锛岃〃鏍间腑鐨勬爣璁般�佸弻鍑讳簨浠躲�佹牸寮忓寲銆佽鍚堝苟銆佸墠缂�銆佸悗缂�銆佸瓧娈甸�忚绛夋晥鏋滃皢鏃犳晥锛屼笖鏁版嵁閮戒細浠ユ枃鏈牸寮忔樉绀恒��">
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
index 86caaea..3ed654e 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
@@ -557,36 +557,50 @@
               title = title && title[0] ? title[0] : ''
               let _text = title ? item.sql.replace(title, '') : item.sql
 
-              return (
-                <div className={'script-item ' + (script && script.uuid === item.uuid ? 'active' : '') } key={item.uuid}>
-                  <div style={{cursor: 'pointer'}} onClick={() => {
-                    this.setState({script: item, scriptValue: item.sql})
-                  }}>
-                    {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
-                    <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
-                    <div>{item.status === 'false' ?
-                      <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
-                        绂佺敤
-                        <StopOutlined style={{marginLeft: '5px'}} />
-                      </span> : 
-                      <span style={{color: '#26C281', marginLeft: '20px'}}>
-                        鍚敤
-                        <CheckCircleOutlined style={{marginLeft: '5px'}}/>
-                      </span>}
+              if (item.status === 'false') {
+                return (
+                  <div className="script-item" key={item.uuid}>
+                    <div style={{cursor: 'not-allowed'}}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>
+                        <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
+                          绂佺敤
+                          <StopOutlined style={{marginLeft: '5px'}} />
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{height: '24px'}}></div>
+                  </div>
+                )
+              } else {
+                return (
+                  <div className={'script-item ' + (script && script.uuid === item.uuid ? 'active' : '') } key={item.uuid}>
+                    <div style={{cursor: 'pointer'}} onClick={() => {
+                      this.setState({script: item, scriptValue: item.sql})
+                    }}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>
+                        <span style={{color: '#26C281', marginLeft: '20px'}}>
+                          鍚敤
+                          <CheckCircleOutlined style={{marginLeft: '5px'}}/>
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{textAlign: 'right'}}>
+                      <span className="operation-btn" onClick={() => this.handleStatus(item)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
+                      <Popconfirm
+                        overlayClassName="popover-confirm"
+                        title="纭畾鍒犻櫎鍚�?"
+                        onConfirm={() => this.handleDelete(item)
+                      }>
+                        <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
+                      </Popconfirm>
                     </div>
                   </div>
-                  <div style={{textAlign: 'right'}}>
-                    <span className="operation-btn" onClick={() => this.handleStatus(item)} style={{color: '#8E44AD'}}><SwapOutlined /></span>
-                    <Popconfirm
-                      overlayClassName="popover-confirm"
-                      title="纭畾鍒犻櫎鍚�?"
-                      onConfirm={() => this.handleDelete(item)
-                    }>
-                      <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
-                    </Popconfirm>
-                  </div>
-                </div>
-              )
+                )
+              }
             })}
           </div>
           <div className="script-button">
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
index 6305eb8..2f679b7 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
@@ -300,6 +300,8 @@
 
     param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
     param.LText = param.LText.replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+    param.LText = param.LText.replace(/\n/g, ' ')
+    
     param.LText = Utils.formatOptions(param.LText)
     param.secretkey = Utils.encrypt('', param.timestamp)
     
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
index b165395..5f186cf 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
@@ -103,7 +103,7 @@
 
     if (arr_field && _dataresource && /\/\*\$sum@/ig.test(_dataresource)) {
       let _sql = _dataresource.replace(/\/\*\$sum@|@sum\$\*\//ig, '')
-      _sql = `${_sql} ${_search}`
+      _sql = `/*system_query*/${_sql} ${_search}`
       if (_customScript) {
         sumSql = `/* sql sum楠岃瘉 */
           ${_customScript}
@@ -125,7 +125,7 @@
         _dataresource = '(' + _dataresource + ') tb'
       }
 
-      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
+      _dataresource = `/*system_query*/select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
     }
 
     if (_customScript) {
@@ -166,7 +166,9 @@
     if (sumSql) {
       sumSql = sumSql.replace(/\n\s{10}/ig, '\n')
       console.info(sumSql)
+      sumSql = sumSql.replace(/\n/g, ' ')
     }
+    sql = sql.replace(/\n/g, ' ')
 
     return { sql, sumSql, errors: errors.join('锛�') }
   }
diff --git a/src/templates/sharecomponent/treesettingcomponent/index.jsx b/src/templates/sharecomponent/treesettingcomponent/index.jsx
index 867f295..055a295 100644
--- a/src/templates/sharecomponent/treesettingcomponent/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/index.jsx
@@ -4,7 +4,6 @@
 import { SettingOutlined } from '@ant-design/icons'
 
 import SettingForm from './settingform'
-import MKEmitter from '@/utils/events.js'
 
 import './index.scss'
 
@@ -32,7 +31,6 @@
       visible: true,
       menu: menu
     })
-    MKEmitter.emit('modalStatus', '鏁版嵁婧�')
   }
 
   /**
@@ -69,8 +67,6 @@
       res.searchLwidth = config.setting.searchLwidth !== undefined ? config.setting.searchLwidth : 33.3
 
       this.props.updatesetting({...config, setting: res})
-
-      MKEmitter.emit('modalStatus', false)
     }, () => {
       this.setState({
         loading: false
@@ -100,7 +96,7 @@
           visible={visible}
           width={'75vw'}
           maskClosable={false}
-          onCancel={() => { MKEmitter.emit('modalStatus', false); this.setState({ visible: false })}}
+          onCancel={() => { this.setState({ visible: false })}}
           confirmLoading={loading}
           onOk={this.settingSave}
           destroyOnClose
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
index 075f17b..ab38ac2 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
@@ -245,7 +245,12 @@
             </Form.Item>
           </Col>
           {interType === 'outer' ? <Col span={8}>
-            <Form.Item label="绯荤粺鎺ュ彛">
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍗曠偣鐧诲綍绯荤粺">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                绯荤粺鎺ュ彛
+              </Tooltip>
+            }>
               {getFieldDecorator('sysInterface', {
                 initialValue: setting.sysInterface || 'false',
                 rules: [
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
index 664ef5b..bc1225f 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
@@ -140,6 +140,7 @@
         exec_type: 'y',
         LText: SettingUtils.getDebugSql(setting, _scripts, timestamp)
       }
+      param.LText = param.LText.replace(/\n/g, ' ')
       param.LText = Utils.formatOptions(param.LText)
       param.timestamp = timestamp
       param.secretkey = Utils.encrypt('', timestamp)
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
index 0444260..80e7bc0 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
@@ -64,7 +64,7 @@
         _dataresource = '(' + _dataresource + ') tb'
       }
 
-      _dataresource = ` select ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource}) tmptable order by tmptable.rows `
+      _dataresource = `/*system_query*/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) {
diff --git a/src/templates/subtableconfig/index.jsx b/src/templates/subtableconfig/index.jsx
index c3d4de1..34c9408 100644
--- a/src/templates/subtableconfig/index.jsx
+++ b/src/templates/subtableconfig/index.jsx
@@ -63,7 +63,6 @@
     activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
     chartview: null,         // 褰撳墠瑙嗗浘
     openEdition: '',         // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
   }
 
   /**
@@ -149,10 +148,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -165,7 +177,6 @@
         return false
       }
     }
-    MKEmitter.addListener('modalStatus', this.modalStatus)
   }
 
   /**
@@ -226,11 +237,6 @@
       return
     }
     document.onkeydown = () => {}
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   // 椤甸潰杩斿洖
diff --git a/src/templates/treepageconfig/index.jsx b/src/templates/treepageconfig/index.jsx
index a030308..eab9619 100644
--- a/src/templates/treepageconfig/index.jsx
+++ b/src/templates/treepageconfig/index.jsx
@@ -10,7 +10,6 @@
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
 
-import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
 import MenuForm from '@/templates/comtableconfig/menuform'
 import SourceElement from '@/templates/zshare/dragsource'
@@ -43,7 +42,6 @@
     tabviews: [],            // 鎵�鏈夋爣绛鹃〉
     activeKey: '0',          // 榛樿灞曞紑鍩烘湰淇℃伅
     openEdition: '',         // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
   }
 
   /**
@@ -127,10 +125,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -143,7 +154,6 @@
         return false
       }
     }
-    MKEmitter.addListener('modalStatus', this.modalStatus)
   }
 
   /**
@@ -154,11 +164,6 @@
       return
     }
     document.onkeydown = () => {}
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   /**
diff --git a/src/templates/zshare/codemirror/index.jsx b/src/templates/zshare/codemirror/index.jsx
index 98b324a..24f6872 100644
--- a/src/templates/zshare/codemirror/index.jsx
+++ b/src/templates/zshare/codemirror/index.jsx
@@ -2,9 +2,10 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Dropdown, Menu } from 'antd'
-import { FullscreenOutlined, FullscreenExitOutlined, FontSizeOutlined } from '@ant-design/icons'
+import { FullscreenOutlined, FullscreenExitOutlined, FontSizeOutlined, FormatPainterOutlined } from '@ant-design/icons'
 
 import {UnControlled as CodeMirror} from 'react-codemirror2'
+import sqlFormatter from '@/utils/sqlFormatter.js'
 import 'codemirror/mode/javascript/javascript'
 import 'codemirror/mode/sql/sql'
 import 'codemirror/mode/xml/xml'
@@ -26,7 +27,6 @@
   }
 
   state = {
-    editor: null,   // code瀵硅薄
     defaultVal: '', // 鍒濆鍊�
     value: '',      // 瀹炴椂鍐呭
     options: null,  // mode : text/xml, text/css, text/javascript銆乼ext/x-mysql ; theme : cobalt - 榛戝簳
@@ -34,6 +34,8 @@
     style: {fontSize: '18px', lineHeight: '32px'},
     display: true
   }
+
+  editor = null
 
   UNSAFE_componentWillMount () {
     let options = {
@@ -55,15 +57,15 @@
   }
 
   UNSAFE_componentWillReceiveProps (nextProps) {
-    const { value, editor } = this.state
+    const { value } = this.state
 
     if (value !== nextProps.value) {
       this.setState({
         value: nextProps.value || ''
       })
 
-      if (editor && editor.setValue) {
-        editor.setValue(nextProps.value || '')
+      if (this.editor && this.editor.setValue) {
+        this.editor.setValue(nextProps.value || '')
       } else {
         this.setState({
           defaultVal: nextProps.value || ''
@@ -73,7 +75,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS({...this.state, value: '', editor: ''}), fromJS({...nextState, value: '', editor: ''}))
+    return !is(fromJS({...this.state, value: ''}), fromJS({...nextState, value: ''}))
   }
 
   fullScreenChange = () => {
@@ -99,12 +101,52 @@
     }
 
     // 鍒囨崲瀛椾綋澶у皬锛屽埛鏂扮紪杈戝櫒绐楀彛锛岃В鍐宠皟鏁村悗鐨勯�夋嫨鍖哄煙閿欎贡闂
-    this.setState({style: _style, display: false, editor: null, defaultVal: this.state.value}, () => {
+    this.setState({style: _style, display: false, defaultVal: this.state.value}, () => {
       this.setState({display: true})
     })
   }
 
+  handleFormat = () => {
+    let _sql = this.state.value
+    
+    if (!_sql) return
+
+    let getuuid = () => {
+      let uuid = []
+      let _options = '0123456789abcdefghigklmnopqrstuv'
+      for (let i = 0; i < 19; i++) {
+        uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
+      }
+      return '\'' + uuid.join('') + '\''
+    }
+
+    let arr = []
+    _sql = _sql.replace(/@[0-9a-zA-Z_]+@/g, (word) => {
+      let uuid = getuuid()
+      arr.push({id: uuid, value: word})
+      return uuid
+    })
+
+    _sql = sqlFormatter.format(_sql.replace(/\s{2,}/g, ' '))
+    // _sql = _sql.replace(/case\n\s+when\s/ig, (word) => {
+    //   return word.replace(/case/, '').replace(/when/, 'case when')
+    // })
+    
+    _sql = _sql.replace(/\$\s\*\s\//g, '$*/').replace(/\*\s\//g, '*/')
+
+    arr.forEach(item => {
+      _sql = _sql.replace(item.id, item.value)
+    })
+
+    this.setState({display: false, defaultVal: _sql}, () => {
+      this.setState({display: true})
+    })
+    
+    this.props.onChange(_sql)
+  }
+
   render() {
+    const { mode } = this.props
     const { defaultVal, options, fullScreen, style, display } = this.state
     const menu = (
       <Menu>
@@ -149,6 +191,7 @@
 
     return (
       <div className="code-mirror-wrap" style={fullScreen ? style : null}>
+        {!mode && !fullScreen ? <FormatPainterOutlined onClick={this.handleFormat}/> : null}
         {!fullScreen ? <FullscreenOutlined onClick={this.fullScreenChange}/> : null}
         {fullScreen ? <FullscreenExitOutlined onClick={this.fullScreenChange}/> : null}
         {fullScreen ? <Dropdown overlay={menu} placement="bottomRight">
@@ -158,8 +201,9 @@
           className="code-mirror-area"
           value={defaultVal}
           options={options}
+          editorDidMount={editor => { this.editor = editor }}
           onChange={(editor, data, value) => {
-            this.setState({editor, value})
+            this.setState({value})
             this.props.onChange(value)
           }}
         /> : null}
diff --git a/src/templates/zshare/codemirror/index.scss b/src/templates/zshare/codemirror/index.scss
index 9b80e11..6444614 100644
--- a/src/templates/zshare/codemirror/index.scss
+++ b/src/templates/zshare/codemirror/index.scss
@@ -2,6 +2,17 @@
   font-size : 14px;
   line-height : 25px;
   position: relative;
+  .anticon-format-painter {
+    position: absolute;
+    z-index: 10;
+    right: 10px;
+    top: 35px;
+    font-size: 18px;
+    opacity: 0.7;
+    color: orange;
+    cursor: pointer;
+    transition: opacity 0.3s;
+  }
   .anticon-fullscreen {
     position: absolute;
     z-index: 10;
diff --git a/src/templates/zshare/customscript/index.jsx b/src/templates/zshare/customscript/index.jsx
index bd962d5..bdd9c1d 100644
--- a/src/templates/zshare/customscript/index.jsx
+++ b/src/templates/zshare/customscript/index.jsx
@@ -422,7 +422,7 @@
               <Form.Item label={'sql'}>
                 {getFieldDecorator('sql', {
                   initialValue: ''
-                })(<CodeMirror />)}
+                })(<CodeMirror/>)}
               </Form.Item>
             </Col>
           </Row>
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 4065d94..ecbc867 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -67,6 +67,7 @@
       key: 'sysInterface',
       label: '绯荤粺鎺ュ彛',
       initVal: setting.sysInterface || 'false',
+      tooltip: '鍗曠偣鐧诲綍绯荤粺',
       required: false,
       readonly: false,
       options: [
@@ -1118,6 +1119,7 @@
       key: 'sysInterface',
       label: '绯荤粺鎺ュ彛',
       initVal: card.sysInterface || 'false',
+      tooltip: '鍗曠偣鐧诲綍绯荤粺',
       required: true,
       options: [{
         value: 'true',
@@ -2379,7 +2381,7 @@
  * @param {*} linkableFields  // 鍙叧鑱旇〃鍗�
  * @param {*} linksupFields   // 涓婄骇琛ㄥ崟
  */
-export function getModalForm (card, inputfields = [], tabfields = [], linkableFields, linksupFields) {
+export function getModalForm (card, inputfields = [], tabfields = [], linkableFields, linksupFields, columns = []) {
   let appType = sessionStorage.getItem('appType')
   let roleList = sessionStorage.getItem('sysRoles')
   if (roleList) {
@@ -2578,7 +2580,8 @@
       label: '瀛楁',
       initVal: card.field || '',
       required: true,
-      readonly: false
+      readonly: false,
+      options: columns
     },
     {
       type: 'select',
diff --git a/src/templates/zshare/modalform/index.jsx b/src/templates/zshare/modalform/index.jsx
index 7b0eb1f..b4354cc 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { fromJS } from 'immutable'
-import { Form, Row, Col, Input, Select, Radio, notification, InputNumber, Tooltip, Checkbox } from 'antd'
+import { Form, Row, Col, Input, Select, Radio, notification, InputNumber, Tooltip, Checkbox, AutoComplete } from 'antd'
 import { QuestionCircleOutlined } from '@ant-design/icons'
 
 import { formRule } from '@/utils/option.js'
@@ -565,6 +565,12 @@
     this.props.form.setFieldsValue({dataSource: resource})
   }
 
+  complete = (key, option) => {
+    let label = option.props.label
+
+    this.props.form.setFieldsValue({label: label})
+  }
+
   getFields() {
     const { getFieldDecorator } = this.props.form
     const { transfield } = this.state
@@ -613,7 +619,20 @@
           })
         }
 
-        content = <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />
+        if (item.key === 'field' && item.options && item.options.length > 0) {
+          content = <AutoComplete
+            dataSource={item.options.map((cell) => <AutoComplete.Option label={cell.label} value={cell.field} key={cell.uuid}>
+              {cell.field}
+            </AutoComplete.Option>)}
+            filterOption={(input, option) => option.props.children.indexOf(input) > -1}
+            onSelect={this.complete}
+            placeholder=""
+          >
+            <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />
+          </AutoComplete>
+        } else {
+          content = <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />
+        }
       } else if (item.type === 'number') {
         rules = [
           { required: item.required, message: '璇疯緭鍏�' + item.label + '!' }
diff --git a/src/templates/zshare/unattended/index.jsx b/src/templates/zshare/unattended/index.jsx
index 4b6b8f5..abb8018 100644
--- a/src/templates/zshare/unattended/index.jsx
+++ b/src/templates/zshare/unattended/index.jsx
@@ -27,12 +27,20 @@
     const { config } = this.props
     let actions = []
 
-    config.action.forEach(item => {
-      if (item.position !== 'toolbar') return
-      if (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print')) {
-        actions.push(item)
-      }
-    })
+    if (config.components) {
+      config.components[0].action.forEach(item => {
+        if (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print')) {
+          actions.push(item)
+        }
+      })
+    } else {
+      config.action.forEach(item => {
+        if (item.position !== 'toolbar') return
+        if (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print')) {
+          actions.push(item)
+        }
+      })
+    }
 
     this.setState({
       actions,
@@ -48,6 +56,7 @@
       this.setState({
         visible: false
       })
+
       this.props.updateConfig({...config, autoMatic: res})
     })
   }
diff --git a/src/templates/zshare/verifycard/baseform/index.jsx b/src/templates/zshare/verifycard/baseform/index.jsx
index 19fdbf9..a0f5892 100644
--- a/src/templates/zshare/verifycard/baseform/index.jsx
+++ b/src/templates/zshare/verifycard/baseform/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { fromJS } from 'immutable'
-import { Form, Row, Col, Select, Radio, Tooltip, Input } from 'antd'
+import { Form, Row, Col, Select, Radio, Tooltip, Input, notification } from 'antd'
 import { QuestionCircleOutlined } from '@ant-design/icons'
 
 import Api from '@/api'
@@ -107,7 +107,7 @@
   }
 
   onOptionChange = (value, key) => {
-    const { verify } = this.props
+    const { verify, setting } = this.props
 
     let _verify = {...verify, [key]: value}
 
@@ -131,6 +131,15 @@
       }
     }
 
+    if (verify.invalid !== 'true' && _verify.invalid === 'true' && setting.maxScript && setting.maxScript >= 300) {
+      notification.warning({
+        top: 92,
+        message: '鏁版嵁婧愪腑鑷畾涔夎剼鏈繃浜庡鏉傦紝涓嶈兘浣跨敤澶辨晥楠岃瘉锛�',
+        duration: 5
+      })
+      return
+    }
+
     this.props.onChange(_verify)
   }
 
diff --git a/src/templates/zshare/verifycard/callbackcustomscript/index.jsx b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
index e702c44..d2b8119 100644
--- a/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
+++ b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
@@ -150,6 +150,7 @@
 
         console.info(`/* sql 楠岃瘉 */\n${param.LText.replace(/\n\s{6,20}/ig, '\n')}`)
 
+        param.LText = param.LText.replace(/\n/g, ' ')
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
         
diff --git a/src/templates/zshare/verifycard/customform/index.jsx b/src/templates/zshare/verifycard/customform/index.jsx
index 8938b1c..be0e47b 100644
--- a/src/templates/zshare/verifycard/customform/index.jsx
+++ b/src/templates/zshare/verifycard/customform/index.jsx
@@ -49,6 +49,15 @@
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     this.props.form.validateFieldsAndScroll((err, values) => {
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
+        
         values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
         values.resultName = values.result === 'false' ? '涓嶅瓨鍦�' : '瀛樺湪'
 
@@ -117,7 +126,8 @@
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         param.LText = param.LText.replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
-
+        param.LText = param.LText.replace(/\n/g, ' ')
+        
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
         
diff --git a/src/templates/zshare/verifycard/customscript/index.jsx b/src/templates/zshare/verifycard/customscript/index.jsx
index 6535615..944c203 100644
--- a/src/templates/zshare/verifycard/customscript/index.jsx
+++ b/src/templates/zshare/verifycard/customscript/index.jsx
@@ -57,6 +57,14 @@
         return
       }
       if (!err) {
+        if (/^[\s\n]+$/.test(values.sql)) {
+          notification.warning({
+            top: 92,
+            message: '璇疯緭鍏ql!',
+            duration: 5
+          })
+          return
+        }
         values.uuid = editItem ? editItem.uuid : ''
         values.position = values.position || (editItem ? editItem.position : 'front')
 
@@ -176,10 +184,11 @@
         }
 
         param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-        param.LText = param.LText.replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id)@/ig, `'${param.timestamp}'`)
+        param.LText = param.LText.replace(/@(BID|ID|LoginUID|SessionUid|UserID|Appkey|time_id|typename)@/ig, `'${param.timestamp}'`)
 
         console.info(`/* sql 楠岃瘉 */\n${param.LText.replace(/\n\s{6,20}/ig, '\n')}`)
 
+        param.LText = param.LText.replace(/\n/g, ' ')
         param.LText = Utils.formatOptions(param.LText)
         param.secretkey = Utils.encrypt('', param.timestamp)
         
@@ -272,7 +281,7 @@
           </Col> : null}
           {!_type ? <Col span={24} className="sqlfield">
             <Form.Item label={'鍙敤瀛楁'}>
-              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'鍏叡鍊硷紝璇锋寜鐓xxx@鏍煎紡浣跨敤銆�'}><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id</span></Tooltip>,&nbsp;
+              <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'鍏叡鍊硷紝璇锋寜鐓xxx@鏍煎紡浣跨敤銆�'}><span style={{color: '#1890ff'}}>BID, ID, LoginUID, SessionUid, UserID, Appkey, time_id, typename</span></Tooltip>,&nbsp;
               <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'绯荤粺鍙橀噺锛岀郴缁熶細瀹氫箟鍙橀噺骞惰祴鍊笺��'}><span style={{color: '#fa8c16'}}>UserName, FullName, RoleID, mk_departmentcode, mk_organization, mk_user_type, mk_nation, mk_province, mk_city, mk_district, mk_address</span></Tooltip>,&nbsp;
               <Tooltip mouseLeaveDelay={0.3} mouseEnterDelay={0.3} placement="top" title={'绯荤粺鍙橀噺锛岀郴缁熶細瀹氫箟鍙橀噺骞跺湪鍗曞彿鐢熸垚鎴栧垱寤哄嚟璇佹椂浣跨敤銆�'}><span style={{color: '#13c2c2'}}>BillCode, BVoucher, FIBVoucherDate, FiYear, ModularDetailCode</span></Tooltip>
               {usefulfields ? <span>, {usefulfields}</span> : ''}
diff --git a/src/templates/zshare/verifycard/index.jsx b/src/templates/zshare/verifycard/index.jsx
index 86ece6e..c198cbb 100644
--- a/src/templates/zshare/verifycard/index.jsx
+++ b/src/templates/zshare/verifycard/index.jsx
@@ -827,6 +827,10 @@
           keys.push('bid')
           values.push('@BID@')
         }
+        if (!keys.includes('typename')) {
+          keys.push('typename')
+          values.push('@typename@')
+        }
   
         keys = keys.join(', ')
         values = values.join(', ')
@@ -861,6 +865,9 @@
           if (!_arr.includes('submituserid')) {
             _form.push('submituserid=@userid@')
           }
+          if (!_arr.includes('typename')) {
+            _form.push(`typename=@typename@`)
+          }
         } else {
           if (!_arr.includes('modifydate')) {
             _form.push('modifydate=getdate()')
@@ -873,6 +880,9 @@
           }
           if (!_arr.includes('modifyuserid')) {
             _form.push('modifyuserid=@userid@')
+          }
+          if (!_arr.includes('typename')) {
+            _form.push(`typename=@typename@`)
           }
         }
 
@@ -925,7 +935,7 @@
             _index++
           })
         }
-        _defaultsql += `insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${card.sql} 鏁版嵁: ${_msg}${_primaryKey}='+@ID@,200),@userid@,@username,@fullname delete ${card.sql} where ${_primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
+        _defaultsql += `insert into snote (remark,createuserid,CreateUser,CreateStaff,typename) select left('鍒犻櫎琛�:${card.sql} 鏁版嵁: ${_msg}${_primaryKey}='+@ID@,200),@userid@,@username,@fullname,@typename@ delete ${card.sql} where ${_primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
       }
 
       let _columns = []
@@ -1501,7 +1511,7 @@
 
   render() {
     const { card } = this.props
-    const { activeKey, verifyInter, verify, fields, visible, uniqueFields, uniqueColumns, unionFields, onceUniqueColumns, columnsFields, contrastColumns, customColumns, orderColumns, scriptsColumns, cbScriptsColumns, orderModular, orderModularDetail, voucher, voucherDetail, notes, appType } = this.state
+    const { activeKey, verifyInter, setting, verify, fields, visible, uniqueFields, uniqueColumns, unionFields, onceUniqueColumns, columnsFields, contrastColumns, customColumns, orderColumns, scriptsColumns, cbScriptsColumns, orderModular, orderModularDetail, voucher, voucherDetail, notes, appType } = this.state
     const formItemLayout = {
       labelCol: {
         xs: { span: 24 },
@@ -1523,7 +1533,7 @@
               {verify.default === 'false' ? <span className="count-tip"><ExclamationOutlined style={{color: 'orange'}}/></span> : null}
             </span>
           } key="base">
-            <BaseForm card={card} unionFields={unionFields} verify={verify} notes={notes} onChange={(verify) => this.setState({verify})} wrappedComponentRef={(inst) => this.baseForm = inst}/>
+            <BaseForm card={card} unionFields={unionFields} setting={setting} verify={verify} notes={notes} onChange={(verify) => this.setState({verify})} wrappedComponentRef={(inst) => this.baseForm = inst}/>
           </TabPane> : null}
           {verifyInter === 'system' ? <TabPane tab={
             <span>
@@ -1753,37 +1763,51 @@
                 position = <span style={{color: '#1890ff'}}>sql鍚�</span>
               }
 
-              return (
-                <div className={'script-item ' + (this.state.scriptId === item.uuid ? 'active' : '') } key={item.uuid}>
-                  <div style={{cursor: 'pointer'}} onClick={() => {
-                    this.scriptsFullForm.edit(item)
-                    this.setState({scriptId: item.uuid})
-                  }}>
-                    {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
-                    <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
-                    <div>{position}{item.status === 'false' ?
-                      <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
-                        绂佺敤
-                        <StopOutlined style={{marginLeft: '5px'}} />
-                      </span> : 
-                      <span style={{color: '#26C281', marginLeft: '20px'}}>
-                        鍚敤
-                        <CheckCircleOutlined style={{marginLeft: '5px'}}/>
-                      </span>}
+              if (item.status === 'false') {
+                return (
+                  <div className="script-item" key={item.uuid}>
+                    <div style={{cursor: 'not-allowed'}}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>{position}
+                        <span style={{color: '#ff4d4f', marginLeft: '20px'}}>
+                          绂佺敤
+                          <StopOutlined style={{marginLeft: '5px'}} />
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{height: '24px'}}></div>
+                  </div>
+                )
+              } else {
+                return (
+                  <div className={'script-item ' + (this.state.scriptId === item.uuid ? 'active' : '') } key={item.uuid}>
+                    <div style={{cursor: 'pointer'}} onClick={() => {
+                      this.scriptsFullForm.edit(item)
+                      this.setState({scriptId: item.uuid})
+                    }}>
+                      {title ? <div style={{color: '#a50', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis'}}>{title}</div> : null}
+                      <Paragraph copyable={{ text: item.sql }} ellipsis={{ rows: 4 }}>{_text}</Paragraph>
+                      <div>{position}
+                        <span style={{color: '#26C281', marginLeft: '20px'}}>
+                          鍚敤
+                          <CheckCircleOutlined style={{marginLeft: '5px'}}/>
+                        </span>
+                      </div>
+                    </div>
+                    <div style={{textAlign: 'right'}}>
+                      <span className="operation-btn" onClick={() => this.handleStatus(item, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
+                      <Popconfirm
+                        overlayClassName="popover-confirm"
+                        title="纭畾鍒犻櫎鍚�?"
+                        onConfirm={() => this.handleDelete(item, 'scripts')
+                      }>
+                        <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
+                      </Popconfirm>
                     </div>
                   </div>
-                  <div style={{textAlign: 'right'}}>
-                    <span className="operation-btn" onClick={() => this.handleStatus(item, 'scripts')} style={{color: '#8E44AD'}}><SwapOutlined /></span>
-                    <Popconfirm
-                      overlayClassName="popover-confirm"
-                      title="纭畾鍒犻櫎鍚�?"
-                      onConfirm={() => this.handleDelete(item, 'scripts')
-                    }>
-                      <span className="operation-btn" style={{color: '#ff4d4f'}}><DeleteOutlined /></span>
-                    </Popconfirm>
-                  </div>
-                </div>
-              )
+                )
+              }
             })}
           </div>
           <CustomScript
diff --git a/src/utils/asyncComponent.jsx b/src/utils/asyncComponent.jsx
index b137307..4e679f2 100644
--- a/src/utils/asyncComponent.jsx
+++ b/src/utils/asyncComponent.jsx
@@ -20,6 +20,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
 
diff --git a/src/utils/asyncIconComponent.jsx b/src/utils/asyncIconComponent.jsx
index 5fe6c82..2624be9 100644
--- a/src/utils/asyncIconComponent.jsx
+++ b/src/utils/asyncIconComponent.jsx
@@ -21,6 +21,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
 
diff --git a/src/utils/asyncLoadComponent.jsx b/src/utils/asyncLoadComponent.jsx
index 9836222..2d1bcd3 100644
--- a/src/utils/asyncLoadComponent.jsx
+++ b/src/utils/asyncLoadComponent.jsx
@@ -21,6 +21,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
 
diff --git a/src/utils/asyncSpinComponent.jsx b/src/utils/asyncSpinComponent.jsx
index 8642bf8..336ae9f 100644
--- a/src/utils/asyncSpinComponent.jsx
+++ b/src/utils/asyncSpinComponent.jsx
@@ -21,6 +21,12 @@
       this.setState({component})
     }
 
+    componentWillUnmount () {
+      this.setState = () => {
+        return
+      }
+    }
+
     render() {
       const C = this.state.component
 
diff --git a/src/utils/option.js b/src/utils/option.js
index fac69e0..ca6a857 100644
--- a/src/utils/option.js
+++ b/src/utils/option.js
@@ -45,20 +45,20 @@
 
 // 绯荤粺妯℃澘
 export const sysTemps = [
-  {
-    title: '鍩虹琛ㄦ牸',
-    type: 'CommonTable',
-    url: nortable,
-    baseconfig: '',
-    isSystem: true
-  },
   // {
-  //   title: '鍩虹琛ㄦ牸锛堟柊锛�',
-  //   type: 'BaseTable',
+  //   title: '鍩虹琛ㄦ牸',
+  //   type: 'CommonTable',
   //   url: nortable,
   //   baseconfig: '',
   //   isSystem: true
   // },
+  {
+    title: '鍩虹琛ㄦ牸',
+    type: 'BaseTable',
+    url: nortable,
+    baseconfig: '',
+    isSystem: true
+  },
   // {
   //   title: '涓诲瓙琛ㄨ〃鏍�',
   //   type: 'CommonTable',
diff --git a/src/utils/sqlFormatter-origin.js b/src/utils/sqlFormatter-origin.js
new file mode 100644
index 0000000..a4c7658
--- /dev/null
+++ b/src/utils/sqlFormatter-origin.js
@@ -0,0 +1,3381 @@
+/* eslint-disable */
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["sqlFormatter"] = factory();
+	else
+		root["sqlFormatter"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+ 
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+ 
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId])
+/******/ 			return installedModules[moduleId].exports;
+ 
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			exports: {},
+/******/ 			id: moduleId,
+/******/ 			loaded: false
+/******/ 		};
+ 
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+ 
+/******/ 		// Flag the module as loaded
+/******/ 		module.loaded = true;
+ 
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+ 
+ 
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+ 
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+ 
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+ 
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Db2Formatter = __webpack_require__(24);
+ 
+	var _Db2Formatter2 = _interopRequireDefault(_Db2Formatter);
+ 
+	var _N1qlFormatter = __webpack_require__(25);
+ 
+	var _N1qlFormatter2 = _interopRequireDefault(_N1qlFormatter);
+ 
+	var _PlSqlFormatter = __webpack_require__(26);
+ 
+	var _PlSqlFormatter2 = _interopRequireDefault(_PlSqlFormatter);
+ 
+	var _StandardSqlFormatter = __webpack_require__(27);
+ 
+	var _StandardSqlFormatter2 = _interopRequireDefault(_StandardSqlFormatter);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	exports["default"] = {
+	    /**
+	     * Format whitespaces in a query to make it easier to read.
+	     *
+	     * @param {String} query
+	     * @param {Object} cfg
+	     *  @param {String} cfg.language Query language, default is Standard SQL
+	     *  @param {String} cfg.indent Characters used for indentation, default is "  " (2 spaces)
+	     *  @param {Object} cfg.params Collection of params for placeholder replacement
+	     * @return {String}
+	     */
+	    format: function format(query, cfg) {
+	        cfg = cfg || {};
+ 
+	        switch (cfg.language) {
+	            case "db2":
+	                return new _Db2Formatter2["default"](cfg).format(query);
+	            case "n1ql":
+	                return new _N1qlFormatter2["default"](cfg).format(query);
+	            case "pl/sql":
+	                return new _PlSqlFormatter2["default"](cfg).format(query);
+	            case "sql":
+	            case undefined:
+	                return new _StandardSqlFormatter2["default"](cfg).format(query);
+	            default:
+	                throw Error("Unsupported SQL dialect: " + cfg.language);
+	        }
+	    }
+	};
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var freeGlobal = __webpack_require__(12);
+ 
+	/** Detect free variable `self`. */
+	var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+ 
+	/** Used as a reference to the global object. */
+	var root = freeGlobal || freeSelf || Function('return this')();
+ 
+	module.exports = root;
+ 
+ 
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9),
+	    getRawTag = __webpack_require__(48),
+	    objectToString = __webpack_require__(57);
+ 
+	/** `Object#toString` result references. */
+	var nullTag = '[object Null]',
+	    undefinedTag = '[object Undefined]';
+ 
+	/** Built-in value references. */
+	var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+ 
+	/**
+	 * The base implementation of `getTag` without fallbacks for buggy environments.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the `toStringTag`.
+	 */
+	function baseGetTag(value) {
+	  if (value == null) {
+	    return value === undefined ? undefinedTag : nullTag;
+	  }
+	  return (symToStringTag && symToStringTag in Object(value))
+	    ? getRawTag(value)
+	    : objectToString(value);
+	}
+ 
+	module.exports = baseGetTag;
+ 
+ 
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsNative = __webpack_require__(39),
+	    getValue = __webpack_require__(50);
+ 
+	/**
+	 * Gets the native function at `key` of `object`.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @param {string} key The key of the method to get.
+	 * @returns {*} Returns the function if it's native, else `undefined`.
+	 */
+	function getNative(object, key) {
+	  var value = getValue(object, key);
+	  return baseIsNative(value) ? value : undefined;
+	}
+ 
+	module.exports = getNative;
+ 
+ 
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _trimEnd = __webpack_require__(74);
+ 
+	var _trimEnd2 = _interopRequireDefault(_trimEnd);
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	var _Indentation = __webpack_require__(21);
+ 
+	var _Indentation2 = _interopRequireDefault(_Indentation);
+ 
+	var _InlineBlock = __webpack_require__(22);
+ 
+	var _InlineBlock2 = _interopRequireDefault(_InlineBlock);
+ 
+	var _Params = __webpack_require__(23);
+ 
+	var _Params2 = _interopRequireDefault(_Params);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var Formatter = function () {
+	    /**
+	     * @param {Object} cfg
+	     *   @param {Object} cfg.indent
+	     *   @param {Object} cfg.params
+	     * @param {Tokenizer} tokenizer
+	     */
+	    function Formatter(cfg, tokenizer) {
+	        _classCallCheck(this, Formatter);
+ 
+	        this.cfg = cfg || {};
+	        this.indentation = new _Indentation2["default"](this.cfg.indent);
+	        this.inlineBlock = new _InlineBlock2["default"]();
+	        this.params = new _Params2["default"](this.cfg.params);
+	        this.tokenizer = tokenizer;
+	        this.previousReservedWord = {};
+	        this.tokens = [];
+	        this.index = 0;
+	    }
+ 
+	    /**
+	     * Formats whitespaces in a SQL string to make it easier to read.
+	     *
+	     * @param {String} query The SQL query string
+	     * @return {String} formatted query
+	     */
+ 
+ 
+	    Formatter.prototype.format = function format(query) {
+	        this.tokens = this.tokenizer.tokenize(query);
+	        var formattedQuery = this.getFormattedQueryFromTokens();
+	        return formattedQuery.trim();
+	    };
+ 
+	    Formatter.prototype.getFormattedQueryFromTokens = function getFormattedQueryFromTokens() {
+	        var _this = this;
+	        var formattedQuery = "";
+	        this.tokens.forEach(function (token, index) {
+	            _this.index = index;
+	            if (token.type === _tokenTypes2["default"].WHITESPACE) {
+	                // ignore (we do our own whitespace formatting)
+	            } else if (token.type === _tokenTypes2["default"].LINE_COMMENT) {
+	                formattedQuery = _this.formatLineComment(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].BLOCK_COMMENT) {
+	                formattedQuery = _this.formatBlockComment(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].RESERVED_TOPLEVEL) {
+	                formattedQuery = _this.formatToplevelReservedWord(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            } else if (token.type === _tokenTypes2["default"].RESERVED_NEWLINE) {
+	                formattedQuery = _this.formatNewlineReservedWord(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            }else if (token.type === _tokenTypes2["default"].RESERVED) {
+	                formattedQuery = _this.formatWithSpaces(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            } else if (token.type === _tokenTypes2["default"].OPEN_PAREN) {
+	                formattedQuery = _this.formatOpeningParentheses(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].CLOSE_PAREN) {
+	                formattedQuery = _this.formatClosingParentheses(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].PLACEHOLDER) {
+	                formattedQuery = _this.formatPlaceholder(token, formattedQuery);
+	            }   else if (token.value === '$') {
+	                formattedQuery = _this.formatNewWithSpaces(token, formattedQuery);
+	            } else if (token.value === ",") {
+	                formattedQuery = _this.formatComma(token, formattedQuery);
+	            } else if (token.value === ":") {
+	                formattedQuery = _this.formatWithSpaceAfter(token, formattedQuery);
+	            } else if (token.value === ".") {
+	                formattedQuery = _this.formatWithoutSpaces(token, formattedQuery);
+	            } else if (token.value === ";") {
+	                formattedQuery = _this.formatQuerySeparator(token, formattedQuery);
+	            } else {
+	                formattedQuery = _this.formatWithSpaces(token, formattedQuery);
+	            }
+	        });
+	        return formattedQuery;
+	    };
+ 
+	    Formatter.prototype.formatLineComment = function formatLineComment(token, query) {
+	        return this.addNewline(query + token.value);
+	    };
+ 
+	    Formatter.prototype.formatBlockComment = function formatBlockComment(token, query) {
+	        return this.addNewline(this.addNewline(query) + this.indentComment(token.value));
+	    };
+ 
+	    Formatter.prototype.indentComment = function indentComment(comment) {
+	        return comment.replace(/\n/g, "\n" + this.indentation.getIndent());
+	    };
+ 
+	    Formatter.prototype.formatToplevelReservedWord = function formatToplevelReservedWord(token, query) {
+	        this.indentation.decreaseTopLevel();
+	        query = this.addNewline(query);
+ 
+					this.indentation.increaseToplevel();
+					// 椤剁骇涓嶅姞绌烘牸
+					// query += " " + this.equalizeWhitespace(token.value);
+	        query += this.equalizeWhitespace(token.value);
+			// return this.addNewline(query);
+			if (token.type === _tokenTypes2["default"].RESERVED_TOPLEVEL) {
+				return this.addNewLineReserved(query);
+			} else {
+				return this.addNewline(query);
+			}
+	    };
+ 
+	    Formatter.prototype.formatNewlineReservedWord = function formatNewlineReservedWord(token, query) {
+	        return this.addNewline(query) + this.equalizeWhitespace(token.value) + " ";
+	    };
+ 
+	    // Replace any sequence of whitespace characters with single space
+ 
+ 
+	    Formatter.prototype.equalizeWhitespace = function equalizeWhitespace(string) {
+	        return string.replace(/\s+/g, " ");
+	    };
+ 
+	    // Opening parentheses increase the block indent level and start a new line
+ 
+ 
+	    Formatter.prototype.formatOpeningParentheses = function formatOpeningParentheses(token, query) {
+	        // Take out the preceding space unless there was whitespace there in the original query
+	        // or another opening parens or line comment
+	        var preserveWhitespaceFor = [_tokenTypes2["default"].WHITESPACE, _tokenTypes2["default"].OPEN_PAREN, _tokenTypes2["default"].LINE_COMMENT];
+	        if (!preserveWhitespaceFor.includes(this.previousToken().type)) {
+	            query = (0, _trimEnd2["default"])(query);
+	        }
+	        query += token.value;
+ 
+	        this.inlineBlock.beginIfPossible(this.tokens, this.index);
+ 
+	        if (!this.inlineBlock.isActive()) {
+	            this.indentation.increaseBlockLevel();
+	            query = this.addNewline(query);
+	        }
+	        return query;
+	    };
+ 
+	    // Closing parentheses decrease the block indent level
+ 
+ 
+	    Formatter.prototype.formatClosingParentheses = function formatClosingParentheses(token, query) {
+	        if (this.inlineBlock.isActive()) {
+	            this.inlineBlock.end();
+	            return this.formatWithSpaceAfter(token, query);
+	        } else {
+	            this.indentation.decreaseBlockLevel();
+	            return this.formatWithSpaces(token, this.addNewline(query));
+	        }
+	    };
+ 
+	    Formatter.prototype.formatPlaceholder = function formatPlaceholder(token, query) {
+	        return query + this.params.get(token) + " ";
+	    };
+ 
+	    // Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause)
+ 
+ 
+	    Formatter.prototype.formatComma = function formatComma(token, query) {
+	        query = this.trimTrailingWhitespace(query) + token.value + " ";
+ 
+	        if (this.inlineBlock.isActive()) {
+	            return query;
+	        } else if (/^LIMIT$/i.test(this.previousReservedWord.value)) {
+	            return query;
+	        } else {
+	            return this.addNewline(query);
+	        }
+	    };
+ 
+	    Formatter.prototype.formatWithSpaceAfter = function formatWithSpaceAfter(token, query) {
+	        return this.trimTrailingWhitespace(query) + token.value + " ";
+	    };
+ 
+	    Formatter.prototype.formatWithoutSpaces = function formatWithoutSpaces(token, query) {
+	        return this.trimTrailingWhitespace(query) + token.value;
+	    };
+ 
+	    Formatter.prototype.formatWithSpaces = function formatWithSpaces(token, query) {
+	        return query + token.value + " ";
+		};
+		// $绗﹀彿鍚庨潰涓庡叧閿瓧涔嬮棿涓嶅姞绌烘牸
+		Formatter.prototype.formatNewWithSpaces = function formatNewWithSpaces(token, query) {
+				return query + token.value;
+		};
+
+		Formatter.prototype.formatQuerySeparator = function formatQuerySeparator(token, query) {
+				return this.trimTrailingWhitespace(query) + token.value + "\n";
+		};
+
+		Formatter.prototype.addNewline = function addNewline(query) {
+				return (0, _trimEnd2["default"])(query) + "\n" + this.indentation.getIndent();
+		};
+		// 鍏抽敭瀛楀悗闈笉鐩存帴鎹㈣锛屽叧閿瓧鍚�2涓┖鏍�
+		Formatter.prototype.addNewLineReserved = function addNewLineReserved(query) {
+			return (0, _trimEnd2["default"])(query) + "  ";
+			// return (0, _trimEnd2["default"])(query) + this.indentation.getIndent();
+		};
+ 
+		Formatter.prototype.trimTrailingWhitespace = function trimTrailingWhitespace(query) {
+			if (this.previousNonWhitespaceToken().type === _tokenTypes2["default"].LINE_COMMENT) {
+					return (0, _trimEnd2["default"])(query) + "\n";
+			} else {
+					return (0, _trimEnd2["default"])(query);
+			}
+		};
+
+		Formatter.prototype.previousNonWhitespaceToken = function previousNonWhitespaceToken() {
+				var n = 1;
+				while (this.previousToken(n).type === _tokenTypes2["default"].WHITESPACE) {
+						n++;
+				}
+				return this.previousToken(n);
+		};
+
+		Formatter.prototype.previousToken = function previousToken() {
+				var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+
+				return this.tokens[this.index - offset] || {};
+		};
+
+		return Formatter;
+	}();
+ 
+	exports["default"] = Formatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _isEmpty = __webpack_require__(66);
+ 
+	var _isEmpty2 = _interopRequireDefault(_isEmpty);
+ 
+	var _escapeRegExp = __webpack_require__(63);
+ 
+	var _escapeRegExp2 = _interopRequireDefault(_escapeRegExp);
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var Tokenizer = function () {
+	    /**
+	     * @param {Object} cfg
+	     *  @param {String[]} cfg.reservedWords Reserved words in SQL
+	     *  @param {String[]} cfg.reservedToplevelWords Words that are set to new line separately
+	     *  @param {String[]} cfg.reservedNewlineWords Words that are set to newline
+	     *  @param {String[]} cfg.stringTypes String types to enable: "", '', ``, [], N''
+	     *  @param {String[]} cfg.openParens Opening parentheses to enable, like (, [
+	     *  @param {String[]} cfg.closeParens Closing parentheses to enable, like ), ]
+	     *  @param {String[]} cfg.indexedPlaceholderTypes Prefixes for indexed placeholders, like ?
+	     *  @param {String[]} cfg.namedPlaceholderTypes Prefixes for named placeholders, like @ and :
+	     *  @param {String[]} cfg.lineCommentTypes Line comments to enable, like # and --
+	     *  @param {String[]} cfg.specialWordChars Special chars that can be found inside of words, like @ and #
+	     */
+	    function Tokenizer(cfg) {
+	        _classCallCheck(this, Tokenizer);
+ 
+	        this.WHITESPACE_REGEX = /^(\s+)/;
+	        this.NUMBER_REGEX = /^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/;
+	        this.OPERATOR_REGEX = /^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|~~\*|~~|!~~\*|!~~|~\*|!~\*|!~|.)/;
+ 
+	        this.BLOCK_COMMENT_REGEX = /^(\/\*[^]*?(?:\*\/|$))/;
+	        this.LINE_COMMENT_REGEX = this.createLineCommentRegex(cfg.lineCommentTypes);
+ 
+	        this.RESERVED_TOPLEVEL_REGEX = this.createReservedWordRegex(cfg.reservedToplevelWords);
+	        this.RESERVED_NEWLINE_REGEX = this.createReservedWordRegex(cfg.reservedNewlineWords);
+	        this.RESERVED_PLAIN_REGEX = this.createReservedWordRegex(cfg.reservedWords);
+ 
+	        this.WORD_REGEX = this.createWordRegex(cfg.specialWordChars);
+	        this.STRING_REGEX = this.createStringRegex(cfg.stringTypes);
+ 
+	        this.OPEN_PAREN_REGEX = this.createParenRegex(cfg.openParens);
+	        this.CLOSE_PAREN_REGEX = this.createParenRegex(cfg.closeParens);
+ 
+	        this.INDEXED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.indexedPlaceholderTypes, "[0-9]*");
+	        this.IDENT_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, "[a-zA-Z0-9._$]+");
+	        this.STRING_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, this.createStringPattern(cfg.stringTypes));
+	    }
+ 
+	    Tokenizer.prototype.createLineCommentRegex = function createLineCommentRegex(lineCommentTypes) {
+	        return new RegExp("^((?:" + lineCommentTypes.map(function (c) {
+	            return (0, _escapeRegExp2["default"])(c);
+	        }).join("|") + ").*?(?:\n|$))");
+	    };
+ 
+	    Tokenizer.prototype.createReservedWordRegex = function createReservedWordRegex(reservedWords) {
+	        var reservedWordsPattern = reservedWords.join("|").replace(/ /g, "\\s+");
+	        return new RegExp("^(" + reservedWordsPattern + ")\\b", "i");
+	    };
+ 
+	    Tokenizer.prototype.createWordRegex = function createWordRegex() {
+	        var specialChars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+ 
+	        return new RegExp("^([\\w" + specialChars.join("") + "]+)");
+	    };
+ 
+	    Tokenizer.prototype.createStringRegex = function createStringRegex(stringTypes) {
+	        return new RegExp("^(" + this.createStringPattern(stringTypes) + ")");
+	    };
+ 
+	    // This enables the following string patterns:
+	    // 1. backtick quoted string using `` to escape
+	    // 2. square bracket quoted string (SQL Server) using ]] to escape
+	    // 3. double quoted string using "" or \" to escape
+	    // 4. single quoted string using '' or \' to escape
+	    // 5. national character quoted string using N'' or N\' to escape
+ 
+ 
+	    Tokenizer.prototype.createStringPattern = function createStringPattern(stringTypes) {
+	        var patterns = {
+	            "``": "((`[^`]*($|`))+)",
+	            "[]": "((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)",
+	            "\"\"": "((\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*(\"|$))+)",
+	            "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)",
+	            "N''": "((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)"
+	        };
+ 
+	        return stringTypes.map(function (t) {
+	            return patterns[t];
+	        }).join("|");
+	    };
+ 
+	    Tokenizer.prototype.createParenRegex = function createParenRegex(parens) {
+	        var _this = this;
+ 
+	        return new RegExp("^(" + parens.map(function (p) {
+	            return _this.escapeParen(p);
+	        }).join("|") + ")", "i");
+	    };
+ 
+	    Tokenizer.prototype.escapeParen = function escapeParen(paren) {
+	        if (paren.length === 1) {
+	            // A single punctuation character
+	            return (0, _escapeRegExp2["default"])(paren);
+	        } else {
+	            // longer word
+	            return "\\b" + paren + "\\b";
+	        }
+	    };
+ 
+	    Tokenizer.prototype.createPlaceholderRegex = function createPlaceholderRegex(types, pattern) {
+	        if ((0, _isEmpty2["default"])(types)) {
+	            return false;
+	        }
+	        var typesRegex = types.map(_escapeRegExp2["default"]).join("|");
+ 
+	        return new RegExp("^((?:" + typesRegex + ")(?:" + pattern + "))");
+	    };
+ 
+	    /**
+	     * Takes a SQL string and breaks it into tokens.
+	     * Each token is an object with type and value.
+	     *
+	     * @param {String} input The SQL string
+	     * @return {Object[]} tokens An array of tokens.
+	     *  @return {String} token.type
+	     *  @return {String} token.value
+	     */
+ 
+ 
+	    Tokenizer.prototype.tokenize = function tokenize(input) {
+	        var tokens = [];
+	        var token = void 0;
+ 
+	        // Keep processing the string until it is empty
+	        while (input.length) {
+	            // Get the next token and the token type
+	            token = this.getNextToken(input, token);
+	            // Advance the string
+	            input = input.substring(token.value.length);
+ 
+	            tokens.push(token);
+	        }
+	        return tokens;
+	    };
+ 
+	    Tokenizer.prototype.getNextToken = function getNextToken(input, previousToken) {
+	        return this.getWhitespaceToken(input) || this.getCommentToken(input) || this.getStringToken(input) || this.getOpenParenToken(input) || this.getCloseParenToken(input) || this.getPlaceholderToken(input) || this.getNumberToken(input) || this.getReservedWordToken(input, previousToken) || this.getWordToken(input) || this.getOperatorToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getWhitespaceToken = function getWhitespaceToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].WHITESPACE,
+	            regex: this.WHITESPACE_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getCommentToken = function getCommentToken(input) {
+	        return this.getLineCommentToken(input) || this.getBlockCommentToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getLineCommentToken = function getLineCommentToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].LINE_COMMENT,
+	            regex: this.LINE_COMMENT_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getBlockCommentToken = function getBlockCommentToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].BLOCK_COMMENT,
+	            regex: this.BLOCK_COMMENT_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getStringToken = function getStringToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].STRING,
+	            regex: this.STRING_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getOpenParenToken = function getOpenParenToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].OPEN_PAREN,
+	            regex: this.OPEN_PAREN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getCloseParenToken = function getCloseParenToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].CLOSE_PAREN,
+	            regex: this.CLOSE_PAREN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlaceholderToken = function getPlaceholderToken(input) {
+	        return this.getIdentNamedPlaceholderToken(input) || this.getStringNamedPlaceholderToken(input) || this.getIndexedPlaceholderToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getIdentNamedPlaceholderToken = function getIdentNamedPlaceholderToken(input) {
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.IDENT_NAMED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return v.slice(1);
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getStringNamedPlaceholderToken = function getStringNamedPlaceholderToken(input) {
+	        var _this2 = this;
+ 
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.STRING_NAMED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return _this2.getEscapedPlaceholderKey({ key: v.slice(2, -1), quoteChar: v.slice(-1) });
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getIndexedPlaceholderToken = function getIndexedPlaceholderToken(input) {
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.INDEXED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return v.slice(1);
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlaceholderTokenWithKey = function getPlaceholderTokenWithKey(_ref) {
+	        var input = _ref.input,
+	            regex = _ref.regex,
+	            parseKey = _ref.parseKey;
+ 
+	        var token = this.getTokenOnFirstMatch({ input: input, regex: regex, type: _tokenTypes2["default"].PLACEHOLDER });
+	        if (token) {
+	            token.key = parseKey(token.value);
+	        }
+	        return token;
+	    };
+ 
+	    Tokenizer.prototype.getEscapedPlaceholderKey = function getEscapedPlaceholderKey(_ref2) {
+	        var key = _ref2.key,
+	            quoteChar = _ref2.quoteChar;
+ 
+	        return key.replace(new RegExp((0, _escapeRegExp2["default"])("\\") + quoteChar, "g"), quoteChar);
+	    };
+ 
+	    // Decimal, binary, or hex numbers
+ 
+ 
+	    Tokenizer.prototype.getNumberToken = function getNumberToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].NUMBER,
+	            regex: this.NUMBER_REGEX
+	        });
+	    };
+ 
+	    // Punctuation and symbols
+ 
+ 
+	    Tokenizer.prototype.getOperatorToken = function getOperatorToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].OPERATOR,
+	            regex: this.OPERATOR_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getReservedWordToken = function getReservedWordToken(input, previousToken) {
+	        // A reserved word cannot be preceded by a "."
+	        // this makes it so in "mytable.from", "from" is not considered a reserved word
+	        if (previousToken && previousToken.value && previousToken.value === ".") {
+	            return;
+	        }
+	        return this.getToplevelReservedToken(input) || this.getNewlineReservedToken(input) || this.getPlainReservedToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getToplevelReservedToken = function getToplevelReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED_TOPLEVEL,
+	            regex: this.RESERVED_TOPLEVEL_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getNewlineReservedToken = function getNewlineReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED_NEWLINE,
+	            regex: this.RESERVED_NEWLINE_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlainReservedToken = function getPlainReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED,
+	            regex: this.RESERVED_PLAIN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getWordToken = function getWordToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].WORD,
+	            regex: this.WORD_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getTokenOnFirstMatch = function getTokenOnFirstMatch(_ref3) {
+	        var input = _ref3.input,
+	            type = _ref3.type,
+	            regex = _ref3.regex;
+ 
+	        var matches = input.match(regex);
+ 
+	        if (matches) {
+	            return { type: type, value: matches[1] };
+	        }
+	    };
+ 
+	    return Tokenizer;
+	}();
+ 
+	exports["default"] = Tokenizer;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 6 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is the
+	 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+	 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+	 * @example
+	 *
+	 * _.isObject({});
+	 * // => true
+	 *
+	 * _.isObject([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isObject(_.noop);
+	 * // => true
+	 *
+	 * _.isObject(null);
+	 * // => false
+	 */
+	function isObject(value) {
+	  var type = typeof value;
+	  return value != null && (type == 'object' || type == 'function');
+	}
+ 
+	module.exports = isObject;
+ 
+ 
+/***/ }),
+/* 7 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is object-like. A value is object-like if it's not `null`
+	 * and has a `typeof` result of "object".
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+	 * @example
+	 *
+	 * _.isObjectLike({});
+	 * // => true
+	 *
+	 * _.isObjectLike([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isObjectLike(_.noop);
+	 * // => false
+	 *
+	 * _.isObjectLike(null);
+	 * // => false
+	 */
+	function isObjectLike(value) {
+	  return value != null && typeof value == 'object';
+	}
+ 
+	module.exports = isObjectLike;
+ 
+ 
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+	/**
+	 * Constants for token types
+	 */
+	exports["default"] = {
+	    WHITESPACE: "whitespace",
+	    WORD: "word",
+	    STRING: "string",
+	    RESERVED: "reserved",
+	    RESERVED_TOPLEVEL: "reserved-toplevel",
+	    RESERVED_NEWLINE: "reserved-newline",
+	    OPERATOR: "operator",
+	    OPEN_PAREN: "open-paren",
+	    CLOSE_PAREN: "close-paren",
+	    LINE_COMMENT: "line-comment",
+	    BLOCK_COMMENT: "block-comment",
+	    NUMBER: "number",
+	    PLACEHOLDER: "placeholder"
+	};
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var root = __webpack_require__(1);
+ 
+	/** Built-in value references. */
+	var Symbol = root.Symbol;
+ 
+	module.exports = Symbol;
+ 
+ 
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseToString = __webpack_require__(11);
+ 
+	/**
+	 * Converts `value` to a string. An empty string is returned for `null`
+	 * and `undefined` values. The sign of `-0` is preserved.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {string} Returns the converted string.
+	 * @example
+	 *
+	 * _.toString(null);
+	 * // => ''
+	 *
+	 * _.toString(-0);
+	 * // => '-0'
+	 *
+	 * _.toString([1, 2, 3]);
+	 * // => '1,2,3'
+	 */
+	function toString(value) {
+	  return value == null ? '' : baseToString(value);
+	}
+ 
+	module.exports = toString;
+ 
+ 
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9),
+	    arrayMap = __webpack_require__(33),
+	    isArray = __webpack_require__(15),
+	    isSymbol = __webpack_require__(19);
+ 
+	/** Used as references for various `Number` constants. */
+	var INFINITY = 1 / 0;
+ 
+	/** Used to convert symbols to primitives and strings. */
+	var symbolProto = Symbol ? Symbol.prototype : undefined,
+	    symbolToString = symbolProto ? symbolProto.toString : undefined;
+ 
+	/**
+	 * The base implementation of `_.toString` which doesn't convert nullish
+	 * values to empty strings.
+	 *
+	 * @private
+	 * @param {*} value The value to process.
+	 * @returns {string} Returns the string.
+	 */
+	function baseToString(value) {
+	  // Exit early for strings to avoid a performance hit in some environments.
+	  if (typeof value == 'string') {
+	    return value;
+	  }
+	  if (isArray(value)) {
+	    // Recursively convert values (susceptible to call stack limits).
+	    return arrayMap(value, baseToString) + '';
+	  }
+	  if (isSymbol(value)) {
+	    return symbolToString ? symbolToString.call(value) : '';
+	  }
+	  var result = (value + '');
+	  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+	}
+ 
+	module.exports = baseToString;
+ 
+ 
+/***/ }),
+/* 12 */
+/***/ (function(module, exports) {
+ 
+	/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
+	var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+ 
+	module.exports = freeGlobal;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
+ 
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/**
+	 * Checks if `value` is likely a prototype object.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+	 */
+	function isPrototype(value) {
+	  var Ctor = value && value.constructor,
+	      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+ 
+	  return value === proto;
+	}
+ 
+	module.exports = isPrototype;
+ 
+ 
+/***/ }),
+/* 14 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var funcProto = Function.prototype;
+ 
+	/** Used to resolve the decompiled source of functions. */
+	var funcToString = funcProto.toString;
+ 
+	/**
+	 * Converts `func` to its source code.
+	 *
+	 * @private
+	 * @param {Function} func The function to convert.
+	 * @returns {string} Returns the source code.
+	 */
+	function toSource(func) {
+	  if (func != null) {
+	    try {
+	      return funcToString.call(func);
+	    } catch (e) {}
+	    try {
+	      return (func + '');
+	    } catch (e) {}
+	  }
+	  return '';
+	}
+ 
+	module.exports = toSource;
+ 
+ 
+/***/ }),
+/* 15 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is classified as an `Array` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+	 * @example
+	 *
+	 * _.isArray([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isArray(document.body.children);
+	 * // => false
+	 *
+	 * _.isArray('abc');
+	 * // => false
+	 *
+	 * _.isArray(_.noop);
+	 * // => false
+	 */
+	var isArray = Array.isArray;
+ 
+	module.exports = isArray;
+ 
+ 
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isFunction = __webpack_require__(17),
+	    isLength = __webpack_require__(18);
+ 
+	/**
+	 * Checks if `value` is array-like. A value is considered array-like if it's
+	 * not a function and has a `value.length` that's an integer greater than or
+	 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+	 * @example
+	 *
+	 * _.isArrayLike([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isArrayLike(document.body.children);
+	 * // => true
+	 *
+	 * _.isArrayLike('abc');
+	 * // => true
+	 *
+	 * _.isArrayLike(_.noop);
+	 * // => false
+	 */
+	function isArrayLike(value) {
+	  return value != null && isLength(value.length) && !isFunction(value);
+	}
+ 
+	module.exports = isArrayLike;
+ 
+ 
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObject = __webpack_require__(6);
+ 
+	/** `Object#toString` result references. */
+	var asyncTag = '[object AsyncFunction]',
+	    funcTag = '[object Function]',
+	    genTag = '[object GeneratorFunction]',
+	    proxyTag = '[object Proxy]';
+ 
+	/**
+	 * Checks if `value` is classified as a `Function` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+	 * @example
+	 *
+	 * _.isFunction(_);
+	 * // => true
+	 *
+	 * _.isFunction(/abc/);
+	 * // => false
+	 */
+	function isFunction(value) {
+	  if (!isObject(value)) {
+	    return false;
+	  }
+	  // The use of `Object#toString` avoids issues with the `typeof` operator
+	  // in Safari 9 which returns 'object' for typed arrays and other constructors.
+	  var tag = baseGetTag(value);
+	  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
+	}
+ 
+	module.exports = isFunction;
+ 
+ 
+/***/ }),
+/* 18 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/**
+	 * Checks if `value` is a valid array-like length.
+	 *
+	 * **Note:** This method is loosely based on
+	 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+	 * @example
+	 *
+	 * _.isLength(3);
+	 * // => true
+	 *
+	 * _.isLength(Number.MIN_VALUE);
+	 * // => false
+	 *
+	 * _.isLength(Infinity);
+	 * // => false
+	 *
+	 * _.isLength('3');
+	 * // => false
+	 */
+	function isLength(value) {
+	  return typeof value == 'number' &&
+	    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+	}
+ 
+	module.exports = isLength;
+ 
+ 
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var symbolTag = '[object Symbol]';
+ 
+	/**
+	 * Checks if `value` is classified as a `Symbol` primitive or object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+	 * @example
+	 *
+	 * _.isSymbol(Symbol.iterator);
+	 * // => true
+	 *
+	 * _.isSymbol('abc');
+	 * // => false
+	 */
+	function isSymbol(value) {
+	  return typeof value == 'symbol' ||
+	    (isObjectLike(value) && baseGetTag(value) == symbolTag);
+	}
+ 
+	module.exports = isSymbol;
+ 
+ 
+/***/ }),
+/* 20 */
+/***/ (function(module, exports) {
+ 
+	module.exports = function(module) {
+		if(!module.webpackPolyfill) {
+			module.deprecate = function() {};
+			module.paths = [];
+			// module.parent = undefined by default
+			module.children = [];
+			module.webpackPolyfill = 1;
+		}
+		return module;
+	}
+ 
+ 
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _repeat = __webpack_require__(69);
+ 
+	var _repeat2 = _interopRequireDefault(_repeat);
+ 
+	var _last = __webpack_require__(68);
+ 
+	var _last2 = _interopRequireDefault(_last);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var INDENT_TYPE_TOP_LEVEL = "top-level";
+	var INDENT_TYPE_BLOCK_LEVEL = "block-level";
+ 
+	/**
+	 * Manages indentation levels.
+	 *
+	 * There are two types of indentation levels:
+	 *
+	 * - BLOCK_LEVEL : increased by open-parenthesis
+	 * - TOP_LEVEL : increased by RESERVED_TOPLEVEL words
+	 */
+ 
+	var Indentation = function () {
+	    /**
+	     * @param {String} indent Indent value, default is "  " (2 spaces)
+	     */
+	    function Indentation(indent) {
+	        _classCallCheck(this, Indentation);
+ 
+					// 榛樿缂╄繘6涓┖鏍�
+	        this.indent = indent || "      ";
+	        // this.indent = indent || "  ";
+	        this.indentTypes = [];
+	    }
+ 
+	    /**
+	     * Returns current indentation string.
+	     * @return {String}
+	     */
+ 
+ 
+	    Indentation.prototype.getIndent = function getIndent() {
+	        return (0, _repeat2["default"])(this.indent, this.indentTypes.length);
+	    };
+ 
+	    /**
+	     * Increases indentation by one top-level indent.
+	     */
+ 
+ 
+	    Indentation.prototype.increaseToplevel = function increaseToplevel() {
+	        this.indentTypes.push(INDENT_TYPE_TOP_LEVEL);
+	    };
+ 
+	    /**
+	     * Increases indentation by one block-level indent.
+	     */
+ 
+ 
+	    Indentation.prototype.increaseBlockLevel = function increaseBlockLevel() {
+	        this.indentTypes.push(INDENT_TYPE_BLOCK_LEVEL);
+	    };
+ 
+	    /**
+	     * Decreases indentation by one top-level indent.
+	     * Does nothing when the previous indent is not top-level.
+	     */
+ 
+ 
+	    Indentation.prototype.decreaseTopLevel = function decreaseTopLevel() {
+	        if ((0, _last2["default"])(this.indentTypes) === INDENT_TYPE_TOP_LEVEL) {
+	            this.indentTypes.pop();
+	        }
+	    };
+ 
+	    /**
+	     * Decreases indentation by one block-level indent.
+	     * If there are top-level indents within the block-level indent,
+	     * throws away these as well.
+	     */
+ 
+ 
+	    Indentation.prototype.decreaseBlockLevel = function decreaseBlockLevel() {
+	        while (this.indentTypes.length > 0) {
+	            var type = this.indentTypes.pop();
+	            if (type !== INDENT_TYPE_TOP_LEVEL) {
+	                break;
+	            }
+	        }
+	    };
+ 
+	    return Indentation;
+	}();
+ 
+	exports["default"] = Indentation;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 22 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var INLINE_MAX_LENGTH = 50;
+ 
+	/**
+	 * Bookkeeper for inline blocks.
+	 *
+	 * Inline blocks are parenthized expressions that are shorter than INLINE_MAX_LENGTH.
+	 * These blocks are formatted on a single line, unlike longer parenthized
+	 * expressions where open-parenthesis causes newline and increase of indentation.
+	 */
+ 
+	var InlineBlock = function () {
+	    function InlineBlock() {
+	        _classCallCheck(this, InlineBlock);
+ 
+	        this.level = 0;
+	    }
+ 
+	    /**
+	     * Begins inline block when lookahead through upcoming tokens determines
+	     * that the block would be smaller than INLINE_MAX_LENGTH.
+	     * @param  {Object[]} tokens Array of all tokens
+	     * @param  {Number} index Current token position
+	     */
+ 
+ 
+	    InlineBlock.prototype.beginIfPossible = function beginIfPossible(tokens, index) {
+	        if (this.level === 0 && this.isInlineBlock(tokens, index)) {
+	            this.level = 1;
+	        } else if (this.level > 0) {
+	            this.level++;
+	        } else {
+	            this.level = 0;
+	        }
+	    };
+ 
+	    /**
+	     * Finishes current inline block.
+	     * There might be several nested ones.
+	     */
+ 
+ 
+	    InlineBlock.prototype.end = function end() {
+	        this.level--;
+	    };
+ 
+	    /**
+	     * True when inside an inline block
+	     * @return {Boolean}
+	     */
+ 
+ 
+	    InlineBlock.prototype.isActive = function isActive() {
+	        return this.level > 0;
+	    };
+ 
+	    // Check if this should be an inline parentheses block
+	    // Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2)
+ 
+ 
+	    InlineBlock.prototype.isInlineBlock = function isInlineBlock(tokens, index) {
+	        var length = 0;
+	        var level = 0;
+ 
+	        for (var i = index; i < tokens.length; i++) {
+	            var token = tokens[i];
+	            length += token.value.length;
+ 
+	            // Overran max length
+	            if (length > INLINE_MAX_LENGTH) {
+	                return false;
+	            }
+ 
+	            if (token.type === _tokenTypes2["default"].OPEN_PAREN) {
+	                level++;
+	            } else if (token.type === _tokenTypes2["default"].CLOSE_PAREN) {
+	                level--;
+	                if (level === 0) {
+	                    return true;
+	                }
+	            }
+ 
+	            if (this.isForbiddenToken(token)) {
+	                return false;
+	            }
+	        }
+	        return false;
+	    };
+ 
+	    // Reserved words that cause newlines, comments and semicolons
+	    // are not allowed inside inline parentheses block
+ 
+ 
+	    InlineBlock.prototype.isForbiddenToken = function isForbiddenToken(_ref) {
+	        var type = _ref.type,
+	            value = _ref.value;
+ 
+	        return type === _tokenTypes2["default"].RESERVED_TOPLEVEL || type === _tokenTypes2["default"].RESERVED_NEWLINE || type === _tokenTypes2["default"].COMMENT || type === _tokenTypes2["default"].BLOCK_COMMENT || value === ";";
+	    };
+ 
+	    return InlineBlock;
+	}();
+ 
+	exports["default"] = InlineBlock;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 23 */
+/***/ (function(module, exports) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	/**
+	 * Handles placeholder replacement with given params.
+	 */
+	var Params = function () {
+	    /**
+	     * @param {Object} params
+	     */
+	    function Params(params) {
+	        _classCallCheck(this, Params);
+ 
+	        this.params = params;
+	        this.index = 0;
+	    }
+ 
+	    /**
+	     * Returns param value that matches given placeholder with param key.
+	     * @param {Object} token
+	     *   @param {String} token.key Placeholder key
+	     *   @param {String} token.value Placeholder value
+	     * @return {String} param or token.value when params are missing
+	     */
+ 
+ 
+	    Params.prototype.get = function get(_ref) {
+	        var key = _ref.key,
+	            value = _ref.value;
+ 
+	        if (!this.params) {
+	            return value;
+	        }
+	        if (key) {
+	            return this.params[key];
+	        }
+	        return this.params[this.index++];
+	    };
+ 
+	    return Params;
+	}();
+ 
+	exports["default"] = Params;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 24 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["ABS", "ACTIVATE", "ALIAS", "ALL", "ALLOCATE", "ALLOW", "ALTER", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSOCIATE", "ASUTIME", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTES", "AUDIT", "AUTHORIZATION", "AUX", "AUXILIARY", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BUFFERPOOL", "BY", "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CASCADED", "CASE", "CAST", "CCSID", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLONE", "CLOSE", "CLUSTER", "COALESCE", "COLLATE", "COLLECT", "COLLECTION", "COLLID", "COLUMN", "COMMENT", "COMMIT", "CONCAT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONTAINS", "CONTINUE", "CONVERT", "CORR", "CORRESPONDING", "COUNT", "COUNT_BIG", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_LC_CTYPE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA", "CURRENT_SERVER", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TIMEZONE", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATABASE", "DATAPARTITIONNAME", "DATAPARTITIONNUM", "DATE", "DAY", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL", "DBINFO", "DBPARTITIONNAME", "DBPARTITIONNUM", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFINITION", "DELETE", "DENSERANK", "DENSE_RANK", "DEREF", "DESCRIBE", "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISABLE", "DISALLOW", "DISCONNECT", "DISTINCT", "DO", "DOCUMENT", "DOUBLE", "DROP", "DSSIZE", "DYNAMIC", "EACH", "EDITPROC", "ELEMENT", "ELSE", "ELSEIF", "ENABLE", "ENCODING", "ENCRYPTION", "END", "END-EXEC", "ENDING", "ERASE", "ESCAPE", "EVERY", "EXCEPTION", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTENDED", "EXTERNAL", "EXTRACT", "FALSE", "FENCED", "FETCH", "FIELDPROC", "FILE", "FILTER", "FINAL", "FIRST", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FULL", "FUNCTION", "FUSION", "GENERAL", "GENERATED", "GET", "GLOBAL", "GOTO", "GRANT", "GRAPHIC", "GROUP", "GROUPING", "HANDLER", "HASH", "HASHED_VALUE", "HINT", "HOLD", "HOUR", "HOURS", "IDENTITY", "IF", "IMMEDIATE", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INDEX", "INDICATOR", "INDICATORS", "INF", "INFINITY", "INHERIT", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTEGRITY", "INTERSECTION", "INTERVAL", "INTO", "IS", "ISOBID", "ISOLATION", "ITERATE", "JAR", "JAVA", "KEEP", "KEY", "LABEL", "LANGUAGE", "LARGE", "LATERAL", "LC_CTYPE", "LEADING", "LEAVE", "LEFT", "LIKE", "LINKTYPE", "LN", "LOCAL", "LOCALDATE", "LOCALE", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX", "LOCKSIZE", "LONG", "LOOP", "LOWER", "MAINTAINED", "MATCH", "MATERIALIZED", "MAX", "MAXVALUE", "MEMBER", "MERGE", "METHOD", "MICROSECOND", "MICROSECONDS", "MIN", "MINUTE", "MINUTES", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MONTHS", "MULTISET", "NAN", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEW_TABLE", "NEXTVAL", "NO", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER", "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NORMALIZE", "NORMALIZED", "NOT", "NULL", "NULLIF", "NULLS", "NUMERIC", "NUMPARTS", "OBID", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OLD_TABLE", "ON", "ONLY", "OPEN", "OPTIMIZATION", "OPTIMIZE", "OPTION", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", "PACKAGE", "PADDED", "PAGESIZE", "PARAMETER", "PART", "PARTITION", "PARTITIONED", "PARTITIONING", "PARTITIONS", "PASSWORD", "PATH", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PIECESIZE", "PLAN", "POSITION", "POWER", "PRECISION", "PREPARE", "PREVVAL", "PRIMARY", "PRIQTY", "PRIVILEGES", "PROCEDURE", "PROGRAM", "PSID", "PUBLIC", "QUERY", "QUERYNO", "RANGE", "RANK", "READ", "READS", "REAL", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RENAME", "REPEAT", "RESET", "RESIGNAL", "RESTART", "RESTRICT", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUND_CEILING", "ROUND_DOWN", "ROUND_FLOOR", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN", "ROUND_HALF_UP", "ROUND_UP", "ROUTINE", "ROW", "ROWNUMBER", "ROWS", "ROWSET", "ROW_NUMBER", "RRN", "RUN", "SAVEPOINT", "SCHEMA", "SCOPE", "SCRATCHPAD", "SCROLL", "SEARCH", "SECOND", "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", "SEQUENCE", "SESSION", "SESSION_USER", "SIGNAL", "SIMILAR", "SIMPLE", "SMALLINT", "SNAN", "SOME", "SOURCE", "SPECIFIC", "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLID", "SQLSTATE", "SQLWARNING", "SQRT", "STACKED", "STANDARD", "START", "STARTING", "STATEMENT", "STATIC", "STATMENT", "STAY", "STDDEV_POP", "STDDEV_SAMP", "STOGROUP", "STORES", "STYLE", "SUBMULTISET", "SUBSTRING", "SUM", "SUMMARY", "SYMMETRIC", "SYNONYM", "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TABLESPACE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TYPE", "UESCAPE", "UNDO", "UNIQUE", "UNKNOWN", "UNNEST", "UNTIL", "UPPER", "USAGE", "USER", "USING", "VALIDPROC", "VALUE", "VARCHAR", "VARIABLE", "VARIANT", "VARYING", "VAR_POP", "VAR_SAMP", "VCAT", "VERSION", "VIEW", "VOLATILE", "VOLUMES", "WHEN", "WHENEVER", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WLM", "WRITE", "XMLELEMENT", "XMLEXISTS", "XMLNAMESPACES", "YEAR", "YEARS"];
+ 
+	var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INTERSECT", "LIMIT", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UPDATE", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "CROSS JOIN", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN"];
+ 
+	var tokenizer = void 0;
+ 
+	var Db2Formatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function Db2Formatter(cfg) {
+	        _classCallCheck(this, Db2Formatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Formats DB2 query to make it easier to read
+	     *
+	     * @param {String} query The DB2 query string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    Db2Formatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "''", "``", "[]"],
+	                openParens: ["("],
+	                closeParens: [")"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: [":"],
+	                lineCommentTypes: ["--"],
+	                specialWordChars: ["#", "@"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return Db2Formatter;
+	}();
+ 
+	exports["default"] = Db2Formatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 25 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["ALL", "ALTER", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "BEGIN", "BETWEEN", "BINARY", "BOOLEAN", "BREAK", "BUCKET", "BUILD", "BY", "CALL", "CASE", "CAST", "CLUSTER", "COLLATE", "COLLECTION", "COMMIT", "CONNECT", "CONTINUE", "CORRELATE", "COVER", "CREATE", "DATABASE", "DATASET", "DATASTORE", "DECLARE", "DECREMENT", "DELETE", "DERIVED", "DESC", "DESCRIBE", "DISTINCT", "DO", "DROP", "EACH", "ELEMENT", "ELSE", "END", "EVERY", "EXCEPT", "EXCLUDE", "EXECUTE", "EXISTS", "EXPLAIN", "FALSE", "FETCH", "FIRST", "FLATTEN", "FOR", "FORCE", "FROM", "FUNCTION", "GRANT", "GROUP", "GSI", "HAVING", "IF", "IGNORE", "ILIKE", "IN", "INCLUDE", "INCREMENT", "INDEX", "INFER", "INLINE", "INNER", "INSERT", "INTERSECT", "INTO", "IS", "JOIN", "KEY", "KEYS", "KEYSPACE", "KNOWN", "LAST", "LEFT", "LET", "LETTING", "LIKE", "LIMIT", "LSM", "MAP", "MAPPING", "MATCHED", "MATERIALIZED", "MERGE", "MINUS", "MISSING", "NAMESPACE", "NEST", "NOT", "NULL", "NUMBER", "OBJECT", "OFFSET", "ON", "OPTION", "OR", "ORDER", "OUTER", "OVER", "PARSE", "PARTITION", "PASSWORD", "PATH", "POOL", "PREPARE", "PRIMARY", "PRIVATE", "PRIVILEGE", "PROCEDURE", "PUBLIC", "RAW", "REALM", "REDUCE", "RENAME", "RETURN", "RETURNING", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "SATISFIES", "SCHEMA", "SELECT", "SELF", "SEMI", "SET", "SHOW", "SOME", "START", "STATISTICS", "STRING", "SYSTEM", "THEN", "TO", "TRANSACTION", "TRIGGER", "TRUE", "TRUNCATE", "UNDER", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UNSET", "UPDATE", "UPSERT", "USE", "USER", "USING", "VALIDATE", "VALUE", "VALUED", "VALUES", "VIA", "VIEW", "WHEN", "WHERE", "WHILE", "WITH", "WITHIN", "WORK", "XOR"];
+ 
+	var reservedToplevelWords = ["DELETE FROM", "EXCEPT ALL", "EXCEPT", "EXPLAIN DELETE FROM", "EXPLAIN UPDATE", "EXPLAIN UPSERT", "FROM", "GROUP BY", "HAVING", "INFER", "INSERT INTO", "INTERSECT ALL", "INTERSECT", "LET", "LIMIT", "MERGE", "NEST", "ORDER BY", "PREPARE", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UNNEST", "UPDATE", "UPSERT", "USE KEYS", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var N1qlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function N1qlFormatter(cfg) {
+	        _classCallCheck(this, N1qlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a N1QL string to make it easier to read
+	     *
+	     * @param {String} query The N1QL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    N1qlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "''", "``"],
+	                openParens: ["(", "[", "{"],
+	                closeParens: [")", "]", "}"],
+	                namedPlaceholderTypes: ["$"],
+	                lineCommentTypes: ["#", "--"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return N1qlFormatter;
+	}();
+ 
+	exports["default"] = N1qlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 26 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["A", "ACCESSIBLE", "AGENT", "AGGREGATE", "ALL", "ALTER", "ANY", "ARRAY", "AS", "ASC", "AT", "ATTRIBUTE", "AUTHID", "AVG", "BETWEEN", "BFILE_BASE", "BINARY_INTEGER", "BINARY", "BLOB_BASE", "BLOCK", "BODY", "BOOLEAN", "BOTH", "BOUND", "BULK", "BY", "BYTE", "C", "CALL", "CALLING", "CASCADE", "CASE", "CHAR_BASE", "CHAR", "CHARACTER", "CHARSET", "CHARSETFORM", "CHARSETID", "CHECK", "CLOB_BASE", "CLONE", "CLOSE", "CLUSTER", "CLUSTERS", "COALESCE", "COLAUTH", "COLLECT", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPILED", "COMPRESS", "CONNECT", "CONSTANT", "CONSTRUCTOR", "CONTEXT", "CONTINUE", "CONVERT", "COUNT", "CRASH", "CREATE", "CREDENTIAL", "CURRENT", "CURRVAL", "CURSOR", "CUSTOMDATUM", "DANGLING", "DATA", "DATE_BASE", "DATE", "DAY", "DECIMAL", "DEFAULT", "DEFINE", "DELETE", "DESC", "DETERMINISTIC", "DIRECTORY", "DISTINCT", "DO", "DOUBLE", "DROP", "DURATION", "ELEMENT", "ELSIF", "EMPTY", "ESCAPE", "EXCEPTIONS", "EXCLUSIVE", "EXECUTE", "EXISTS", "EXIT", "EXTENDS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FINAL", "FIRST", "FIXED", "FLOAT", "FOR", "FORALL", "FORCE", "FROM", "FUNCTION", "GENERAL", "GOTO", "GRANT", "GROUP", "HASH", "HEAP", "HIDDEN", "HOUR", "IDENTIFIED", "IF", "IMMEDIATE", "IN", "INCLUDING", "INDEX", "INDEXES", "INDICATOR", "INDICES", "INFINITE", "INSTANTIABLE", "INT", "INTEGER", "INTERFACE", "INTERVAL", "INTO", "INVALIDATE", "IS", "ISOLATION", "JAVA", "LANGUAGE", "LARGE", "LEADING", "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE2", "LIKE4", "LIKEC", "LIMITED", "LOCAL", "LOCK", "LONG", "MAP", "MAX", "MAXLEN", "MEMBER", "MERGE", "MIN", "MINUS", "MINUTE", "MLSLABEL", "MOD", "MODE", "MONTH", "MULTISET", "NAME", "NAN", "NATIONAL", "NATIVE", "NATURAL", "NATURALN", "NCHAR", "NEW", "NEXTVAL", "NOCOMPRESS", "NOCOPY", "NOT", "NOWAIT", "NULL", "NULLIF", "NUMBER_BASE", "NUMBER", "OBJECT", "OCICOLL", "OCIDATE", "OCIDATETIME", "OCIDURATION", "OCIINTERVAL", "OCILOBLOCATOR", "OCINUMBER", "OCIRAW", "OCIREF", "OCIREFCURSOR", "OCIROWID", "OCISTRING", "OCITYPE", "OF", "OLD", "ON", "ONLY", "OPAQUE", "OPEN", "OPERATOR", "OPTION", "ORACLE", "ORADATA", "ORDER", "ORGANIZATION", "ORLANY", "ORLVARY", "OTHERS", "OUT", "OVERLAPS", "OVERRIDING", "PACKAGE", "PARALLEL_ENABLE", "PARAMETER", "PARAMETERS", "PARENT", "PARTITION", "PASCAL", "PCTFREE", "PIPE", "PIPELINED", "PLS_INTEGER", "PLUGGABLE", "POSITIVE", "POSITIVEN", "PRAGMA", "PRECISION", "PRIOR", "PRIVATE", "PROCEDURE", "PUBLIC", "RAISE", "RANGE", "RAW", "READ", "REAL", "RECORD", "REF", "REFERENCE", "RELEASE", "RELIES_ON", "REM", "REMAINDER", "RENAME", "RESOURCE", "RESULT_CACHE", "RESULT", "RETURN", "RETURNING", "REVERSE", "REVOKE", "ROLLBACK", "ROW", "ROWID", "ROWNUM", "ROWTYPE", "SAMPLE", "SAVE", "SAVEPOINT", "SB1", "SB2", "SB4", "SECOND", "SEGMENT", "SELF", "SEPARATE", "SEQUENCE", "SERIALIZABLE", "SHARE", "SHORT", "SIZE_T", "SIZE", "SMALLINT", "SOME", "SPACE", "SPARSE", "SQL", "SQLCODE", "SQLDATA", "SQLERRM", "SQLNAME", "SQLSTATE", "STANDARD", "START", "STATIC", "STDDEV", "STORED", "STRING", "STRUCT", "STYLE", "SUBMULTISET", "SUBPARTITION", "SUBSTITUTABLE", "SUBTYPE", "SUCCESSFUL", "SUM", "SYNONYM", "SYSDATE", "TABAUTH", "TABLE", "TDO", "THE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_ABBR", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TIMEZONE_REGION", "TO", "TRAILING", "TRANSACTION", "TRANSACTIONAL", "TRIGGER", "TRUE", "TRUSTED", "TYPE", "UB1", "UB2", "UB4", "UID", "UNDER", "UNIQUE", "UNPLUG", "UNSIGNED", "UNTRUSTED", "USE", "USER", "USING", "VALIDATE", "VALIST", "VALUE", "VARCHAR", "VARCHAR2", "VARIABLE", "VARIANCE", "VARRAY", "VARYING", "VIEW", "VIEWS", "VOID", "WHENEVER", "WHILE", "WITH", "WORK", "WRAPPED", "WRITE", "YEAR", "ZONE"];
+ 
+	var reservedToplevelWords = ["ADD", "ALTER COLUMN", "ALTER TABLE", "BEGIN", "CONNECT BY", "DECLARE", "DELETE FROM", "DELETE", "END", "EXCEPT", "EXCEPTION", "FETCH FIRST", "FROM", "GROUP BY", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "LOOP", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "START WITH", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "END", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var PlSqlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function PlSqlFormatter(cfg) {
+	        _classCallCheck(this, PlSqlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a PL/SQL string to make it easier to read
+	     *
+	     * @param {String} query The PL/SQL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    PlSqlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "N''", "''", "``"],
+	                openParens: ["(", "CASE"],
+	                closeParens: [")", "END"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: [":"],
+	                lineCommentTypes: ["--"],
+	                specialWordChars: ["_", "$", "#", ".", "@"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return PlSqlFormatter;
+	}();
+ 
+	exports["default"] = PlSqlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 27 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", "AUTO_INCREMENT", "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", "CURRENT_TIMESTAMP", "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENDED", "FAST", "FETCH", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", "INTO", "INVOKER", "IS", "ISOLATION", "KEY", "KEYS", "KILL", "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", "NAMES", "NATURAL", "NOT", "NOW()", "NULL", "OFFSET", "ON DELETE", "ON UPDATE", "ON", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", "PURGE", "QUICK", "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", "YEAR_MONTH"];
+ 
+	var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var StandardSqlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function StandardSqlFormatter(cfg) {
+	        _classCallCheck(this, StandardSqlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a Standard SQL string to make it easier to read
+	     *
+	     * @param {String} query The Standard SQL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    StandardSqlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "N''", "''", "``", "[]"],
+	                openParens: ["(", "CASE"],
+	                closeParens: [")", "END"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: ["@", ":"],
+	                lineCommentTypes: ["#", "--"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return StandardSqlFormatter;
+	}();
+ 
+	exports["default"] = StandardSqlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 28 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var DataView = getNative(root, 'DataView');
+ 
+	module.exports = DataView;
+ 
+ 
+/***/ }),
+/* 29 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Map = getNative(root, 'Map');
+ 
+	module.exports = Map;
+ 
+ 
+/***/ }),
+/* 30 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Promise = getNative(root, 'Promise');
+ 
+	module.exports = Promise;
+ 
+ 
+/***/ }),
+/* 31 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Set = getNative(root, 'Set');
+ 
+	module.exports = Set;
+ 
+ 
+/***/ }),
+/* 32 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var WeakMap = getNative(root, 'WeakMap');
+ 
+	module.exports = WeakMap;
+ 
+ 
+/***/ }),
+/* 33 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * A specialized version of `_.map` for arrays without support for iteratee
+	 * shorthands.
+	 *
+	 * @private
+	 * @param {Array} [array] The array to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 */
+	function arrayMap(array, iteratee) {
+	  var index = -1,
+	      length = array == null ? 0 : array.length,
+	      result = Array(length);
+ 
+	  while (++index < length) {
+	    result[index] = iteratee(array[index], index, array);
+	  }
+	  return result;
+	}
+ 
+	module.exports = arrayMap;
+ 
+ 
+/***/ }),
+/* 34 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Converts an ASCII `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function asciiToArray(string) {
+	  return string.split('');
+	}
+ 
+	module.exports = asciiToArray;
+ 
+ 
+/***/ }),
+/* 35 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.findIndex` and `_.findLastIndex` without
+	 * support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {Function} predicate The function invoked per iteration.
+	 * @param {number} fromIndex The index to search from.
+	 * @param {boolean} [fromRight] Specify iterating from right to left.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function baseFindIndex(array, predicate, fromIndex, fromRight) {
+	  var length = array.length,
+	      index = fromIndex + (fromRight ? 1 : -1);
+ 
+	  while ((fromRight ? index-- : ++index < length)) {
+	    if (predicate(array[index], index, array)) {
+	      return index;
+	    }
+	  }
+	  return -1;
+	}
+ 
+	module.exports = baseFindIndex;
+ 
+ 
+/***/ }),
+/* 36 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseFindIndex = __webpack_require__(35),
+	    baseIsNaN = __webpack_require__(38),
+	    strictIndexOf = __webpack_require__(59);
+ 
+	/**
+	 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {*} value The value to search for.
+	 * @param {number} fromIndex The index to search from.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function baseIndexOf(array, value, fromIndex) {
+	  return value === value
+	    ? strictIndexOf(array, value, fromIndex)
+	    : baseFindIndex(array, baseIsNaN, fromIndex);
+	}
+ 
+	module.exports = baseIndexOf;
+ 
+ 
+/***/ }),
+/* 37 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]';
+ 
+	/**
+	 * The base implementation of `_.isArguments`.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 */
+	function baseIsArguments(value) {
+	  return isObjectLike(value) && baseGetTag(value) == argsTag;
+	}
+ 
+	module.exports = baseIsArguments;
+ 
+ 
+/***/ }),
+/* 38 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.isNaN` without support for number objects.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+	 */
+	function baseIsNaN(value) {
+	  return value !== value;
+	}
+ 
+	module.exports = baseIsNaN;
+ 
+ 
+/***/ }),
+/* 39 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isFunction = __webpack_require__(17),
+	    isMasked = __webpack_require__(54),
+	    isObject = __webpack_require__(6),
+	    toSource = __webpack_require__(14);
+ 
+	/**
+	 * Used to match `RegExp`
+	 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+	 */
+	var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+ 
+	/** Used to detect host constructors (Safari). */
+	var reIsHostCtor = /^\[object .+?Constructor\]$/;
+ 
+	/** Used for built-in method references. */
+	var funcProto = Function.prototype,
+	    objectProto = Object.prototype;
+ 
+	/** Used to resolve the decompiled source of functions. */
+	var funcToString = funcProto.toString;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/** Used to detect if a method is native. */
+	var reIsNative = RegExp('^' +
+	  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+	  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+	);
+ 
+	/**
+	 * The base implementation of `_.isNative` without bad shim checks.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a native function,
+	 *  else `false`.
+	 */
+	function baseIsNative(value) {
+	  if (!isObject(value) || isMasked(value)) {
+	    return false;
+	  }
+	  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
+	  return pattern.test(toSource(value));
+	}
+ 
+	module.exports = baseIsNative;
+ 
+ 
+/***/ }),
+/* 40 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isLength = __webpack_require__(18),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]',
+	    arrayTag = '[object Array]',
+	    boolTag = '[object Boolean]',
+	    dateTag = '[object Date]',
+	    errorTag = '[object Error]',
+	    funcTag = '[object Function]',
+	    mapTag = '[object Map]',
+	    numberTag = '[object Number]',
+	    objectTag = '[object Object]',
+	    regexpTag = '[object RegExp]',
+	    setTag = '[object Set]',
+	    stringTag = '[object String]',
+	    weakMapTag = '[object WeakMap]';
+ 
+	var arrayBufferTag = '[object ArrayBuffer]',
+	    dataViewTag = '[object DataView]',
+	    float32Tag = '[object Float32Array]',
+	    float64Tag = '[object Float64Array]',
+	    int8Tag = '[object Int8Array]',
+	    int16Tag = '[object Int16Array]',
+	    int32Tag = '[object Int32Array]',
+	    uint8Tag = '[object Uint8Array]',
+	    uint8ClampedTag = '[object Uint8ClampedArray]',
+	    uint16Tag = '[object Uint16Array]',
+	    uint32Tag = '[object Uint32Array]';
+ 
+	/** Used to identify `toStringTag` values of typed arrays. */
+	var typedArrayTags = {};
+	typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+	typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+	typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+	typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+	typedArrayTags[uint32Tag] = true;
+	typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+	typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+	typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+	typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+	typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+	typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+	typedArrayTags[setTag] = typedArrayTags[stringTag] =
+	typedArrayTags[weakMapTag] = false;
+ 
+	/**
+	 * The base implementation of `_.isTypedArray` without Node.js optimizations.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+	 */
+	function baseIsTypedArray(value) {
+	  return isObjectLike(value) &&
+	    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
+	}
+ 
+	module.exports = baseIsTypedArray;
+ 
+ 
+/***/ }),
+/* 41 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isPrototype = __webpack_require__(13),
+	    nativeKeys = __webpack_require__(55);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @returns {Array} Returns the array of property names.
+	 */
+	function baseKeys(object) {
+	  if (!isPrototype(object)) {
+	    return nativeKeys(object);
+	  }
+	  var result = [];
+	  for (var key in Object(object)) {
+	    if (hasOwnProperty.call(object, key) && key != 'constructor') {
+	      result.push(key);
+	    }
+	  }
+	  return result;
+	}
+ 
+	module.exports = baseKeys;
+ 
+ 
+/***/ }),
+/* 42 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeFloor = Math.floor;
+ 
+	/**
+	 * The base implementation of `_.repeat` which doesn't coerce arguments.
+	 *
+	 * @private
+	 * @param {string} string The string to repeat.
+	 * @param {number} n The number of times to repeat the string.
+	 * @returns {string} Returns the repeated string.
+	 */
+	function baseRepeat(string, n) {
+	  var result = '';
+	  if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
+	    return result;
+	  }
+	  // Leverage the exponentiation by squaring algorithm for a faster repeat.
+	  // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+	  do {
+	    if (n % 2) {
+	      result += string;
+	    }
+	    n = nativeFloor(n / 2);
+	    if (n) {
+	      string += string;
+	    }
+	  } while (n);
+ 
+	  return result;
+	}
+ 
+	module.exports = baseRepeat;
+ 
+ 
+/***/ }),
+/* 43 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.slice` without an iteratee call guard.
+	 *
+	 * @private
+	 * @param {Array} array The array to slice.
+	 * @param {number} [start=0] The start position.
+	 * @param {number} [end=array.length] The end position.
+	 * @returns {Array} Returns the slice of `array`.
+	 */
+	function baseSlice(array, start, end) {
+	  var index = -1,
+	      length = array.length;
+ 
+	  if (start < 0) {
+	    start = -start > length ? 0 : (length + start);
+	  }
+	  end = end > length ? length : end;
+	  if (end < 0) {
+	    end += length;
+	  }
+	  length = start > end ? 0 : ((end - start) >>> 0);
+	  start >>>= 0;
+ 
+	  var result = Array(length);
+	  while (++index < length) {
+	    result[index] = array[index + start];
+	  }
+	  return result;
+	}
+ 
+	module.exports = baseSlice;
+ 
+ 
+/***/ }),
+/* 44 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.unary` without support for storing metadata.
+	 *
+	 * @private
+	 * @param {Function} func The function to cap arguments for.
+	 * @returns {Function} Returns the new capped function.
+	 */
+	function baseUnary(func) {
+	  return function(value) {
+	    return func(value);
+	  };
+	}
+ 
+	module.exports = baseUnary;
+ 
+ 
+/***/ }),
+/* 45 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseSlice = __webpack_require__(43);
+ 
+	/**
+	 * Casts `array` to a slice if it's needed.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {number} start The start position.
+	 * @param {number} [end=array.length] The end position.
+	 * @returns {Array} Returns the cast slice.
+	 */
+	function castSlice(array, start, end) {
+	  var length = array.length;
+	  end = end === undefined ? length : end;
+	  return (!start && end >= length) ? array : baseSlice(array, start, end);
+	}
+ 
+	module.exports = castSlice;
+ 
+ 
+/***/ }),
+/* 46 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIndexOf = __webpack_require__(36);
+ 
+	/**
+	 * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+	 * that is not found in the character symbols.
+	 *
+	 * @private
+	 * @param {Array} strSymbols The string symbols to inspect.
+	 * @param {Array} chrSymbols The character symbols to find.
+	 * @returns {number} Returns the index of the last unmatched string symbol.
+	 */
+	function charsEndIndex(strSymbols, chrSymbols) {
+	  var index = strSymbols.length;
+ 
+	  while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+	  return index;
+	}
+ 
+	module.exports = charsEndIndex;
+ 
+ 
+/***/ }),
+/* 47 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var root = __webpack_require__(1);
+ 
+	/** Used to detect overreaching core-js shims. */
+	var coreJsData = root['__core-js_shared__'];
+ 
+	module.exports = coreJsData;
+ 
+ 
+/***/ }),
+/* 48 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * Used to resolve the
+	 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+	 * of values.
+	 */
+	var nativeObjectToString = objectProto.toString;
+ 
+	/** Built-in value references. */
+	var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+ 
+	/**
+	 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the raw `toStringTag`.
+	 */
+	function getRawTag(value) {
+	  var isOwn = hasOwnProperty.call(value, symToStringTag),
+	      tag = value[symToStringTag];
+ 
+	  try {
+	    value[symToStringTag] = undefined;
+	    var unmasked = true;
+	  } catch (e) {}
+ 
+	  var result = nativeObjectToString.call(value);
+	  if (unmasked) {
+	    if (isOwn) {
+	      value[symToStringTag] = tag;
+	    } else {
+	      delete value[symToStringTag];
+	    }
+	  }
+	  return result;
+	}
+ 
+	module.exports = getRawTag;
+ 
+ 
+/***/ }),
+/* 49 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var DataView = __webpack_require__(28),
+	    Map = __webpack_require__(29),
+	    Promise = __webpack_require__(30),
+	    Set = __webpack_require__(31),
+	    WeakMap = __webpack_require__(32),
+	    baseGetTag = __webpack_require__(2),
+	    toSource = __webpack_require__(14);
+ 
+	/** `Object#toString` result references. */
+	var mapTag = '[object Map]',
+	    objectTag = '[object Object]',
+	    promiseTag = '[object Promise]',
+	    setTag = '[object Set]',
+	    weakMapTag = '[object WeakMap]';
+ 
+	var dataViewTag = '[object DataView]';
+ 
+	/** Used to detect maps, sets, and weakmaps. */
+	var dataViewCtorString = toSource(DataView),
+	    mapCtorString = toSource(Map),
+	    promiseCtorString = toSource(Promise),
+	    setCtorString = toSource(Set),
+	    weakMapCtorString = toSource(WeakMap);
+ 
+	/**
+	 * Gets the `toStringTag` of `value`.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the `toStringTag`.
+	 */
+	var getTag = baseGetTag;
+ 
+	// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
+	if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+	    (Map && getTag(new Map) != mapTag) ||
+	    (Promise && getTag(Promise.resolve()) != promiseTag) ||
+	    (Set && getTag(new Set) != setTag) ||
+	    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+	  getTag = function(value) {
+	    var result = baseGetTag(value),
+	        Ctor = result == objectTag ? value.constructor : undefined,
+	        ctorString = Ctor ? toSource(Ctor) : '';
+ 
+	    if (ctorString) {
+	      switch (ctorString) {
+	        case dataViewCtorString: return dataViewTag;
+	        case mapCtorString: return mapTag;
+	        case promiseCtorString: return promiseTag;
+	        case setCtorString: return setTag;
+	        case weakMapCtorString: return weakMapTag;
+	      }
+	    }
+	    return result;
+	  };
+	}
+ 
+	module.exports = getTag;
+ 
+ 
+/***/ }),
+/* 50 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Gets the value at `key` of `object`.
+	 *
+	 * @private
+	 * @param {Object} [object] The object to query.
+	 * @param {string} key The key of the property to get.
+	 * @returns {*} Returns the property value.
+	 */
+	function getValue(object, key) {
+	  return object == null ? undefined : object[key];
+	}
+ 
+	module.exports = getValue;
+ 
+ 
+/***/ }),
+/* 51 */
+/***/ (function(module, exports) {
+ 
+	/** Used to compose unicode character classes. */
+	var rsAstralRange = '\\ud800-\\udfff',
+	    rsComboMarksRange = '\\u0300-\\u036f',
+	    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+	    rsComboSymbolsRange = '\\u20d0-\\u20ff',
+	    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+	    rsVarRange = '\\ufe0e\\ufe0f';
+ 
+	/** Used to compose unicode capture groups. */
+	var rsZWJ = '\\u200d';
+ 
+	/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
+	var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboRange + rsVarRange + ']');
+ 
+	/**
+	 * Checks if `string` contains Unicode symbols.
+	 *
+	 * @private
+	 * @param {string} string The string to inspect.
+	 * @returns {boolean} Returns `true` if a symbol is found, else `false`.
+	 */
+	function hasUnicode(string) {
+	  return reHasUnicode.test(string);
+	}
+ 
+	module.exports = hasUnicode;
+ 
+ 
+/***/ }),
+/* 52 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/** Used to detect unsigned integer values. */
+	var reIsUint = /^(?:0|[1-9]\d*)$/;
+ 
+	/**
+	 * Checks if `value` is a valid array-like index.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+	 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+	 */
+	function isIndex(value, length) {
+	  var type = typeof value;
+	  length = length == null ? MAX_SAFE_INTEGER : length;
+ 
+	  return !!length &&
+	    (type == 'number' ||
+	      (type != 'symbol' && reIsUint.test(value))) &&
+	        (value > -1 && value % 1 == 0 && value < length);
+	}
+ 
+	module.exports = isIndex;
+ 
+ 
+/***/ }),
+/* 53 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var eq = __webpack_require__(62),
+	    isArrayLike = __webpack_require__(16),
+	    isIndex = __webpack_require__(52),
+	    isObject = __webpack_require__(6);
+ 
+	/**
+	 * Checks if the given arguments are from an iteratee call.
+	 *
+	 * @private
+	 * @param {*} value The potential iteratee value argument.
+	 * @param {*} index The potential iteratee index or key argument.
+	 * @param {*} object The potential iteratee object argument.
+	 * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+	 *  else `false`.
+	 */
+	function isIterateeCall(value, index, object) {
+	  if (!isObject(object)) {
+	    return false;
+	  }
+	  var type = typeof index;
+	  if (type == 'number'
+	        ? (isArrayLike(object) && isIndex(index, object.length))
+	        : (type == 'string' && index in object)
+	      ) {
+	    return eq(object[index], value);
+	  }
+	  return false;
+	}
+ 
+	module.exports = isIterateeCall;
+ 
+ 
+/***/ }),
+/* 54 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var coreJsData = __webpack_require__(47);
+ 
+	/** Used to detect methods masquerading as native. */
+	var maskSrcKey = (function() {
+	  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+	  return uid ? ('Symbol(src)_1.' + uid) : '';
+	}());
+ 
+	/**
+	 * Checks if `func` has its source masked.
+	 *
+	 * @private
+	 * @param {Function} func The function to check.
+	 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+	 */
+	function isMasked(func) {
+	  return !!maskSrcKey && (maskSrcKey in func);
+	}
+ 
+	module.exports = isMasked;
+ 
+ 
+/***/ }),
+/* 55 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var overArg = __webpack_require__(58);
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeKeys = overArg(Object.keys, Object);
+ 
+	module.exports = nativeKeys;
+ 
+ 
+/***/ }),
+/* 56 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	/* WEBPACK VAR INJECTION */(function(module) {var freeGlobal = __webpack_require__(12);
+ 
+	/** Detect free variable `exports`. */
+	var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+ 
+	/** Detect free variable `module`. */
+	var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+ 
+	/** Detect the popular CommonJS extension `module.exports`. */
+	var moduleExports = freeModule && freeModule.exports === freeExports;
+ 
+	/** Detect free variable `process` from Node.js. */
+	var freeProcess = moduleExports && freeGlobal.process;
+ 
+	/** Used to access faster Node.js helpers. */
+	var nodeUtil = (function() {
+	  try {
+	    // Use `util.types` for Node.js 10+.
+	    var types = freeModule && freeModule.require && freeModule.require('util').types;
+ 
+	    if (types) {
+	      return types;
+	    }
+ 
+	    // Legacy `process.binding('util')` for Node.js < 10.
+	    return freeProcess && freeProcess.binding && freeProcess.binding('util');
+	  } catch (e) {}
+	}());
+ 
+	module.exports = nodeUtil;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(20)(module)))
+ 
+/***/ }),
+/* 57 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/**
+	 * Used to resolve the
+	 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+	 * of values.
+	 */
+	var nativeObjectToString = objectProto.toString;
+ 
+	/**
+	 * Converts `value` to a string using `Object.prototype.toString`.
+	 *
+	 * @private
+	 * @param {*} value The value to convert.
+	 * @returns {string} Returns the converted string.
+	 */
+	function objectToString(value) {
+	  return nativeObjectToString.call(value);
+	}
+ 
+	module.exports = objectToString;
+ 
+ 
+/***/ }),
+/* 58 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Creates a unary function that invokes `func` with its argument transformed.
+	 *
+	 * @private
+	 * @param {Function} func The function to wrap.
+	 * @param {Function} transform The argument transform.
+	 * @returns {Function} Returns the new function.
+	 */
+	function overArg(func, transform) {
+	  return function(arg) {
+	    return func(transform(arg));
+	  };
+	}
+ 
+	module.exports = overArg;
+ 
+ 
+/***/ }),
+/* 59 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * A specialized version of `_.indexOf` which performs strict equality
+	 * comparisons of values, i.e. `===`.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {*} value The value to search for.
+	 * @param {number} fromIndex The index to search from.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function strictIndexOf(array, value, fromIndex) {
+	  var index = fromIndex - 1,
+	      length = array.length;
+ 
+	  while (++index < length) {
+	    if (array[index] === value) {
+	      return index;
+	    }
+	  }
+	  return -1;
+	}
+ 
+	module.exports = strictIndexOf;
+ 
+ 
+/***/ }),
+/* 60 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var asciiToArray = __webpack_require__(34),
+	    hasUnicode = __webpack_require__(51),
+	    unicodeToArray = __webpack_require__(61);
+ 
+	/**
+	 * Converts `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function stringToArray(string) {
+	  return hasUnicode(string)
+	    ? unicodeToArray(string)
+	    : asciiToArray(string);
+	}
+ 
+	module.exports = stringToArray;
+ 
+ 
+/***/ }),
+/* 61 */
+/***/ (function(module, exports) {
+ 
+	/** Used to compose unicode character classes. */
+	var rsAstralRange = '\\ud800-\\udfff',
+	    rsComboMarksRange = '\\u0300-\\u036f',
+	    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+	    rsComboSymbolsRange = '\\u20d0-\\u20ff',
+	    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+	    rsVarRange = '\\ufe0e\\ufe0f';
+ 
+	/** Used to compose unicode capture groups. */
+	var rsAstral = '[' + rsAstralRange + ']',
+	    rsCombo = '[' + rsComboRange + ']',
+	    rsFitz = '\\ud83c[\\udffb-\\udfff]',
+	    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
+	    rsNonAstral = '[^' + rsAstralRange + ']',
+	    rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
+	    rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
+	    rsZWJ = '\\u200d';
+ 
+	/** Used to compose unicode regexes. */
+	var reOptMod = rsModifier + '?',
+	    rsOptVar = '[' + rsVarRange + ']?',
+	    rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
+	    rsSeq = rsOptVar + reOptMod + rsOptJoin,
+	    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
+ 
+	/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
+	var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
+ 
+	/**
+	 * Converts a Unicode `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function unicodeToArray(string) {
+	  return string.match(reUnicode) || [];
+	}
+ 
+	module.exports = unicodeToArray;
+ 
+ 
+/***/ }),
+/* 62 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Performs a
+	 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+	 * comparison between two values to determine if they are equivalent.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to compare.
+	 * @param {*} other The other value to compare.
+	 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+	 * @example
+	 *
+	 * var object = { 'a': 1 };
+	 * var other = { 'a': 1 };
+	 *
+	 * _.eq(object, object);
+	 * // => true
+	 *
+	 * _.eq(object, other);
+	 * // => false
+	 *
+	 * _.eq('a', 'a');
+	 * // => true
+	 *
+	 * _.eq('a', Object('a'));
+	 * // => false
+	 *
+	 * _.eq(NaN, NaN);
+	 * // => true
+	 */
+	function eq(value, other) {
+	  return value === other || (value !== value && other !== other);
+	}
+ 
+	module.exports = eq;
+ 
+ 
+/***/ }),
+/* 63 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toString = __webpack_require__(10);
+ 
+	/**
+	 * Used to match `RegExp`
+	 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+	 */
+	var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
+	    reHasRegExpChar = RegExp(reRegExpChar.source);
+ 
+	/**
+	 * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
+	 * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to escape.
+	 * @returns {string} Returns the escaped string.
+	 * @example
+	 *
+	 * _.escapeRegExp('[lodash](https://lodash.com/)');
+	 * // => '\[lodash\]\(https://lodash\.com/\)'
+	 */
+	function escapeRegExp(string) {
+	  string = toString(string);
+	  return (string && reHasRegExpChar.test(string))
+	    ? string.replace(reRegExpChar, '\\$&')
+	    : string;
+	}
+ 
+	module.exports = escapeRegExp;
+ 
+ 
+/***/ }),
+/* 64 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsArguments = __webpack_require__(37),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/** Built-in value references. */
+	var propertyIsEnumerable = objectProto.propertyIsEnumerable;
+ 
+	/**
+	 * Checks if `value` is likely an `arguments` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 *  else `false`.
+	 * @example
+	 *
+	 * _.isArguments(function() { return arguments; }());
+	 * // => true
+	 *
+	 * _.isArguments([1, 2, 3]);
+	 * // => false
+	 */
+	var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
+	  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
+	    !propertyIsEnumerable.call(value, 'callee');
+	};
+ 
+	module.exports = isArguments;
+ 
+ 
+/***/ }),
+/* 65 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(1),
+	    stubFalse = __webpack_require__(70);
+ 
+	/** Detect free variable `exports`. */
+	var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+ 
+	/** Detect free variable `module`. */
+	var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+ 
+	/** Detect the popular CommonJS extension `module.exports`. */
+	var moduleExports = freeModule && freeModule.exports === freeExports;
+ 
+	/** Built-in value references. */
+	var Buffer = moduleExports ? root.Buffer : undefined;
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
+ 
+	/**
+	 * Checks if `value` is a buffer.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.3.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
+	 * @example
+	 *
+	 * _.isBuffer(new Buffer(2));
+	 * // => true
+	 *
+	 * _.isBuffer(new Uint8Array(2));
+	 * // => false
+	 */
+	var isBuffer = nativeIsBuffer || stubFalse;
+ 
+	module.exports = isBuffer;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(20)(module)))
+ 
+/***/ }),
+/* 66 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseKeys = __webpack_require__(41),
+	    getTag = __webpack_require__(49),
+	    isArguments = __webpack_require__(64),
+	    isArray = __webpack_require__(15),
+	    isArrayLike = __webpack_require__(16),
+	    isBuffer = __webpack_require__(65),
+	    isPrototype = __webpack_require__(13),
+	    isTypedArray = __webpack_require__(67);
+ 
+	/** `Object#toString` result references. */
+	var mapTag = '[object Map]',
+	    setTag = '[object Set]';
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * Checks if `value` is an empty object, collection, map, or set.
+	 *
+	 * Objects are considered empty if they have no own enumerable string keyed
+	 * properties.
+	 *
+	 * Array-like values such as `arguments` objects, arrays, buffers, strings, or
+	 * jQuery-like collections are considered empty if they have a `length` of `0`.
+	 * Similarly, maps and sets are considered empty if they have a `size` of `0`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+	 * @example
+	 *
+	 * _.isEmpty(null);
+	 * // => true
+	 *
+	 * _.isEmpty(true);
+	 * // => true
+	 *
+	 * _.isEmpty(1);
+	 * // => true
+	 *
+	 * _.isEmpty([1, 2, 3]);
+	 * // => false
+	 *
+	 * _.isEmpty({ 'a': 1 });
+	 * // => false
+	 */
+	function isEmpty(value) {
+	  if (value == null) {
+	    return true;
+	  }
+	  if (isArrayLike(value) &&
+	      (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
+	        isBuffer(value) || isTypedArray(value) || isArguments(value))) {
+	    return !value.length;
+	  }
+	  var tag = getTag(value);
+	  if (tag == mapTag || tag == setTag) {
+	    return !value.size;
+	  }
+	  if (isPrototype(value)) {
+	    return !baseKeys(value).length;
+	  }
+	  for (var key in value) {
+	    if (hasOwnProperty.call(value, key)) {
+	      return false;
+	    }
+	  }
+	  return true;
+	}
+ 
+	module.exports = isEmpty;
+ 
+ 
+/***/ }),
+/* 67 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsTypedArray = __webpack_require__(40),
+	    baseUnary = __webpack_require__(44),
+	    nodeUtil = __webpack_require__(56);
+ 
+	/* Node.js helper references. */
+	var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
+ 
+	/**
+	 * Checks if `value` is classified as a typed array.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+	 * @example
+	 *
+	 * _.isTypedArray(new Uint8Array);
+	 * // => true
+	 *
+	 * _.isTypedArray([]);
+	 * // => false
+	 */
+	var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+ 
+	module.exports = isTypedArray;
+ 
+ 
+/***/ }),
+/* 68 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Gets the last element of `array`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Array
+	 * @param {Array} array The array to query.
+	 * @returns {*} Returns the last element of `array`.
+	 * @example
+	 *
+	 * _.last([1, 2, 3]);
+	 * // => 3
+	 */
+	function last(array) {
+	  var length = array == null ? 0 : array.length;
+	  return length ? array[length - 1] : undefined;
+	}
+ 
+	module.exports = last;
+ 
+ 
+/***/ }),
+/* 69 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseRepeat = __webpack_require__(42),
+	    isIterateeCall = __webpack_require__(53),
+	    toInteger = __webpack_require__(72),
+	    toString = __webpack_require__(10);
+ 
+	/**
+	 * Repeats the given string `n` times.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to repeat.
+	 * @param {number} [n=1] The number of times to repeat the string.
+	 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+	 * @returns {string} Returns the repeated string.
+	 * @example
+	 *
+	 * _.repeat('*', 3);
+	 * // => '***'
+	 *
+	 * _.repeat('abc', 2);
+	 * // => 'abcabc'
+	 *
+	 * _.repeat('abc', 0);
+	 * // => ''
+	 */
+	function repeat(string, n, guard) {
+	  if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {
+	    n = 1;
+	  } else {
+	    n = toInteger(n);
+	  }
+	  return baseRepeat(toString(string), n);
+	}
+ 
+	module.exports = repeat;
+ 
+ 
+/***/ }),
+/* 70 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * This method returns `false`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.13.0
+	 * @category Util
+	 * @returns {boolean} Returns `false`.
+	 * @example
+	 *
+	 * _.times(2, _.stubFalse);
+	 * // => [false, false]
+	 */
+	function stubFalse() {
+	  return false;
+	}
+ 
+	module.exports = stubFalse;
+ 
+ 
+/***/ }),
+/* 71 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toNumber = __webpack_require__(73);
+ 
+	/** Used as references for various `Number` constants. */
+	var INFINITY = 1 / 0,
+	    MAX_INTEGER = 1.7976931348623157e+308;
+ 
+	/**
+	 * Converts `value` to a finite number.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.12.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {number} Returns the converted number.
+	 * @example
+	 *
+	 * _.toFinite(3.2);
+	 * // => 3.2
+	 *
+	 * _.toFinite(Number.MIN_VALUE);
+	 * // => 5e-324
+	 *
+	 * _.toFinite(Infinity);
+	 * // => 1.7976931348623157e+308
+	 *
+	 * _.toFinite('3.2');
+	 * // => 3.2
+	 */
+	function toFinite(value) {
+	  if (!value) {
+	    return value === 0 ? value : 0;
+	  }
+	  value = toNumber(value);
+	  if (value === INFINITY || value === -INFINITY) {
+	    var sign = (value < 0 ? -1 : 1);
+	    return sign * MAX_INTEGER;
+	  }
+	  return value === value ? value : 0;
+	}
+ 
+	module.exports = toFinite;
+ 
+ 
+/***/ }),
+/* 72 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toFinite = __webpack_require__(71);
+ 
+	/**
+	 * Converts `value` to an integer.
+	 *
+	 * **Note:** This method is loosely based on
+	 * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {number} Returns the converted integer.
+	 * @example
+	 *
+	 * _.toInteger(3.2);
+	 * // => 3
+	 *
+	 * _.toInteger(Number.MIN_VALUE);
+	 * // => 0
+	 *
+	 * _.toInteger(Infinity);
+	 * // => 1.7976931348623157e+308
+	 *
+	 * _.toInteger('3.2');
+	 * // => 3
+	 */
+	function toInteger(value) {
+	  var result = toFinite(value),
+	      remainder = result % 1;
+ 
+	  return result === result ? (remainder ? result - remainder : result) : 0;
+	}
+ 
+	module.exports = toInteger;
+ 
+ 
+/***/ }),
+/* 73 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isObject = __webpack_require__(6),
+	    isSymbol = __webpack_require__(19);
+ 
+	/** Used as references for various `Number` constants. */
+	var NAN = 0 / 0;
+ 
+	/** Used to match leading and trailing whitespace. */
+	var reTrim = /^\s+|\s+$/g;
+ 
+	/** Used to detect bad signed hexadecimal string values. */
+	var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+ 
+	/** Used to detect binary string values. */
+	var reIsBinary = /^0b[01]+$/i;
+ 
+	/** Used to detect octal string values. */
+	var reIsOctal = /^0o[0-7]+$/i;
+ 
+	/** Built-in method references without a dependency on `root`. */
+	var freeParseInt = parseInt;
+ 
+	/**
+	 * Converts `value` to a number.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to process.
+	 * @returns {number} Returns the number.
+	 * @example
+	 *
+	 * _.toNumber(3.2);
+	 * // => 3.2
+	 *
+	 * _.toNumber(Number.MIN_VALUE);
+	 * // => 5e-324
+	 *
+	 * _.toNumber(Infinity);
+	 * // => Infinity
+	 *
+	 * _.toNumber('3.2');
+	 * // => 3.2
+	 */
+	function toNumber(value) {
+	  if (typeof value == 'number') {
+	    return value;
+	  }
+	  if (isSymbol(value)) {
+	    return NAN;
+	  }
+	  if (isObject(value)) {
+	    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+	    value = isObject(other) ? (other + '') : other;
+	  }
+	  if (typeof value != 'string') {
+	    return value === 0 ? value : +value;
+	  }
+	  value = value.replace(reTrim, '');
+	  var isBinary = reIsBinary.test(value);
+	  return (isBinary || reIsOctal.test(value))
+	    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+	    : (reIsBadHex.test(value) ? NAN : +value);
+	}
+ 
+	module.exports = toNumber;
+ 
+ 
+/***/ }),
+/* 74 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseToString = __webpack_require__(11),
+	    castSlice = __webpack_require__(45),
+	    charsEndIndex = __webpack_require__(46),
+	    stringToArray = __webpack_require__(60),
+	    toString = __webpack_require__(10);
+ 
+	/** Used to match leading and trailing whitespace. */
+	var reTrimEnd = /\s+$/;
+ 
+	/**
+	 * Removes trailing whitespace or specified characters from `string`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to trim.
+	 * @param {string} [chars=whitespace] The characters to trim.
+	 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+	 * @returns {string} Returns the trimmed string.
+	 * @example
+	 *
+	 * _.trimEnd('  abc  ');
+	 * // => '  abc'
+	 *
+	 * _.trimEnd('-_-abc-_-', '_-');
+	 * // => '-_-abc'
+	 */
+	function trimEnd(string, chars, guard) {
+	  string = toString(string);
+	  if (string && (guard || chars === undefined)) {
+	    return string.replace(reTrimEnd, '');
+	  }
+	  if (!string || !(chars = baseToString(chars))) {
+	    return string;
+	  }
+	  var strSymbols = stringToArray(string),
+	      end = charsEndIndex(strSymbols, stringToArray(chars)) + 1;
+ 
+	  return castSlice(strSymbols, 0, end).join('');
+	}
+ 
+	module.exports = trimEnd;
+ 
+ 
+/***/ })
+/******/ ])
+});
+;
\ No newline at end of file
diff --git a/src/utils/sqlFormatter.js b/src/utils/sqlFormatter.js
new file mode 100644
index 0000000..0dd99e8
--- /dev/null
+++ b/src/utils/sqlFormatter.js
@@ -0,0 +1,3387 @@
+/* eslint-disable */
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(typeof exports === 'object' && typeof module === 'object')
+		module.exports = factory();
+	else if(typeof define === 'function' && define.amd)
+		define([], factory);
+	else if(typeof exports === 'object')
+		exports["sqlFormatter"] = factory();
+	else
+		root["sqlFormatter"] = factory();
+})(this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+ 
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+ 
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId])
+/******/ 			return installedModules[moduleId].exports;
+ 
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			exports: {},
+/******/ 			id: moduleId,
+/******/ 			loaded: false
+/******/ 		};
+ 
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+ 
+/******/ 		// Flag the module as loaded
+/******/ 		module.loaded = true;
+ 
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+ 
+ 
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+ 
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+ 
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+ 
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(0);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Db2Formatter = __webpack_require__(24);
+ 
+	var _Db2Formatter2 = _interopRequireDefault(_Db2Formatter);
+ 
+	var _N1qlFormatter = __webpack_require__(25);
+ 
+	var _N1qlFormatter2 = _interopRequireDefault(_N1qlFormatter);
+ 
+	var _PlSqlFormatter = __webpack_require__(26);
+ 
+	var _PlSqlFormatter2 = _interopRequireDefault(_PlSqlFormatter);
+ 
+	var _StandardSqlFormatter = __webpack_require__(27);
+ 
+	var _StandardSqlFormatter2 = _interopRequireDefault(_StandardSqlFormatter);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	exports["default"] = {
+	    /**
+	     * Format whitespaces in a query to make it easier to read.
+	     *
+	     * @param {String} query
+	     * @param {Object} cfg
+	     *  @param {String} cfg.language Query language, default is Standard SQL
+	     *  @param {String} cfg.indent Characters used for indentation, default is "  " (2 spaces)
+	     *  @param {Object} cfg.params Collection of params for placeholder replacement
+	     * @return {String}
+	     */
+	    format: function format(query, cfg) {
+	        cfg = cfg || {};
+ 
+	        switch (cfg.language) {
+	            case "db2":
+	                return new _Db2Formatter2["default"](cfg).format(query);
+	            case "n1ql":
+	                return new _N1qlFormatter2["default"](cfg).format(query);
+	            case "pl/sql":
+	                return new _PlSqlFormatter2["default"](cfg).format(query);
+	            case "sql":
+	            case undefined:
+	                return new _StandardSqlFormatter2["default"](cfg).format(query);
+	            default:
+	                throw Error("Unsupported SQL dialect: " + cfg.language);
+	        }
+	    }
+	};
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 1 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var freeGlobal = __webpack_require__(12);
+ 
+	/** Detect free variable `self`. */
+	var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+ 
+	/** Used as a reference to the global object. */
+	var root = freeGlobal || freeSelf || Function('return this')();
+ 
+	module.exports = root;
+ 
+ 
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9),
+	    getRawTag = __webpack_require__(48),
+	    objectToString = __webpack_require__(57);
+ 
+	/** `Object#toString` result references. */
+	var nullTag = '[object Null]',
+	    undefinedTag = '[object Undefined]';
+ 
+	/** Built-in value references. */
+	var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+ 
+	/**
+	 * The base implementation of `getTag` without fallbacks for buggy environments.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the `toStringTag`.
+	 */
+	function baseGetTag(value) {
+	  if (value == null) {
+	    return value === undefined ? undefinedTag : nullTag;
+	  }
+	  return (symToStringTag && symToStringTag in Object(value))
+	    ? getRawTag(value)
+	    : objectToString(value);
+	}
+ 
+	module.exports = baseGetTag;
+ 
+ 
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsNative = __webpack_require__(39),
+	    getValue = __webpack_require__(50);
+ 
+	/**
+	 * Gets the native function at `key` of `object`.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @param {string} key The key of the method to get.
+	 * @returns {*} Returns the function if it's native, else `undefined`.
+	 */
+	function getNative(object, key) {
+	  var value = getValue(object, key);
+	  return baseIsNative(value) ? value : undefined;
+	}
+ 
+	module.exports = getNative;
+ 
+ 
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _trimEnd = __webpack_require__(74);
+ 
+	var _trimEnd2 = _interopRequireDefault(_trimEnd);
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	var _Indentation = __webpack_require__(21);
+ 
+	var _Indentation2 = _interopRequireDefault(_Indentation);
+ 
+	var _InlineBlock = __webpack_require__(22);
+ 
+	var _InlineBlock2 = _interopRequireDefault(_InlineBlock);
+ 
+	var _Params = __webpack_require__(23);
+ 
+	var _Params2 = _interopRequireDefault(_Params);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var Formatter = function () {
+	    /**
+	     * @param {Object} cfg
+	     *   @param {Object} cfg.indent
+	     *   @param {Object} cfg.params
+	     * @param {Tokenizer} tokenizer
+	     */
+	    function Formatter(cfg, tokenizer) {
+	        _classCallCheck(this, Formatter);
+ 
+	        this.cfg = cfg || {};
+	        this.indentation = new _Indentation2["default"](this.cfg.indent);
+	        this.inlineBlock = new _InlineBlock2["default"]();
+	        this.params = new _Params2["default"](this.cfg.params);
+	        this.tokenizer = tokenizer;
+	        this.previousReservedWord = {};
+	        this.tokens = [];
+	        this.index = 0;
+	    }
+ 
+	    /**
+	     * Formats whitespaces in a SQL string to make it easier to read.
+	     *
+	     * @param {String} query The SQL query string
+	     * @return {String} formatted query
+	     */
+ 
+ 
+	    Formatter.prototype.format = function format(query) {
+					this.tokens = this.tokenizer.tokenize(query);
+	        var formattedQuery = this.getFormattedQueryFromTokens();
+	        return formattedQuery.trim();
+	    };
+ 
+	    Formatter.prototype.getFormattedQueryFromTokens = function getFormattedQueryFromTokens() {
+	        var _this = this;
+	        var formattedQuery = "";
+	        this.tokens.forEach(function (token, index) {
+	            _this.index = index;
+	            if (token.type === _tokenTypes2["default"].WHITESPACE) {
+	                // ignore (we do our own whitespace formatting)
+	            } else if (token.type === _tokenTypes2["default"].LINE_COMMENT) {
+	                formattedQuery = _this.formatLineComment(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].BLOCK_COMMENT) {
+	                formattedQuery = _this.formatBlockComment(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].RESERVED_TOPLEVEL) {
+	                formattedQuery = _this.formatToplevelReservedWord(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            } else if (token.type === _tokenTypes2["default"].RESERVED_NEWLINE) {
+	                formattedQuery = _this.formatNewlineReservedWord(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            }else if (token.type === _tokenTypes2["default"].RESERVED) {
+	                formattedQuery = _this.formatWithSpaces(token, formattedQuery);
+	                _this.previousReservedWord = token;
+	            } else if (token.type === _tokenTypes2["default"].OPEN_PAREN) {
+	                formattedQuery = _this.formatOpeningParentheses(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].CLOSE_PAREN) {
+	                formattedQuery = _this.formatClosingParentheses(token, formattedQuery);
+	            } else if (token.type === _tokenTypes2["default"].PLACEHOLDER) {
+	                formattedQuery = _this.formatPlaceholder(token, formattedQuery);
+	            }   else if (token.value === '$') {
+	                formattedQuery = _this.formatNewWithSpaces(token, formattedQuery);
+	            } else if (token.value === ",") {
+	                formattedQuery = _this.formatComma(token, formattedQuery);
+	            } else if (token.value === ":") {
+	                formattedQuery = _this.formatWithSpaceAfter(token, formattedQuery);
+	            } else if (token.value === ".") {
+	                formattedQuery = _this.formatWithoutSpaces(token, formattedQuery);
+	            } else if (token.value === ";") {
+	                formattedQuery = _this.formatQuerySeparator(token, formattedQuery);
+	            } else {
+	                formattedQuery = _this.formatWithSpaces(token, formattedQuery);
+	            }
+	        });
+	        return formattedQuery;
+	    };
+ 
+	    Formatter.prototype.formatLineComment = function formatLineComment(token, query) {
+	        return this.addNewline(query + token.value);
+	    };
+ 
+	    Formatter.prototype.formatBlockComment = function formatBlockComment(token, query) {
+	        return this.addNewline(this.addNewline(query) + this.indentComment(token.value));
+	    };
+ 
+	    Formatter.prototype.indentComment = function indentComment(comment) {
+	        return comment.replace(/\n/g, "\n" + this.indentation.getIndent());
+	    };
+ 
+	    Formatter.prototype.formatToplevelReservedWord = function formatToplevelReservedWord(token, query) {
+	        this.indentation.decreaseTopLevel();
+	        query = this.addNewline(query);
+ 
+					this.indentation.increaseToplevel();
+					// 椤剁骇涓嶅姞绌烘牸
+					// query += " " + this.equalizeWhitespace(token.value);
+	        query += this.equalizeWhitespace(token.value);
+			// return this.addNewline(query);
+			if (token.type === _tokenTypes2["default"].RESERVED_TOPLEVEL) {
+				return this.addNewLineReserved(query);
+			} else {
+				return this.addNewline(query);
+			}
+	    };
+ 
+	    Formatter.prototype.formatNewlineReservedWord = function formatNewlineReservedWord(token, query) {
+	        return this.addNewline(query) + this.equalizeWhitespace(token.value) + " ";
+	    };
+ 
+	    // Replace any sequence of whitespace characters with single space
+ 
+ 
+	    Formatter.prototype.equalizeWhitespace = function equalizeWhitespace(string) {
+	        return string.replace(/\s+/g, " ");
+	    };
+ 
+	    // Opening parentheses increase the block indent level and start a new line
+ 
+ 
+	    Formatter.prototype.formatOpeningParentheses = function formatOpeningParentheses(token, query) {
+	        // Take out the preceding space unless there was whitespace there in the original query
+	        // or another opening parens or line comment
+	        var preserveWhitespaceFor = [_tokenTypes2["default"].WHITESPACE, _tokenTypes2["default"].OPEN_PAREN, _tokenTypes2["default"].LINE_COMMENT];
+	        if (!preserveWhitespaceFor.includes(this.previousToken().type)) {
+	            query = (0, _trimEnd2["default"])(query);
+	        }
+	        query += token.value;
+ 
+	        this.inlineBlock.beginIfPossible(this.tokens, this.index);
+ 
+	        if (!this.inlineBlock.isActive()) {
+	            this.indentation.increaseBlockLevel();
+	            query = this.addNewline(query);
+	        }
+	        return query;
+	    };
+ 
+	    // Closing parentheses decrease the block indent level
+ 
+ 
+	    Formatter.prototype.formatClosingParentheses = function formatClosingParentheses(token, query) {
+	        if (this.inlineBlock.isActive()) {
+	            this.inlineBlock.end();
+	            return this.formatWithSpaceAfter(token, query);
+	        } else {
+	            this.indentation.decreaseBlockLevel();
+	            return this.formatWithSpaces(token, this.addNewline(query));
+	        }
+	    };
+ 
+	    Formatter.prototype.formatPlaceholder = function formatPlaceholder(token, query) {
+	        return query + this.params.get(token) + " ";
+	    };
+ 
+	    // Commas start a new line (unless within inline parentheses or SQL "LIMIT" clause)
+ 
+ 
+	    Formatter.prototype.formatComma = function formatComma(token, query) {
+	        query = this.trimTrailingWhitespace(query) + token.value + " ";
+ 
+	        if (this.inlineBlock.isActive()) {
+	            return query;
+	        } else if (/^LIMIT$/i.test(this.previousReservedWord.value)) {
+	            return query;
+	        } else {
+	            return this.addNewline(query);
+	        }
+	    };
+ 
+	    Formatter.prototype.formatWithSpaceAfter = function formatWithSpaceAfter(token, query) {
+	        return this.trimTrailingWhitespace(query) + token.value + " ";
+	    };
+ 
+	    Formatter.prototype.formatWithoutSpaces = function formatWithoutSpaces(token, query) {
+	        return this.trimTrailingWhitespace(query) + token.value;
+	    };
+ 
+	    Formatter.prototype.formatWithSpaces = function formatWithSpaces(token, query) {
+	        return query + token.value + " ";
+		};
+		// $绗﹀彿鍚庨潰涓庡叧閿瓧涔嬮棿涓嶅姞绌烘牸
+		Formatter.prototype.formatNewWithSpaces = function formatNewWithSpaces(token, query) {
+				return query + token.value;
+		};
+
+		Formatter.prototype.formatQuerySeparator = function formatQuerySeparator(token, query) {
+				return this.trimTrailingWhitespace(query) + token.value + "\n";
+		};
+
+		Formatter.prototype.addNewline = function addNewline(query) {
+				return (0, _trimEnd2["default"])(query) + "\n" + this.indentation.getIndent();
+		};
+		// 鍏抽敭瀛楀悗闈笉鐩存帴鎹㈣锛屽叧閿瓧鍚�2涓┖鏍�
+		Formatter.prototype.addNewLineReserved = function addNewLineReserved(query) {
+			return (0, _trimEnd2["default"])(query) + "  ";
+			// return (0, _trimEnd2["default"])(query) + this.indentation.getIndent();
+		};
+ 
+		Formatter.prototype.trimTrailingWhitespace = function trimTrailingWhitespace(query) {
+			if (this.previousNonWhitespaceToken().type === _tokenTypes2["default"].LINE_COMMENT) {
+					return (0, _trimEnd2["default"])(query) + "\n";
+			} else {
+					return (0, _trimEnd2["default"])(query);
+			}
+		};
+
+		Formatter.prototype.previousNonWhitespaceToken = function previousNonWhitespaceToken() {
+				var n = 1;
+				while (this.previousToken(n).type === _tokenTypes2["default"].WHITESPACE) {
+						n++;
+				}
+				return this.previousToken(n);
+		};
+
+		Formatter.prototype.previousToken = function previousToken() {
+				var offset = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
+
+				return this.tokens[this.index - offset] || {};
+		};
+
+		return Formatter;
+	}();
+ 
+	exports["default"] = Formatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _isEmpty = __webpack_require__(66);
+ 
+	var _isEmpty2 = _interopRequireDefault(_isEmpty);
+ 
+	var _escapeRegExp = __webpack_require__(63);
+ 
+	var _escapeRegExp2 = _interopRequireDefault(_escapeRegExp);
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var Tokenizer = function () {
+	    /**
+	     * @param {Object} cfg
+	     *  @param {String[]} cfg.reservedWords Reserved words in SQL
+	     *  @param {String[]} cfg.reservedToplevelWords Words that are set to new line separately
+	     *  @param {String[]} cfg.reservedNewlineWords Words that are set to newline
+	     *  @param {String[]} cfg.stringTypes String types to enable: "", '', ``, [], N''
+	     *  @param {String[]} cfg.openParens Opening parentheses to enable, like (, [
+	     *  @param {String[]} cfg.closeParens Closing parentheses to enable, like ), ]
+	     *  @param {String[]} cfg.indexedPlaceholderTypes Prefixes for indexed placeholders, like ?
+	     *  @param {String[]} cfg.namedPlaceholderTypes Prefixes for named placeholders, like @ and :
+	     *  @param {String[]} cfg.lineCommentTypes Line comments to enable, like # and --
+	     *  @param {String[]} cfg.specialWordChars Special chars that can be found inside of words, like @ and #
+	     */
+	    function Tokenizer(cfg) {
+	        _classCallCheck(this, Tokenizer);
+ 
+	        this.WHITESPACE_REGEX = /^(\s+)/;
+	        this.NUMBER_REGEX = /^((-\s*)?[0-9]+(\.[0-9]+)?|0x[0-9a-fA-F]+|0b[01]+)\b/;
+	        this.OPERATOR_REGEX = /^(!=|<>|==|<=|>=|!<|!>|\|\||::|->>|->|~~\*|~~|!~~\*|!~~|~\*|!~\*|!~|.)/;
+ 
+	        this.BLOCK_COMMENT_REGEX = /^(\/\*[^]*?(?:\*\/|$))/;
+	        this.LINE_COMMENT_REGEX = this.createLineCommentRegex(cfg.lineCommentTypes);
+ 
+	        this.RESERVED_TOPLEVEL_REGEX = this.createReservedWordRegex(cfg.reservedToplevelWords);
+	        this.RESERVED_NEWLINE_REGEX = this.createReservedWordRegex(cfg.reservedNewlineWords);
+	        this.RESERVED_PLAIN_REGEX = this.createReservedWordRegex(cfg.reservedWords);
+ 
+	        this.WORD_REGEX = this.createWordRegex(cfg.specialWordChars);
+	        this.STRING_REGEX = this.createStringRegex(cfg.stringTypes);
+ 
+	        this.OPEN_PAREN_REGEX = this.createParenRegex(cfg.openParens);
+	        this.CLOSE_PAREN_REGEX = this.createParenRegex(cfg.closeParens);
+ 
+	        this.INDEXED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.indexedPlaceholderTypes, "[0-9]*");
+	        this.IDENT_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, "[a-zA-Z0-9._$]+");
+	        this.STRING_NAMED_PLACEHOLDER_REGEX = this.createPlaceholderRegex(cfg.namedPlaceholderTypes, this.createStringPattern(cfg.stringTypes));
+	    }
+ 
+	    Tokenizer.prototype.createLineCommentRegex = function createLineCommentRegex(lineCommentTypes) {
+	        return new RegExp("^((?:" + lineCommentTypes.map(function (c) {
+	            return (0, _escapeRegExp2["default"])(c);
+	        }).join("|") + ").*?(?:\n|$))");
+	    };
+ 
+	    Tokenizer.prototype.createReservedWordRegex = function createReservedWordRegex(reservedWords) {
+	        var reservedWordsPattern = reservedWords.join("|").replace(/ /g, "\\s+");
+	        return new RegExp("^(" + reservedWordsPattern + ")\\b", "i");
+	    };
+ 
+	    Tokenizer.prototype.createWordRegex = function createWordRegex() {
+	        var specialChars = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
+ 
+	        return new RegExp("^([\\w" + specialChars.join("") + "]+)");
+	    };
+ 
+	    Tokenizer.prototype.createStringRegex = function createStringRegex(stringTypes) {
+	        return new RegExp("^(" + this.createStringPattern(stringTypes) + ")");
+	    };
+ 
+	    // This enables the following string patterns:
+	    // 1. backtick quoted string using `` to escape
+	    // 2. square bracket quoted string (SQL Server) using ]] to escape
+	    // 3. double quoted string using "" or \" to escape
+	    // 4. single quoted string using '' or \' to escape
+	    // 5. national character quoted string using N'' or N\' to escape
+ 
+ 
+	    Tokenizer.prototype.createStringPattern = function createStringPattern(stringTypes) {
+	        var patterns = {
+	            "``": "((`[^`]*($|`))+)",
+	            "[]": "((\\[[^\\]]*($|\\]))(\\][^\\]]*($|\\]))*)",
+	            "\"\"": "((\"[^\"\\\\]*(?:\\\\.[^\"\\\\]*)*(\"|$))+)",
+	            "''": "(('[^'\\\\]*(?:\\\\.[^'\\\\]*)*('|$))+)",
+	            "N''": "((N'[^N'\\\\]*(?:\\\\.[^N'\\\\]*)*('|$))+)"
+	        };
+ 
+	        return stringTypes.map(function (t) {
+	            return patterns[t];
+	        }).join("|");
+	    };
+ 
+	    Tokenizer.prototype.createParenRegex = function createParenRegex(parens) {
+	        var _this = this;
+ 
+	        return new RegExp("^(" + parens.map(function (p) {
+	            return _this.escapeParen(p);
+	        }).join("|") + ")", "i");
+	    };
+ 
+	    Tokenizer.prototype.escapeParen = function escapeParen(paren) {
+	        if (paren.length === 1) {
+	            // A single punctuation character
+	            return (0, _escapeRegExp2["default"])(paren);
+	        } else {
+	            // longer word
+	            return "\\b" + paren + "\\b";
+	        }
+	    };
+ 
+	    Tokenizer.prototype.createPlaceholderRegex = function createPlaceholderRegex(types, pattern) {
+	        if ((0, _isEmpty2["default"])(types)) {
+	            return false;
+	        }
+	        var typesRegex = types.map(_escapeRegExp2["default"]).join("|");
+ 
+	        return new RegExp("^((?:" + typesRegex + ")(?:" + pattern + "))");
+	    };
+ 
+	    /**
+	     * Takes a SQL string and breaks it into tokens.
+	     * Each token is an object with type and value.
+	     *
+	     * @param {String} input The SQL string
+	     * @return {Object[]} tokens An array of tokens.
+	     *  @return {String} token.type
+	     *  @return {String} token.value
+	     */
+ 
+ 
+	    Tokenizer.prototype.tokenize = function tokenize(input) {
+	        var tokens = [];
+	        var token = void 0;
+ 
+	        // Keep processing the string until it is empty
+	        while (input.length) {
+	            // Get the next token and the token type
+	            token = this.getNextToken(input, token);
+	            // Advance the string
+	            input = input.substring(token.value.length);
+ 
+	            tokens.push(token);
+	        }
+	        return tokens;
+	    };
+ 
+	    Tokenizer.prototype.getNextToken = function getNextToken(input, previousToken) {
+	        return this.getWhitespaceToken(input) || this.getCommentToken(input) || this.getStringToken(input) || this.getOpenParenToken(input) || this.getCloseParenToken(input) || this.getPlaceholderToken(input) || this.getNumberToken(input) || this.getReservedWordToken(input, previousToken) || this.getWordToken(input) || this.getOperatorToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getWhitespaceToken = function getWhitespaceToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].WHITESPACE,
+	            regex: this.WHITESPACE_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getCommentToken = function getCommentToken(input) {
+	        return this.getLineCommentToken(input) || this.getBlockCommentToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getLineCommentToken = function getLineCommentToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].LINE_COMMENT,
+	            regex: this.LINE_COMMENT_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getBlockCommentToken = function getBlockCommentToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].BLOCK_COMMENT,
+	            regex: this.BLOCK_COMMENT_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getStringToken = function getStringToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].STRING,
+	            regex: this.STRING_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getOpenParenToken = function getOpenParenToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].OPEN_PAREN,
+	            regex: this.OPEN_PAREN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getCloseParenToken = function getCloseParenToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].CLOSE_PAREN,
+	            regex: this.CLOSE_PAREN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlaceholderToken = function getPlaceholderToken(input) {
+	        return this.getIdentNamedPlaceholderToken(input) || this.getStringNamedPlaceholderToken(input) || this.getIndexedPlaceholderToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getIdentNamedPlaceholderToken = function getIdentNamedPlaceholderToken(input) {
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.IDENT_NAMED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return v.slice(1);
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getStringNamedPlaceholderToken = function getStringNamedPlaceholderToken(input) {
+	        var _this2 = this;
+ 
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.STRING_NAMED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return _this2.getEscapedPlaceholderKey({ key: v.slice(2, -1), quoteChar: v.slice(-1) });
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getIndexedPlaceholderToken = function getIndexedPlaceholderToken(input) {
+	        return this.getPlaceholderTokenWithKey({
+	            input: input,
+	            regex: this.INDEXED_PLACEHOLDER_REGEX,
+	            parseKey: function parseKey(v) {
+	                return v.slice(1);
+	            }
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlaceholderTokenWithKey = function getPlaceholderTokenWithKey(_ref) {
+	        var input = _ref.input,
+	            regex = _ref.regex,
+	            parseKey = _ref.parseKey;
+ 
+	        var token = this.getTokenOnFirstMatch({ input: input, regex: regex, type: _tokenTypes2["default"].PLACEHOLDER });
+	        if (token) {
+	            token.key = parseKey(token.value);
+	        }
+	        return token;
+	    };
+ 
+	    Tokenizer.prototype.getEscapedPlaceholderKey = function getEscapedPlaceholderKey(_ref2) {
+	        var key = _ref2.key,
+	            quoteChar = _ref2.quoteChar;
+ 
+	        return key.replace(new RegExp((0, _escapeRegExp2["default"])("\\") + quoteChar, "g"), quoteChar);
+	    };
+ 
+	    // Decimal, binary, or hex numbers
+ 
+ 
+	    Tokenizer.prototype.getNumberToken = function getNumberToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].NUMBER,
+	            regex: this.NUMBER_REGEX
+	        });
+	    };
+ 
+	    // Punctuation and symbols
+ 
+ 
+	    Tokenizer.prototype.getOperatorToken = function getOperatorToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].OPERATOR,
+	            regex: this.OPERATOR_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getReservedWordToken = function getReservedWordToken(input, previousToken) {
+	        // A reserved word cannot be preceded by a "."
+	        // this makes it so in "mytable.from", "from" is not considered a reserved word
+	        if (previousToken && previousToken.value && previousToken.value === ".") {
+	            return;
+	        }
+	        return this.getToplevelReservedToken(input) || this.getNewlineReservedToken(input) || this.getPlainReservedToken(input);
+	    };
+ 
+	    Tokenizer.prototype.getToplevelReservedToken = function getToplevelReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED_TOPLEVEL,
+	            regex: this.RESERVED_TOPLEVEL_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getNewlineReservedToken = function getNewlineReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED_NEWLINE,
+	            regex: this.RESERVED_NEWLINE_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getPlainReservedToken = function getPlainReservedToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].RESERVED,
+	            regex: this.RESERVED_PLAIN_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getWordToken = function getWordToken(input) {
+	        return this.getTokenOnFirstMatch({
+	            input: input,
+	            type: _tokenTypes2["default"].WORD,
+	            regex: this.WORD_REGEX
+	        });
+	    };
+ 
+	    Tokenizer.prototype.getTokenOnFirstMatch = function getTokenOnFirstMatch(_ref3) {
+	        var input = _ref3.input,
+	            type = _ref3.type,
+	            regex = _ref3.regex;
+ 
+	        var matches = input.match(regex);
+ 
+	        if (matches) {
+	            return { type: type, value: matches[1] };
+	        }
+	    };
+ 
+	    return Tokenizer;
+	}();
+ 
+	exports["default"] = Tokenizer;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 6 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is the
+	 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+	 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+	 * @example
+	 *
+	 * _.isObject({});
+	 * // => true
+	 *
+	 * _.isObject([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isObject(_.noop);
+	 * // => true
+	 *
+	 * _.isObject(null);
+	 * // => false
+	 */
+	function isObject(value) {
+	  var type = typeof value;
+	  return value != null && (type == 'object' || type == 'function');
+	}
+ 
+	module.exports = isObject;
+ 
+ 
+/***/ }),
+/* 7 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is object-like. A value is object-like if it's not `null`
+	 * and has a `typeof` result of "object".
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
+	 * @example
+	 *
+	 * _.isObjectLike({});
+	 * // => true
+	 *
+	 * _.isObjectLike([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isObjectLike(_.noop);
+	 * // => false
+	 *
+	 * _.isObjectLike(null);
+	 * // => false
+	 */
+	function isObjectLike(value) {
+	  return value != null && typeof value == 'object';
+	}
+ 
+	module.exports = isObjectLike;
+ 
+ 
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+	/**
+	 * Constants for token types
+	 */
+	exports["default"] = {
+	    WHITESPACE: "whitespace",
+	    WORD: "word",
+	    STRING: "string",
+	    RESERVED: "reserved",
+	    RESERVED_TOPLEVEL: "reserved-toplevel",
+	    RESERVED_NEWLINE: "reserved-newline",
+	    OPERATOR: "operator",
+	    OPEN_PAREN: "open-paren",
+	    CLOSE_PAREN: "close-paren",
+	    LINE_COMMENT: "line-comment",
+	    BLOCK_COMMENT: "block-comment",
+	    NUMBER: "number",
+	    PLACEHOLDER: "placeholder"
+	};
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var root = __webpack_require__(1);
+ 
+	/** Built-in value references. */
+	var Symbol = root.Symbol;
+ 
+	module.exports = Symbol;
+ 
+ 
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseToString = __webpack_require__(11);
+ 
+	/**
+	 * Converts `value` to a string. An empty string is returned for `null`
+	 * and `undefined` values. The sign of `-0` is preserved.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {string} Returns the converted string.
+	 * @example
+	 *
+	 * _.toString(null);
+	 * // => ''
+	 *
+	 * _.toString(-0);
+	 * // => '-0'
+	 *
+	 * _.toString([1, 2, 3]);
+	 * // => '1,2,3'
+	 */
+	function toString(value) {
+	  return value == null ? '' : baseToString(value);
+	}
+ 
+	module.exports = toString;
+ 
+ 
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9),
+	    arrayMap = __webpack_require__(33),
+	    isArray = __webpack_require__(15),
+	    isSymbol = __webpack_require__(19);
+ 
+	/** Used as references for various `Number` constants. */
+	var INFINITY = 1 / 0;
+ 
+	/** Used to convert symbols to primitives and strings. */
+	var symbolProto = Symbol ? Symbol.prototype : undefined,
+	    symbolToString = symbolProto ? symbolProto.toString : undefined;
+ 
+	/**
+	 * The base implementation of `_.toString` which doesn't convert nullish
+	 * values to empty strings.
+	 *
+	 * @private
+	 * @param {*} value The value to process.
+	 * @returns {string} Returns the string.
+	 */
+	function baseToString(value) {
+	  // Exit early for strings to avoid a performance hit in some environments.
+	  if (typeof value == 'string') {
+	    return value;
+	  }
+	  if (isArray(value)) {
+	    // Recursively convert values (susceptible to call stack limits).
+	    return arrayMap(value, baseToString) + '';
+	  }
+	  if (isSymbol(value)) {
+	    return symbolToString ? symbolToString.call(value) : '';
+	  }
+	  var result = (value + '');
+	  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
+	}
+ 
+	module.exports = baseToString;
+ 
+ 
+/***/ }),
+/* 12 */
+/***/ (function(module, exports) {
+ 
+	/* WEBPACK VAR INJECTION */(function(global) {/** Detect free variable `global` from Node.js. */
+	var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+ 
+	module.exports = freeGlobal;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, (function() { return this; }())))
+ 
+/***/ }),
+/* 13 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/**
+	 * Checks if `value` is likely a prototype object.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
+	 */
+	function isPrototype(value) {
+	  var Ctor = value && value.constructor,
+	      proto = (typeof Ctor == 'function' && Ctor.prototype) || objectProto;
+ 
+	  return value === proto;
+	}
+ 
+	module.exports = isPrototype;
+ 
+ 
+/***/ }),
+/* 14 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var funcProto = Function.prototype;
+ 
+	/** Used to resolve the decompiled source of functions. */
+	var funcToString = funcProto.toString;
+ 
+	/**
+	 * Converts `func` to its source code.
+	 *
+	 * @private
+	 * @param {Function} func The function to convert.
+	 * @returns {string} Returns the source code.
+	 */
+	function toSource(func) {
+	  if (func != null) {
+	    try {
+	      return funcToString.call(func);
+	    } catch (e) {}
+	    try {
+	      return (func + '');
+	    } catch (e) {}
+	  }
+	  return '';
+	}
+ 
+	module.exports = toSource;
+ 
+ 
+/***/ }),
+/* 15 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Checks if `value` is classified as an `Array` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
+	 * @example
+	 *
+	 * _.isArray([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isArray(document.body.children);
+	 * // => false
+	 *
+	 * _.isArray('abc');
+	 * // => false
+	 *
+	 * _.isArray(_.noop);
+	 * // => false
+	 */
+	var isArray = Array.isArray;
+ 
+	module.exports = isArray;
+ 
+ 
+/***/ }),
+/* 16 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isFunction = __webpack_require__(17),
+	    isLength = __webpack_require__(18);
+ 
+	/**
+	 * Checks if `value` is array-like. A value is considered array-like if it's
+	 * not a function and has a `value.length` that's an integer greater than or
+	 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
+	 * @example
+	 *
+	 * _.isArrayLike([1, 2, 3]);
+	 * // => true
+	 *
+	 * _.isArrayLike(document.body.children);
+	 * // => true
+	 *
+	 * _.isArrayLike('abc');
+	 * // => true
+	 *
+	 * _.isArrayLike(_.noop);
+	 * // => false
+	 */
+	function isArrayLike(value) {
+	  return value != null && isLength(value.length) && !isFunction(value);
+	}
+ 
+	module.exports = isArrayLike;
+ 
+ 
+/***/ }),
+/* 17 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObject = __webpack_require__(6);
+ 
+	/** `Object#toString` result references. */
+	var asyncTag = '[object AsyncFunction]',
+	    funcTag = '[object Function]',
+	    genTag = '[object GeneratorFunction]',
+	    proxyTag = '[object Proxy]';
+ 
+	/**
+	 * Checks if `value` is classified as a `Function` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+	 * @example
+	 *
+	 * _.isFunction(_);
+	 * // => true
+	 *
+	 * _.isFunction(/abc/);
+	 * // => false
+	 */
+	function isFunction(value) {
+	  if (!isObject(value)) {
+	    return false;
+	  }
+	  // The use of `Object#toString` avoids issues with the `typeof` operator
+	  // in Safari 9 which returns 'object' for typed arrays and other constructors.
+	  var tag = baseGetTag(value);
+	  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
+	}
+ 
+	module.exports = isFunction;
+ 
+ 
+/***/ }),
+/* 18 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/**
+	 * Checks if `value` is a valid array-like length.
+	 *
+	 * **Note:** This method is loosely based on
+	 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
+	 * @example
+	 *
+	 * _.isLength(3);
+	 * // => true
+	 *
+	 * _.isLength(Number.MIN_VALUE);
+	 * // => false
+	 *
+	 * _.isLength(Infinity);
+	 * // => false
+	 *
+	 * _.isLength('3');
+	 * // => false
+	 */
+	function isLength(value) {
+	  return typeof value == 'number' &&
+	    value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
+	}
+ 
+	module.exports = isLength;
+ 
+ 
+/***/ }),
+/* 19 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var symbolTag = '[object Symbol]';
+ 
+	/**
+	 * Checks if `value` is classified as a `Symbol` primitive or object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
+	 * @example
+	 *
+	 * _.isSymbol(Symbol.iterator);
+	 * // => true
+	 *
+	 * _.isSymbol('abc');
+	 * // => false
+	 */
+	function isSymbol(value) {
+	  return typeof value == 'symbol' ||
+	    (isObjectLike(value) && baseGetTag(value) == symbolTag);
+	}
+ 
+	module.exports = isSymbol;
+ 
+ 
+/***/ }),
+/* 20 */
+/***/ (function(module, exports) {
+ 
+	module.exports = function(module) {
+		if(!module.webpackPolyfill) {
+			module.deprecate = function() {};
+			module.paths = [];
+			// module.parent = undefined by default
+			module.children = [];
+			module.webpackPolyfill = 1;
+		}
+		return module;
+	}
+ 
+ 
+/***/ }),
+/* 21 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _repeat = __webpack_require__(69);
+ 
+	var _repeat2 = _interopRequireDefault(_repeat);
+ 
+	var _last = __webpack_require__(68);
+ 
+	var _last2 = _interopRequireDefault(_last);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var INDENT_TYPE_TOP_LEVEL = "top-level";
+	var INDENT_TYPE_BLOCK_LEVEL = "block-level";
+ 
+	/**
+	 * Manages indentation levels.
+	 *
+	 * There are two types of indentation levels:
+	 *
+	 * - BLOCK_LEVEL : increased by open-parenthesis
+	 * - TOP_LEVEL : increased by RESERVED_TOPLEVEL words
+	 */
+ 
+	var Indentation = function () {
+	    /**
+	     * @param {String} indent Indent value, default is "  " (2 spaces)
+	     */
+	    function Indentation(indent) {
+	        _classCallCheck(this, Indentation);
+ 
+					// 榛樿缂╄繘6涓┖鏍�
+	        this.indent = indent || "      ";
+	        // this.indent = indent || "  ";
+	        this.indentTypes = [];
+	    }
+ 
+	    /**
+	     * Returns current indentation string.
+	     * @return {String}
+	     */
+ 
+ 
+	    Indentation.prototype.getIndent = function getIndent() {
+	        return (0, _repeat2["default"])(this.indent, this.indentTypes.length);
+	    };
+ 
+	    /**
+	     * Increases indentation by one top-level indent.
+	     */
+ 
+ 
+	    Indentation.prototype.increaseToplevel = function increaseToplevel() {
+	        this.indentTypes.push(INDENT_TYPE_TOP_LEVEL);
+	    };
+ 
+	    /**
+	     * Increases indentation by one block-level indent.
+	     */
+ 
+ 
+	    Indentation.prototype.increaseBlockLevel = function increaseBlockLevel() {
+	        this.indentTypes.push(INDENT_TYPE_BLOCK_LEVEL);
+	    };
+ 
+	    /**
+	     * Decreases indentation by one top-level indent.
+	     * Does nothing when the previous indent is not top-level.
+	     */
+ 
+ 
+	    Indentation.prototype.decreaseTopLevel = function decreaseTopLevel() {
+	        if ((0, _last2["default"])(this.indentTypes) === INDENT_TYPE_TOP_LEVEL) {
+	            this.indentTypes.pop();
+	        }
+	    };
+ 
+	    /**
+	     * Decreases indentation by one block-level indent.
+	     * If there are top-level indents within the block-level indent,
+	     * throws away these as well.
+	     */
+ 
+ 
+	    Indentation.prototype.decreaseBlockLevel = function decreaseBlockLevel() {
+	        while (this.indentTypes.length > 0) {
+	            var type = this.indentTypes.pop();
+	            if (type !== INDENT_TYPE_TOP_LEVEL) {
+	                break;
+	            }
+	        }
+	    };
+ 
+	    return Indentation;
+	}();
+ 
+	exports["default"] = Indentation;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 22 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _tokenTypes = __webpack_require__(8);
+ 
+	var _tokenTypes2 = _interopRequireDefault(_tokenTypes);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var INLINE_MAX_LENGTH = 50;
+ 
+	/**
+	 * Bookkeeper for inline blocks.
+	 *
+	 * Inline blocks are parenthized expressions that are shorter than INLINE_MAX_LENGTH.
+	 * These blocks are formatted on a single line, unlike longer parenthized
+	 * expressions where open-parenthesis causes newline and increase of indentation.
+	 */
+ 
+	var InlineBlock = function () {
+	    function InlineBlock() {
+	        _classCallCheck(this, InlineBlock);
+ 
+	        this.level = 0;
+	    }
+ 
+	    /**
+	     * Begins inline block when lookahead through upcoming tokens determines
+	     * that the block would be smaller than INLINE_MAX_LENGTH.
+	     * @param  {Object[]} tokens Array of all tokens
+	     * @param  {Number} index Current token position
+	     */
+ 
+ 
+	    InlineBlock.prototype.beginIfPossible = function beginIfPossible(tokens, index) {
+	        if (this.level === 0 && this.isInlineBlock(tokens, index)) {
+	            this.level = 1;
+	        } else if (this.level > 0) {
+	            this.level++;
+	        } else {
+	            this.level = 0;
+	        }
+	    };
+ 
+	    /**
+	     * Finishes current inline block.
+	     * There might be several nested ones.
+	     */
+ 
+ 
+	    InlineBlock.prototype.end = function end() {
+	        this.level--;
+	    };
+ 
+	    /**
+	     * True when inside an inline block
+	     * @return {Boolean}
+	     */
+ 
+ 
+	    InlineBlock.prototype.isActive = function isActive() {
+	        return this.level > 0;
+	    };
+ 
+	    // Check if this should be an inline parentheses block
+	    // Examples are "NOW()", "COUNT(*)", "int(10)", key(`somecolumn`), DECIMAL(7,2)
+ 
+ 
+	    InlineBlock.prototype.isInlineBlock = function isInlineBlock(tokens, index) {
+	        var length = 0;
+	        var level = 0;
+ 
+	        for (var i = index; i < tokens.length; i++) {
+	            var token = tokens[i];
+	            length += token.value.length;
+ 
+	            // Overran max length
+	            if (length > INLINE_MAX_LENGTH) {
+	                return false;
+	            }
+ 
+	            if (token.type === _tokenTypes2["default"].OPEN_PAREN) {
+	                level++;
+	            } else if (token.type === _tokenTypes2["default"].CLOSE_PAREN) {
+	                level--;
+	                if (level === 0) {
+	                    return true;
+	                }
+	            }
+ 
+	            if (this.isForbiddenToken(token)) {
+	                return false;
+	            }
+	        }
+	        return false;
+	    };
+ 
+	    // Reserved words that cause newlines, comments and semicolons
+	    // are not allowed inside inline parentheses block
+ 
+ 
+	    InlineBlock.prototype.isForbiddenToken = function isForbiddenToken(_ref) {
+	        var type = _ref.type,
+	            value = _ref.value;
+ 
+	        return type === _tokenTypes2["default"].RESERVED_TOPLEVEL || type === _tokenTypes2["default"].RESERVED_NEWLINE || type === _tokenTypes2["default"].COMMENT || type === _tokenTypes2["default"].BLOCK_COMMENT || value === ";";
+	    };
+ 
+	    return InlineBlock;
+	}();
+ 
+	exports["default"] = InlineBlock;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 23 */
+/***/ (function(module, exports) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	/**
+	 * Handles placeholder replacement with given params.
+	 */
+	var Params = function () {
+	    /**
+	     * @param {Object} params
+	     */
+	    function Params(params) {
+	        _classCallCheck(this, Params);
+ 
+	        this.params = params;
+	        this.index = 0;
+	    }
+ 
+	    /**
+	     * Returns param value that matches given placeholder with param key.
+	     * @param {Object} token
+	     *   @param {String} token.key Placeholder key
+	     *   @param {String} token.value Placeholder value
+	     * @return {String} param or token.value when params are missing
+	     */
+ 
+ 
+	    Params.prototype.get = function get(_ref) {
+	        var key = _ref.key,
+	            value = _ref.value;
+ 
+	        if (!this.params) {
+	            return value;
+	        }
+	        if (key) {
+	            return this.params[key];
+	        }
+	        return this.params[this.index++];
+	    };
+ 
+	    return Params;
+	}();
+ 
+	exports["default"] = Params;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 24 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["ABS", "ACTIVATE", "ALIAS", "ALL", "ALLOCATE", "ALLOW", "ALTER", "ANY", "ARE", "ARRAY", "AS", "ASC", "ASENSITIVE", "ASSOCIATE", "ASUTIME", "ASYMMETRIC", "AT", "ATOMIC", "ATTRIBUTES", "AUDIT", "AUTHORIZATION", "AUX", "AUXILIARY", "AVG", "BEFORE", "BEGIN", "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOOLEAN", "BOTH", "BUFFERPOOL", "BY", "CACHE", "CALL", "CALLED", "CAPTURE", "CARDINALITY", "CASCADED", "CASE", "CAST", "CCSID", "CEIL", "CEILING", "CHAR", "CHARACTER", "CHARACTER_LENGTH", "CHAR_LENGTH", "CHECK", "CLOB", "CLONE", "CLOSE", "CLUSTER", "COALESCE", "COLLATE", "COLLECT", "COLLECTION", "COLLID", "COLUMN", "COMMENT", "COMMIT", "CONCAT", "CONDITION", "CONNECT", "CONNECTION", "CONSTRAINT", "CONTAINS", "CONTINUE", "CONVERT", "CORR", "CORRESPONDING", "COUNT", "COUNT_BIG", "COVAR_POP", "COVAR_SAMP", "CREATE", "CROSS", "CUBE", "CUME_DIST", "CURRENT", "CURRENT_DATE", "CURRENT_DEFAULT_TRANSFORM_GROUP", "CURRENT_LC_CTYPE", "CURRENT_PATH", "CURRENT_ROLE", "CURRENT_SCHEMA", "CURRENT_SERVER", "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_TIMEZONE", "CURRENT_TRANSFORM_GROUP_FOR_TYPE", "CURRENT_USER", "CURSOR", "CYCLE", "DATA", "DATABASE", "DATAPARTITIONNAME", "DATAPARTITIONNUM", "DATE", "DAY", "DAYS", "DB2GENERAL", "DB2GENRL", "DB2SQL", "DBINFO", "DBPARTITIONNAME", "DBPARTITIONNUM", "DEALLOCATE", "DEC", "DECIMAL", "DECLARE", "DEFAULT", "DEFAULTS", "DEFINITION", "DELETE", "DENSERANK", "DENSE_RANK", "DEREF", "DESCRIBE", "DESCRIPTOR", "DETERMINISTIC", "DIAGNOSTICS", "DISABLE", "DISALLOW", "DISCONNECT", "DISTINCT", "DO", "DOCUMENT", "DOUBLE", "DROP", "DSSIZE", "DYNAMIC", "EACH", "EDITPROC", "ELEMENT", "ELSE", "ELSEIF", "ENABLE", "ENCODING", "ENCRYPTION", "END", "END-EXEC", "ENDING", "ERASE", "ESCAPE", "EVERY", "EXCEPTION", "EXCLUDING", "EXCLUSIVE", "EXEC", "EXECUTE", "EXISTS", "EXIT", "EXP", "EXPLAIN", "EXTENDED", "EXTERNAL", "EXTRACT", "FALSE", "FENCED", "FETCH", "FIELDPROC", "FILE", "FILTER", "FINAL", "FIRST", "FLOAT", "FLOOR", "FOR", "FOREIGN", "FREE", "FULL", "FUNCTION", "FUSION", "GENERAL", "GENERATED", "GET", "GLOBAL", "GOTO", "GRANT", "GRAPHIC", "GROUP", "GROUPING", "HANDLER", "HASH", "HASHED_VALUE", "HINT", "HOLD", "HOUR", "HOURS", "IDENTITY", "IF", "IMMEDIATE", "IN", "INCLUDING", "INCLUSIVE", "INCREMENT", "INDEX", "INDICATOR", "INDICATORS", "INF", "INFINITY", "INHERIT", "INNER", "INOUT", "INSENSITIVE", "INSERT", "INT", "INTEGER", "INTEGRITY", "INTERSECTION", "INTERVAL", "INTO", "IS", "ISOBID", "ISOLATION", "ITERATE", "JAR", "JAVA", "KEEP", "KEY", "LABEL", "LANGUAGE", "LARGE", "LATERAL", "LC_CTYPE", "LEADING", "LEAVE", "LEFT", "LIKE", "LINKTYPE", "LN", "LOCAL", "LOCALDATE", "LOCALE", "LOCALTIME", "LOCALTIMESTAMP", "LOCATOR", "LOCATORS", "LOCK", "LOCKMAX", "LOCKSIZE", "LONG", "LOOP", "LOWER", "MAINTAINED", "MATCH", "MATERIALIZED", "MAX", "MAXVALUE", "MEMBER", "MERGE", "METHOD", "MICROSECOND", "MICROSECONDS", "MIN", "MINUTE", "MINUTES", "MINVALUE", "MOD", "MODE", "MODIFIES", "MODULE", "MONTH", "MONTHS", "MULTISET", "NAN", "NATIONAL", "NATURAL", "NCHAR", "NCLOB", "NEW", "NEW_TABLE", "NEXTVAL", "NO", "NOCACHE", "NOCYCLE", "NODENAME", "NODENUMBER", "NOMAXVALUE", "NOMINVALUE", "NONE", "NOORDER", "NORMALIZE", "NORMALIZED", "NOT", "NULL", "NULLIF", "NULLS", "NUMERIC", "NUMPARTS", "OBID", "OCTET_LENGTH", "OF", "OFFSET", "OLD", "OLD_TABLE", "ON", "ONLY", "OPEN", "OPTIMIZATION", "OPTIMIZE", "OPTION", "ORDER", "OUT", "OUTER", "OVER", "OVERLAPS", "OVERLAY", "OVERRIDING", "PACKAGE", "PADDED", "PAGESIZE", "PARAMETER", "PART", "PARTITION", "PARTITIONED", "PARTITIONING", "PARTITIONS", "PASSWORD", "PATH", "PERCENTILE_CONT", "PERCENTILE_DISC", "PERCENT_RANK", "PIECESIZE", "PLAN", "POSITION", "POWER", "PRECISION", "PREPARE", "PREVVAL", "PRIMARY", "PRIQTY", "PRIVILEGES", "PROCEDURE", "PROGRAM", "PSID", "PUBLIC", "QUERY", "QUERYNO", "RANGE", "RANK", "READ", "READS", "REAL", "RECOVERY", "RECURSIVE", "REF", "REFERENCES", "REFERENCING", "REFRESH", "REGR_AVGX", "REGR_AVGY", "REGR_COUNT", "REGR_INTERCEPT", "REGR_R2", "REGR_SLOPE", "REGR_SXX", "REGR_SXY", "REGR_SYY", "RELEASE", "RENAME", "REPEAT", "RESET", "RESIGNAL", "RESTART", "RESTRICT", "RESULT", "RESULT_SET_LOCATOR", "RETURN", "RETURNS", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "ROLLUP", "ROUND_CEILING", "ROUND_DOWN", "ROUND_FLOOR", "ROUND_HALF_DOWN", "ROUND_HALF_EVEN", "ROUND_HALF_UP", "ROUND_UP", "ROUTINE", "ROW", "ROWNUMBER", "ROWS", "ROWSET", "ROW_NUMBER", "RRN", "RUN", "SAVEPOINT", "SCHEMA", "SCOPE", "SCRATCHPAD", "SCROLL", "SEARCH", "SECOND", "SECONDS", "SECQTY", "SECURITY", "SENSITIVE", "SEQUENCE", "SESSION", "SESSION_USER", "SIGNAL", "SIMILAR", "SIMPLE", "SMALLINT", "SNAN", "SOME", "SOURCE", "SPECIFIC", "SPECIFICTYPE", "SQL", "SQLEXCEPTION", "SQLID", "SQLSTATE", "SQLWARNING", "SQRT", "STACKED", "STANDARD", "START", "STARTING", "STATEMENT", "STATIC", "STATMENT", "STAY", "STDDEV_POP", "STDDEV_SAMP", "STOGROUP", "STORES", "STYLE", "SUBMULTISET", "SUBSTRING", "SUM", "SUMMARY", "SYMMETRIC", "SYNONYM", "SYSFUN", "SYSIBM", "SYSPROC", "SYSTEM", "SYSTEM_USER", "TABLE", "TABLESAMPLE", "TABLESPACE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TO", "TRAILING", "TRANSACTION", "TRANSLATE", "TRANSLATION", "TREAT", "TRIGGER", "TRIM", "TRUE", "TRUNCATE", "TYPE", "UESCAPE", "UNDO", "UNIQUE", "UNKNOWN", "UNNEST", "UNTIL", "UPPER", "USAGE", "USER", "USING", "VALIDPROC", "VALUE", "VARCHAR", "VARIABLE", "VARIANT", "VARYING", "VAR_POP", "VAR_SAMP", "VCAT", "VERSION", "VIEW", "VOLATILE", "VOLUMES", "WHEN", "WHENEVER", "WHILE", "WIDTH_BUCKET", "WINDOW", "WITH", "WITHIN", "WITHOUT", "WLM", "WRITE", "XMLELEMENT", "XMLEXISTS", "XMLNAMESPACES", "YEAR", "YEARS"];
+ 
+	var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INTERSECT", "LIMIT", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UPDATE", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "CROSS JOIN", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN"];
+ 
+	var tokenizer = void 0;
+ 
+	var Db2Formatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function Db2Formatter(cfg) {
+	        _classCallCheck(this, Db2Formatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Formats DB2 query to make it easier to read
+	     *
+	     * @param {String} query The DB2 query string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    Db2Formatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "''", "``", "[]"],
+	                openParens: ["("],
+	                closeParens: [")"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: [":"],
+	                lineCommentTypes: ["--"],
+	                specialWordChars: ["#", "@"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return Db2Formatter;
+	}();
+ 
+	exports["default"] = Db2Formatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 25 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["ALL", "ALTER", "ANALYZE", "AND", "ANY", "ARRAY", "AS", "ASC", "BEGIN", "BETWEEN", "BINARY", "BOOLEAN", "BREAK", "BUCKET", "BUILD", "BY", "CALL", "CASE", "CAST", "CLUSTER", "COLLATE", "COLLECTION", "COMMIT", "CONNECT", "CONTINUE", "CORRELATE", "COVER", "CREATE", "DATABASE", "DATASET", "DATASTORE", "DECLARE", "DECREMENT", "DELETE", "DERIVED", "DESC", "DESCRIBE", "DISTINCT", "DO", "DROP", "EACH", "ELEMENT", "ELSE", "END", "EVERY", "EXCEPT", "EXCLUDE", "EXECUTE", "EXISTS", "EXPLAIN", "FALSE", "FETCH", "FIRST", "FLATTEN", "FOR", "FORCE", "FROM", "FUNCTION", "GRANT", "GROUP", "GSI", "HAVING", "IF", "IGNORE", "ILIKE", "IN", "INCLUDE", "INCREMENT", "INDEX", "INFER", "INLINE", "INNER", "INSERT", "INTERSECT", "INTO", "IS", "JOIN", "KEY", "KEYS", "KEYSPACE", "KNOWN", "LAST", "LEFT", "LET", "LETTING", "LIKE", "LIMIT", "LSM", "MAP", "MAPPING", "MATCHED", "MATERIALIZED", "MERGE", "MINUS", "MISSING", "NAMESPACE", "NEST", "NOT", "NULL", "NUMBER", "OBJECT", "OFFSET", "ON", "OPTION", "OR", "ORDER", "OUTER", "OVER", "PARSE", "PARTITION", "PASSWORD", "PATH", "POOL", "PREPARE", "PRIMARY", "PRIVATE", "PRIVILEGE", "PROCEDURE", "PUBLIC", "RAW", "REALM", "REDUCE", "RENAME", "RETURN", "RETURNING", "REVOKE", "RIGHT", "ROLE", "ROLLBACK", "SATISFIES", "SCHEMA", "SELECT", "SELF", "SEMI", "SET", "SHOW", "SOME", "START", "STATISTICS", "STRING", "SYSTEM", "THEN", "TO", "TRANSACTION", "TRIGGER", "TRUE", "TRUNCATE", "UNDER", "UNION", "UNIQUE", "UNKNOWN", "UNNEST", "UNSET", "UPDATE", "UPSERT", "USE", "USER", "USING", "VALIDATE", "VALUE", "VALUED", "VALUES", "VIA", "VIEW", "WHEN", "WHERE", "WHILE", "WITH", "WITHIN", "WORK", "XOR"];
+ 
+	var reservedToplevelWords = ["DELETE FROM", "EXCEPT ALL", "EXCEPT", "EXPLAIN DELETE FROM", "EXPLAIN UPDATE", "EXPLAIN UPSERT", "FROM", "GROUP BY", "HAVING", "INFER", "INSERT INTO", "INTERSECT ALL", "INTERSECT", "LET", "LIMIT", "MERGE", "NEST", "ORDER BY", "PREPARE", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UNNEST", "UPDATE", "UPSERT", "USE KEYS", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var N1qlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function N1qlFormatter(cfg) {
+	        _classCallCheck(this, N1qlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a N1QL string to make it easier to read
+	     *
+	     * @param {String} query The N1QL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    N1qlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "''", "``"],
+	                openParens: ["(", "[", "{"],
+	                closeParens: [")", "]", "}"],
+	                namedPlaceholderTypes: ["$"],
+	                lineCommentTypes: ["#", "--"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return N1qlFormatter;
+	}();
+ 
+	exports["default"] = N1qlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 26 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	var reservedWords = ["A", "ACCESSIBLE", "AGENT", "AGGREGATE", "ALL", "ALTER", "ANY", "ARRAY", "AS", "ASC", "AT", "ATTRIBUTE", "AUTHID", "AVG", "BETWEEN", "BFILE_BASE", "BINARY_INTEGER", "BINARY", "BLOB_BASE", "BLOCK", "BODY", "BOOLEAN", "BOTH", "BOUND", "BULK", "BY", "BYTE", "C", "CALL", "CALLING", "CASCADE", "CASE", "CHAR_BASE", "CHAR", "CHARACTER", "CHARSET", "CHARSETFORM", "CHARSETID", "CHECK", "CLOB_BASE", "CLONE", "CLOSE", "CLUSTER", "CLUSTERS", "COALESCE", "COLAUTH", "COLLECT", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPILED", "COMPRESS", "CONNECT", "CONSTANT", "CONSTRUCTOR", "CONTEXT", "CONTINUE", "CONVERT", "COUNT", "CRASH", "CREATE", "CREDENTIAL", "CURRENT", "CURRVAL", "CURSOR", "CUSTOMDATUM", "DANGLING", "DATA", "DATE_BASE", "DATE", "DAY", "DECIMAL", "DEFAULT", "DEFINE", "DELETE", "DESC", "DETERMINISTIC", "DIRECTORY", "DISTINCT", "DO", "DOUBLE", "DROP", "DURATION", "ELEMENT", "ELSIF", "EMPTY", "ESCAPE", "EXCEPTIONS", "EXCLUSIVE", "EXECUTE", "EXISTS", "EXIT", "EXTENDS", "EXTERNAL", "EXTRACT", "FALSE", "FETCH", "FINAL", "FIRST", "FIXED", "FLOAT", "FOR", "FORALL", "FORCE", "FROM", "FUNCTION", "GENERAL", "GOTO", "GRANT", "GROUP", "HASH", "HEAP", "HIDDEN", "HOUR", "IDENTIFIED", "IF", "IMMEDIATE", "IN", "INCLUDING", "INDEX", "INDEXES", "INDICATOR", "INDICES", "INFINITE", "INSTANTIABLE", "INT", "INTEGER", "INTERFACE", "INTERVAL", "INTO", "INVALIDATE", "IS", "ISOLATION", "JAVA", "LANGUAGE", "LARGE", "LEADING", "LENGTH", "LEVEL", "LIBRARY", "LIKE", "LIKE2", "LIKE4", "LIKEC", "LIMITED", "LOCAL", "LOCK", "LONG", "MAP", "MAX", "MAXLEN", "MEMBER", "MERGE", "MIN", "MINUS", "MINUTE", "MLSLABEL", "MOD", "MODE", "MONTH", "MULTISET", "NAME", "NAN", "NATIONAL", "NATIVE", "NATURAL", "NATURALN", "NCHAR", "NEW", "NEXTVAL", "NOCOMPRESS", "NOCOPY", "NOT", "NOWAIT", "NULL", "NULLIF", "NUMBER_BASE", "NUMBER", "OBJECT", "OCICOLL", "OCIDATE", "OCIDATETIME", "OCIDURATION", "OCIINTERVAL", "OCILOBLOCATOR", "OCINUMBER", "OCIRAW", "OCIREF", "OCIREFCURSOR", "OCIROWID", "OCISTRING", "OCITYPE", "OF", "OLD", "ON", "ONLY", "OPAQUE", "OPEN", "OPERATOR", "OPTION", "ORACLE", "ORADATA", "ORDER", "ORGANIZATION", "ORLANY", "ORLVARY", "OTHERS", "OUT", "OVERLAPS", "OVERRIDING", "PACKAGE", "PARALLEL_ENABLE", "PARAMETER", "PARAMETERS", "PARENT", "PARTITION", "PASCAL", "PCTFREE", "PIPE", "PIPELINED", "PLS_INTEGER", "PLUGGABLE", "POSITIVE", "POSITIVEN", "PRAGMA", "PRECISION", "PRIOR", "PRIVATE", "PROCEDURE", "PUBLIC", "RAISE", "RANGE", "RAW", "READ", "REAL", "RECORD", "REF", "REFERENCE", "RELEASE", "RELIES_ON", "REM", "REMAINDER", "RENAME", "RESOURCE", "RESULT_CACHE", "RESULT", "RETURN", "RETURNING", "REVERSE", "REVOKE", "ROLLBACK", "ROW", "ROWID", "ROWNUM", "ROWTYPE", "SAMPLE", "SAVE", "SAVEPOINT", "SB1", "SB2", "SB4", "SECOND", "SEGMENT", "SELF", "SEPARATE", "SEQUENCE", "SERIALIZABLE", "SHARE", "SHORT", "SIZE_T", "SIZE", "SMALLINT", "SOME", "SPACE", "SPARSE", "SQL", "SQLCODE", "SQLDATA", "SQLERRM", "SQLNAME", "SQLSTATE", "STANDARD", "START", "STATIC", "STDDEV", "STORED", "STRING", "STRUCT", "STYLE", "SUBMULTISET", "SUBPARTITION", "SUBSTITUTABLE", "SUBTYPE", "SUCCESSFUL", "SUM", "SYNONYM", "SYSDATE", "TABAUTH", "TABLE", "TDO", "THE", "THEN", "TIME", "TIMESTAMP", "TIMEZONE_ABBR", "TIMEZONE_HOUR", "TIMEZONE_MINUTE", "TIMEZONE_REGION", "TO", "TRAILING", "TRANSACTION", "TRANSACTIONAL", "TRIGGER", "TRUE", "TRUSTED", "TYPE", "UB1", "UB2", "UB4", "UID", "UNDER", "UNIQUE", "UNPLUG", "UNSIGNED", "UNTRUSTED", "USE", "USER", "USING", "VALIDATE", "VALIST", "VALUE", "VARCHAR", "VARCHAR2", "VARIABLE", "VARIANCE", "VARRAY", "VARYING", "VIEW", "VIEWS", "VOID", "WHENEVER", "WHILE", "WITH", "WORK", "WRAPPED", "WRITE", "YEAR", "ZONE"];
+ 
+	var reservedToplevelWords = ["ADD", "ALTER COLUMN", "ALTER TABLE", "BEGIN", "CONNECT BY", "DECLARE", "DELETE FROM", "DELETE", "END", "EXCEPT", "EXCEPTION", "FETCH FIRST", "FROM", "GROUP BY", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "LOOP", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "START WITH", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"];
+ 
+	var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "END", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var PlSqlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function PlSqlFormatter(cfg) {
+	        _classCallCheck(this, PlSqlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a PL/SQL string to make it easier to read
+	     *
+	     * @param {String} query The PL/SQL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    PlSqlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "N''", "''", "``"],
+	                openParens: ["(", "CASE"],
+	                closeParens: [")", "END"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: [":"],
+	                lineCommentTypes: ["--"],
+	                specialWordChars: ["_", "$", "#", ".", "@"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return PlSqlFormatter;
+	}();
+ 
+	exports["default"] = PlSqlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 27 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	"use strict";
+ 
+	exports.__esModule = true;
+ 
+	var _Formatter = __webpack_require__(4);
+ 
+	var _Formatter2 = _interopRequireDefault(_Formatter);
+ 
+	var _Tokenizer = __webpack_require__(5);
+ 
+	var _Tokenizer2 = _interopRequireDefault(_Tokenizer);
+ 
+	function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; }
+ 
+	function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
+ 
+	// var reservedWords = ["ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", "AUTO_INCREMENT", "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", "CURRENT_TIMESTAMP", "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENDED", "FAST", "FETCH", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", "INTO", "INVOKER", "IS", "ISOLATION", "KEY", "KEYS", "KILL", "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", "NAMES", "NATURAL", "NOT", "NOW()", "NULL", "OFFSET", "ON DELETE", "ON UPDATE", "ON", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", "PURGE", "QUICK", "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", "YEAR_MONTH"];
+ 
+	// var reservedToplevelWords = ["ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "FROM", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UPDATE", "VALUES", "WHERE"];
+ 
+	// var reservedNewlineWords = ["AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"];
+	
+	var reservedWords = ["ACCESSIBLE", "ACTION", "AGAINST", "AGGREGATE", "ALGORITHM", "ALL", "ALTER", "ANALYSE", "ANALYZE", "AS", "ASC", "AUTOCOMMIT", "AUTO_INCREMENT", "BACKUP", "BEGIN", "BETWEEN", "BINLOG", "BOTH", "CASCADE", "CASE", "CHANGE", "CHANGED", "CHARACTER SET", "CHARSET", "CHECK", "CHECKSUM", "COLLATE", "COLLATION", "COLUMN", "COLUMNS", "COMMENT", "COMMIT", "COMMITTED", "COMPRESSED", "CONCURRENT", "CONSTRAINT", "CONTAINS", "CONVERT", "CREATE", "CROSS", "CURRENT_TIMESTAMP", "DATABASE", "DATABASES", "DAY", "DAY_HOUR", "DAY_MINUTE", "DAY_SECOND", "DEFAULT", "DEFINER", "DELAYED", "DELETE", "DESC", "DESCRIBE", "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DO", "DROP", "DUMPFILE", "DUPLICATE", "DYNAMIC", "ELSE", "ENCLOSED", "END", "ENGINE", "ENGINES", "ENGINE_TYPE", "ESCAPE", "ESCAPED", "EVENTS", "EXEC", "EXECUTE", "EXISTS", "EXPLAIN", "EXTENDED", "FAST", "FETCH", "FIELDS", "FILE", "FIRST", "FIXED", "FLUSH", "FOR", "FORCE", "FOREIGN", "FULL", "FULLTEXT", "FUNCTION", "GLOBAL", "GRANT", "GRANTS", "GROUP_CONCAT", "HEAP", "HIGH_PRIORITY", "HOSTS", "HOUR", "HOUR_MINUTE", "HOUR_SECOND", "IDENTIFIED", "IF", "IFNULL", "IGNORE", "IN", "INDEX", "INDEXES", "INFILE", "INSERT", "INSERT_ID", "INSERT_METHOD", "INTERVAL", "INTO", "INVOKER", "IS", "ISOLATION", "KEY", "KEYS", "KILL", "LAST_INSERT_ID", "LEADING", "LEVEL", "LIKE", "LINEAR", "LINES", "LOAD", "LOCAL", "LOCK", "LOCKS", "LOGS", "LOW_PRIORITY", "MARIA", "MASTER", "MASTER_CONNECT_RETRY", "MASTER_HOST", "MASTER_LOG_FILE", "MATCH", "MAX_CONNECTIONS_PER_HOUR", "MAX_QUERIES_PER_HOUR", "MAX_ROWS", "MAX_UPDATES_PER_HOUR", "MAX_USER_CONNECTIONS", "MEDIUM", "MERGE", "MINUTE", "MINUTE_SECOND", "MIN_ROWS", "MODE", "MODIFY", "MONTH", "MRG_MYISAM", "MYISAM", "NAMES", "NATURAL", "NOT", "NOW()", "NULL", "OFFSET", "ON DELETE", "ON UPDATE", "ON", "ONLY", "OPEN", "OPTIMIZE", "OPTION", "OPTIONALLY", "OUTFILE", "PACK_KEYS", "PAGE", "PARTIAL", "PARTITION", "PARTITIONS", "PASSWORD", "PRIMARY", "PRIVILEGES", "PROCEDURE", "PROCESS", "PROCESSLIST", "PURGE", "QUICK", "RAID0", "RAID_CHUNKS", "RAID_CHUNKSIZE", "RAID_TYPE", "RANGE", "READ", "READ_ONLY", "READ_WRITE", "REFERENCES", "REGEXP", "RELOAD", "RENAME", "REPAIR", "REPEATABLE", "REPLACE", "REPLICATION", "RESET", "RESTORE", "RESTRICT", "RETURN", "RETURNS", "REVOKE", "RLIKE", "ROLLBACK", "ROW", "ROWS", "ROW_FORMAT", "SECOND", "SECURITY", "SEPARATOR", "SERIALIZABLE", "SESSION", "SHARE", "SHOW", "SHUTDOWN", "SLAVE", "SONAME", "SOUNDS", "SQL", "SQL_AUTO_IS_NULL", "SQL_BIG_RESULT", "SQL_BIG_SELECTS", "SQL_BIG_TABLES", "SQL_BUFFER_RESULT", "SQL_CACHE", "SQL_CALC_FOUND_ROWS", "SQL_LOG_BIN", "SQL_LOG_OFF", "SQL_LOG_UPDATE", "SQL_LOW_PRIORITY_UPDATES", "SQL_MAX_JOIN_SIZE", "SQL_NO_CACHE", "SQL_QUOTE_SHOW_CREATE", "SQL_SAFE_UPDATES", "SQL_SELECT_LIMIT", "SQL_SLAVE_SKIP_COUNTER", "SQL_SMALL_RESULT", "SQL_WARNINGS", "START", "STARTING", "STATUS", "STOP", "STORAGE", "STRAIGHT_JOIN", "STRING", "STRIPED", "SUPER", "TABLE", "TABLES", "TEMPORARY", "TERMINATED", "THEN", "TO", "TRAILING", "TRANSACTIONAL", "TRUE", "TRUNCATE", "TYPE", "TYPES", "UNCOMMITTED", "UNIQUE", "UNLOCK", "UNSIGNED", "USAGE", "USE", "USING", "VARIABLES", "VIEW", "WHEN", "WITH", "WORK", "WRITE", "YEAR_MONTH"];
+ 
+	var reservedToplevelWords = ["DECLARE", "ADD", "AFTER", "ALTER COLUMN", "ALTER TABLE", "DELETE FROM", "EXCEPT", "FETCH FIRST", "GROUP BY", "GO", "HAVING", "INSERT INTO", "INSERT", "INTERSECT", "LIMIT", "MODIFY", "ORDER BY", "SELECT", "SET CURRENT SCHEMA", "SET SCHEMA", "SET", "UNION ALL", "UNION", "UPDATE", "VALUES"];
+ 
+	var reservedNewlineWords = ["FROM", "WHERE", "AND", "CROSS APPLY", "CROSS JOIN", "ELSE", "INNER JOIN", "JOIN", "LEFT JOIN", "LEFT OUTER JOIN", "OR", "OUTER APPLY", "OUTER JOIN", "RIGHT JOIN", "RIGHT OUTER JOIN", "WHEN", "XOR"];
+ 
+	var tokenizer = void 0;
+ 
+	var StandardSqlFormatter = function () {
+	    /**
+	     * @param {Object} cfg Different set of configurations
+	     */
+	    function StandardSqlFormatter(cfg) {
+	        _classCallCheck(this, StandardSqlFormatter);
+ 
+	        this.cfg = cfg;
+	    }
+ 
+	    /**
+	     * Format the whitespace in a Standard SQL string to make it easier to read
+	     *
+	     * @param {String} query The Standard SQL string
+	     * @return {String} formatted string
+	     */
+ 
+ 
+	    StandardSqlFormatter.prototype.format = function format(query) {
+	        if (!tokenizer) {
+	            tokenizer = new _Tokenizer2["default"]({
+	                reservedWords: reservedWords,
+	                reservedToplevelWords: reservedToplevelWords,
+	                reservedNewlineWords: reservedNewlineWords,
+	                stringTypes: ["\"\"", "N''", "''", "``", "[]"],
+	                openParens: ["(", "CASE"],
+	                closeParens: [")", "END"],
+	                indexedPlaceholderTypes: ["?"],
+	                namedPlaceholderTypes: ["@", ":"],
+	                lineCommentTypes: ["#", "--"]
+	            });
+	        }
+	        return new _Formatter2["default"](this.cfg, tokenizer).format(query);
+	    };
+ 
+	    return StandardSqlFormatter;
+	}();
+ 
+	exports["default"] = StandardSqlFormatter;
+	module.exports = exports["default"];
+ 
+/***/ }),
+/* 28 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var DataView = getNative(root, 'DataView');
+ 
+	module.exports = DataView;
+ 
+ 
+/***/ }),
+/* 29 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Map = getNative(root, 'Map');
+ 
+	module.exports = Map;
+ 
+ 
+/***/ }),
+/* 30 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Promise = getNative(root, 'Promise');
+ 
+	module.exports = Promise;
+ 
+ 
+/***/ }),
+/* 31 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var Set = getNative(root, 'Set');
+ 
+	module.exports = Set;
+ 
+ 
+/***/ }),
+/* 32 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var getNative = __webpack_require__(3),
+	    root = __webpack_require__(1);
+ 
+	/* Built-in method references that are verified to be native. */
+	var WeakMap = getNative(root, 'WeakMap');
+ 
+	module.exports = WeakMap;
+ 
+ 
+/***/ }),
+/* 33 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * A specialized version of `_.map` for arrays without support for iteratee
+	 * shorthands.
+	 *
+	 * @private
+	 * @param {Array} [array] The array to iterate over.
+	 * @param {Function} iteratee The function invoked per iteration.
+	 * @returns {Array} Returns the new mapped array.
+	 */
+	function arrayMap(array, iteratee) {
+	  var index = -1,
+	      length = array == null ? 0 : array.length,
+	      result = Array(length);
+ 
+	  while (++index < length) {
+	    result[index] = iteratee(array[index], index, array);
+	  }
+	  return result;
+	}
+ 
+	module.exports = arrayMap;
+ 
+ 
+/***/ }),
+/* 34 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Converts an ASCII `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function asciiToArray(string) {
+	  return string.split('');
+	}
+ 
+	module.exports = asciiToArray;
+ 
+ 
+/***/ }),
+/* 35 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.findIndex` and `_.findLastIndex` without
+	 * support for iteratee shorthands.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {Function} predicate The function invoked per iteration.
+	 * @param {number} fromIndex The index to search from.
+	 * @param {boolean} [fromRight] Specify iterating from right to left.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function baseFindIndex(array, predicate, fromIndex, fromRight) {
+	  var length = array.length,
+	      index = fromIndex + (fromRight ? 1 : -1);
+ 
+	  while ((fromRight ? index-- : ++index < length)) {
+	    if (predicate(array[index], index, array)) {
+	      return index;
+	    }
+	  }
+	  return -1;
+	}
+ 
+	module.exports = baseFindIndex;
+ 
+ 
+/***/ }),
+/* 36 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseFindIndex = __webpack_require__(35),
+	    baseIsNaN = __webpack_require__(38),
+	    strictIndexOf = __webpack_require__(59);
+ 
+	/**
+	 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {*} value The value to search for.
+	 * @param {number} fromIndex The index to search from.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function baseIndexOf(array, value, fromIndex) {
+	  return value === value
+	    ? strictIndexOf(array, value, fromIndex)
+	    : baseFindIndex(array, baseIsNaN, fromIndex);
+	}
+ 
+	module.exports = baseIndexOf;
+ 
+ 
+/***/ }),
+/* 37 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]';
+ 
+	/**
+	 * The base implementation of `_.isArguments`.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 */
+	function baseIsArguments(value) {
+	  return isObjectLike(value) && baseGetTag(value) == argsTag;
+	}
+ 
+	module.exports = baseIsArguments;
+ 
+ 
+/***/ }),
+/* 38 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.isNaN` without support for number objects.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+	 */
+	function baseIsNaN(value) {
+	  return value !== value;
+	}
+ 
+	module.exports = baseIsNaN;
+ 
+ 
+/***/ }),
+/* 39 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isFunction = __webpack_require__(17),
+	    isMasked = __webpack_require__(54),
+	    isObject = __webpack_require__(6),
+	    toSource = __webpack_require__(14);
+ 
+	/**
+	 * Used to match `RegExp`
+	 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+	 */
+	var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+ 
+	/** Used to detect host constructors (Safari). */
+	var reIsHostCtor = /^\[object .+?Constructor\]$/;
+ 
+	/** Used for built-in method references. */
+	var funcProto = Function.prototype,
+	    objectProto = Object.prototype;
+ 
+	/** Used to resolve the decompiled source of functions. */
+	var funcToString = funcProto.toString;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/** Used to detect if a method is native. */
+	var reIsNative = RegExp('^' +
+	  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+	  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+	);
+ 
+	/**
+	 * The base implementation of `_.isNative` without bad shim checks.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a native function,
+	 *  else `false`.
+	 */
+	function baseIsNative(value) {
+	  if (!isObject(value) || isMasked(value)) {
+	    return false;
+	  }
+	  var pattern = isFunction(value) ? reIsNative : reIsHostCtor;
+	  return pattern.test(toSource(value));
+	}
+ 
+	module.exports = baseIsNative;
+ 
+ 
+/***/ }),
+/* 40 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseGetTag = __webpack_require__(2),
+	    isLength = __webpack_require__(18),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** `Object#toString` result references. */
+	var argsTag = '[object Arguments]',
+	    arrayTag = '[object Array]',
+	    boolTag = '[object Boolean]',
+	    dateTag = '[object Date]',
+	    errorTag = '[object Error]',
+	    funcTag = '[object Function]',
+	    mapTag = '[object Map]',
+	    numberTag = '[object Number]',
+	    objectTag = '[object Object]',
+	    regexpTag = '[object RegExp]',
+	    setTag = '[object Set]',
+	    stringTag = '[object String]',
+	    weakMapTag = '[object WeakMap]';
+ 
+	var arrayBufferTag = '[object ArrayBuffer]',
+	    dataViewTag = '[object DataView]',
+	    float32Tag = '[object Float32Array]',
+	    float64Tag = '[object Float64Array]',
+	    int8Tag = '[object Int8Array]',
+	    int16Tag = '[object Int16Array]',
+	    int32Tag = '[object Int32Array]',
+	    uint8Tag = '[object Uint8Array]',
+	    uint8ClampedTag = '[object Uint8ClampedArray]',
+	    uint16Tag = '[object Uint16Array]',
+	    uint32Tag = '[object Uint32Array]';
+ 
+	/** Used to identify `toStringTag` values of typed arrays. */
+	var typedArrayTags = {};
+	typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
+	typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
+	typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
+	typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
+	typedArrayTags[uint32Tag] = true;
+	typedArrayTags[argsTag] = typedArrayTags[arrayTag] =
+	typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
+	typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
+	typedArrayTags[errorTag] = typedArrayTags[funcTag] =
+	typedArrayTags[mapTag] = typedArrayTags[numberTag] =
+	typedArrayTags[objectTag] = typedArrayTags[regexpTag] =
+	typedArrayTags[setTag] = typedArrayTags[stringTag] =
+	typedArrayTags[weakMapTag] = false;
+ 
+	/**
+	 * The base implementation of `_.isTypedArray` without Node.js optimizations.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+	 */
+	function baseIsTypedArray(value) {
+	  return isObjectLike(value) &&
+	    isLength(value.length) && !!typedArrayTags[baseGetTag(value)];
+	}
+ 
+	module.exports = baseIsTypedArray;
+ 
+ 
+/***/ }),
+/* 41 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isPrototype = __webpack_require__(13),
+	    nativeKeys = __webpack_require__(55);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
+	 *
+	 * @private
+	 * @param {Object} object The object to query.
+	 * @returns {Array} Returns the array of property names.
+	 */
+	function baseKeys(object) {
+	  if (!isPrototype(object)) {
+	    return nativeKeys(object);
+	  }
+	  var result = [];
+	  for (var key in Object(object)) {
+	    if (hasOwnProperty.call(object, key) && key != 'constructor') {
+	      result.push(key);
+	    }
+	  }
+	  return result;
+	}
+ 
+	module.exports = baseKeys;
+ 
+ 
+/***/ }),
+/* 42 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeFloor = Math.floor;
+ 
+	/**
+	 * The base implementation of `_.repeat` which doesn't coerce arguments.
+	 *
+	 * @private
+	 * @param {string} string The string to repeat.
+	 * @param {number} n The number of times to repeat the string.
+	 * @returns {string} Returns the repeated string.
+	 */
+	function baseRepeat(string, n) {
+	  var result = '';
+	  if (!string || n < 1 || n > MAX_SAFE_INTEGER) {
+	    return result;
+	  }
+	  // Leverage the exponentiation by squaring algorithm for a faster repeat.
+	  // See https://en.wikipedia.org/wiki/Exponentiation_by_squaring for more details.
+	  do {
+	    if (n % 2) {
+	      result += string;
+	    }
+	    n = nativeFloor(n / 2);
+	    if (n) {
+	      string += string;
+	    }
+	  } while (n);
+ 
+	  return result;
+	}
+ 
+	module.exports = baseRepeat;
+ 
+ 
+/***/ }),
+/* 43 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.slice` without an iteratee call guard.
+	 *
+	 * @private
+	 * @param {Array} array The array to slice.
+	 * @param {number} [start=0] The start position.
+	 * @param {number} [end=array.length] The end position.
+	 * @returns {Array} Returns the slice of `array`.
+	 */
+	function baseSlice(array, start, end) {
+	  var index = -1,
+	      length = array.length;
+ 
+	  if (start < 0) {
+	    start = -start > length ? 0 : (length + start);
+	  }
+	  end = end > length ? length : end;
+	  if (end < 0) {
+	    end += length;
+	  }
+	  length = start > end ? 0 : ((end - start) >>> 0);
+	  start >>>= 0;
+ 
+	  var result = Array(length);
+	  while (++index < length) {
+	    result[index] = array[index + start];
+	  }
+	  return result;
+	}
+ 
+	module.exports = baseSlice;
+ 
+ 
+/***/ }),
+/* 44 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * The base implementation of `_.unary` without support for storing metadata.
+	 *
+	 * @private
+	 * @param {Function} func The function to cap arguments for.
+	 * @returns {Function} Returns the new capped function.
+	 */
+	function baseUnary(func) {
+	  return function(value) {
+	    return func(value);
+	  };
+	}
+ 
+	module.exports = baseUnary;
+ 
+ 
+/***/ }),
+/* 45 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseSlice = __webpack_require__(43);
+ 
+	/**
+	 * Casts `array` to a slice if it's needed.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {number} start The start position.
+	 * @param {number} [end=array.length] The end position.
+	 * @returns {Array} Returns the cast slice.
+	 */
+	function castSlice(array, start, end) {
+	  var length = array.length;
+	  end = end === undefined ? length : end;
+	  return (!start && end >= length) ? array : baseSlice(array, start, end);
+	}
+ 
+	module.exports = castSlice;
+ 
+ 
+/***/ }),
+/* 46 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIndexOf = __webpack_require__(36);
+ 
+	/**
+	 * Used by `_.trim` and `_.trimEnd` to get the index of the last string symbol
+	 * that is not found in the character symbols.
+	 *
+	 * @private
+	 * @param {Array} strSymbols The string symbols to inspect.
+	 * @param {Array} chrSymbols The character symbols to find.
+	 * @returns {number} Returns the index of the last unmatched string symbol.
+	 */
+	function charsEndIndex(strSymbols, chrSymbols) {
+	  var index = strSymbols.length;
+ 
+	  while (index-- && baseIndexOf(chrSymbols, strSymbols[index], 0) > -1) {}
+	  return index;
+	}
+ 
+	module.exports = charsEndIndex;
+ 
+ 
+/***/ }),
+/* 47 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var root = __webpack_require__(1);
+ 
+	/** Used to detect overreaching core-js shims. */
+	var coreJsData = root['__core-js_shared__'];
+ 
+	module.exports = coreJsData;
+ 
+ 
+/***/ }),
+/* 48 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var Symbol = __webpack_require__(9);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * Used to resolve the
+	 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+	 * of values.
+	 */
+	var nativeObjectToString = objectProto.toString;
+ 
+	/** Built-in value references. */
+	var symToStringTag = Symbol ? Symbol.toStringTag : undefined;
+ 
+	/**
+	 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the raw `toStringTag`.
+	 */
+	function getRawTag(value) {
+	  var isOwn = hasOwnProperty.call(value, symToStringTag),
+	      tag = value[symToStringTag];
+ 
+	  try {
+	    value[symToStringTag] = undefined;
+	    var unmasked = true;
+	  } catch (e) {}
+ 
+	  var result = nativeObjectToString.call(value);
+	  if (unmasked) {
+	    if (isOwn) {
+	      value[symToStringTag] = tag;
+	    } else {
+	      delete value[symToStringTag];
+	    }
+	  }
+	  return result;
+	}
+ 
+	module.exports = getRawTag;
+ 
+ 
+/***/ }),
+/* 49 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var DataView = __webpack_require__(28),
+	    Map = __webpack_require__(29),
+	    Promise = __webpack_require__(30),
+	    Set = __webpack_require__(31),
+	    WeakMap = __webpack_require__(32),
+	    baseGetTag = __webpack_require__(2),
+	    toSource = __webpack_require__(14);
+ 
+	/** `Object#toString` result references. */
+	var mapTag = '[object Map]',
+	    objectTag = '[object Object]',
+	    promiseTag = '[object Promise]',
+	    setTag = '[object Set]',
+	    weakMapTag = '[object WeakMap]';
+ 
+	var dataViewTag = '[object DataView]';
+ 
+	/** Used to detect maps, sets, and weakmaps. */
+	var dataViewCtorString = toSource(DataView),
+	    mapCtorString = toSource(Map),
+	    promiseCtorString = toSource(Promise),
+	    setCtorString = toSource(Set),
+	    weakMapCtorString = toSource(WeakMap);
+ 
+	/**
+	 * Gets the `toStringTag` of `value`.
+	 *
+	 * @private
+	 * @param {*} value The value to query.
+	 * @returns {string} Returns the `toStringTag`.
+	 */
+	var getTag = baseGetTag;
+ 
+	// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
+	if ((DataView && getTag(new DataView(new ArrayBuffer(1))) != dataViewTag) ||
+	    (Map && getTag(new Map) != mapTag) ||
+	    (Promise && getTag(Promise.resolve()) != promiseTag) ||
+	    (Set && getTag(new Set) != setTag) ||
+	    (WeakMap && getTag(new WeakMap) != weakMapTag)) {
+	  getTag = function(value) {
+	    var result = baseGetTag(value),
+	        Ctor = result == objectTag ? value.constructor : undefined,
+	        ctorString = Ctor ? toSource(Ctor) : '';
+ 
+	    if (ctorString) {
+	      switch (ctorString) {
+	        case dataViewCtorString: return dataViewTag;
+	        case mapCtorString: return mapTag;
+	        case promiseCtorString: return promiseTag;
+	        case setCtorString: return setTag;
+	        case weakMapCtorString: return weakMapTag;
+	      }
+	    }
+	    return result;
+	  };
+	}
+ 
+	module.exports = getTag;
+ 
+ 
+/***/ }),
+/* 50 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Gets the value at `key` of `object`.
+	 *
+	 * @private
+	 * @param {Object} [object] The object to query.
+	 * @param {string} key The key of the property to get.
+	 * @returns {*} Returns the property value.
+	 */
+	function getValue(object, key) {
+	  return object == null ? undefined : object[key];
+	}
+ 
+	module.exports = getValue;
+ 
+ 
+/***/ }),
+/* 51 */
+/***/ (function(module, exports) {
+ 
+	/** Used to compose unicode character classes. */
+	var rsAstralRange = '\\ud800-\\udfff',
+	    rsComboMarksRange = '\\u0300-\\u036f',
+	    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+	    rsComboSymbolsRange = '\\u20d0-\\u20ff',
+	    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+	    rsVarRange = '\\ufe0e\\ufe0f';
+ 
+	/** Used to compose unicode capture groups. */
+	var rsZWJ = '\\u200d';
+ 
+	/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
+	var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboRange + rsVarRange + ']');
+ 
+	/**
+	 * Checks if `string` contains Unicode symbols.
+	 *
+	 * @private
+	 * @param {string} string The string to inspect.
+	 * @returns {boolean} Returns `true` if a symbol is found, else `false`.
+	 */
+	function hasUnicode(string) {
+	  return reHasUnicode.test(string);
+	}
+ 
+	module.exports = hasUnicode;
+ 
+ 
+/***/ }),
+/* 52 */
+/***/ (function(module, exports) {
+ 
+	/** Used as references for various `Number` constants. */
+	var MAX_SAFE_INTEGER = 9007199254740991;
+ 
+	/** Used to detect unsigned integer values. */
+	var reIsUint = /^(?:0|[1-9]\d*)$/;
+ 
+	/**
+	 * Checks if `value` is a valid array-like index.
+	 *
+	 * @private
+	 * @param {*} value The value to check.
+	 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
+	 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
+	 */
+	function isIndex(value, length) {
+	  var type = typeof value;
+	  length = length == null ? MAX_SAFE_INTEGER : length;
+ 
+	  return !!length &&
+	    (type == 'number' ||
+	      (type != 'symbol' && reIsUint.test(value))) &&
+	        (value > -1 && value % 1 == 0 && value < length);
+	}
+ 
+	module.exports = isIndex;
+ 
+ 
+/***/ }),
+/* 53 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var eq = __webpack_require__(62),
+	    isArrayLike = __webpack_require__(16),
+	    isIndex = __webpack_require__(52),
+	    isObject = __webpack_require__(6);
+ 
+	/**
+	 * Checks if the given arguments are from an iteratee call.
+	 *
+	 * @private
+	 * @param {*} value The potential iteratee value argument.
+	 * @param {*} index The potential iteratee index or key argument.
+	 * @param {*} object The potential iteratee object argument.
+	 * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
+	 *  else `false`.
+	 */
+	function isIterateeCall(value, index, object) {
+	  if (!isObject(object)) {
+	    return false;
+	  }
+	  var type = typeof index;
+	  if (type == 'number'
+	        ? (isArrayLike(object) && isIndex(index, object.length))
+	        : (type == 'string' && index in object)
+	      ) {
+	    return eq(object[index], value);
+	  }
+	  return false;
+	}
+ 
+	module.exports = isIterateeCall;
+ 
+ 
+/***/ }),
+/* 54 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var coreJsData = __webpack_require__(47);
+ 
+	/** Used to detect methods masquerading as native. */
+	var maskSrcKey = (function() {
+	  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+	  return uid ? ('Symbol(src)_1.' + uid) : '';
+	}());
+ 
+	/**
+	 * Checks if `func` has its source masked.
+	 *
+	 * @private
+	 * @param {Function} func The function to check.
+	 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+	 */
+	function isMasked(func) {
+	  return !!maskSrcKey && (maskSrcKey in func);
+	}
+ 
+	module.exports = isMasked;
+ 
+ 
+/***/ }),
+/* 55 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var overArg = __webpack_require__(58);
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeKeys = overArg(Object.keys, Object);
+ 
+	module.exports = nativeKeys;
+ 
+ 
+/***/ }),
+/* 56 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	/* WEBPACK VAR INJECTION */(function(module) {var freeGlobal = __webpack_require__(12);
+ 
+	/** Detect free variable `exports`. */
+	var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+ 
+	/** Detect free variable `module`. */
+	var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+ 
+	/** Detect the popular CommonJS extension `module.exports`. */
+	var moduleExports = freeModule && freeModule.exports === freeExports;
+ 
+	/** Detect free variable `process` from Node.js. */
+	var freeProcess = moduleExports && freeGlobal.process;
+ 
+	/** Used to access faster Node.js helpers. */
+	var nodeUtil = (function() {
+	  try {
+	    // Use `util.types` for Node.js 10+.
+	    var types = freeModule && freeModule.require && freeModule.require('util').types;
+ 
+	    if (types) {
+	      return types;
+	    }
+ 
+	    // Legacy `process.binding('util')` for Node.js < 10.
+	    return freeProcess && freeProcess.binding && freeProcess.binding('util');
+	  } catch (e) {}
+	}());
+ 
+	module.exports = nodeUtil;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(20)(module)))
+ 
+/***/ }),
+/* 57 */
+/***/ (function(module, exports) {
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/**
+	 * Used to resolve the
+	 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+	 * of values.
+	 */
+	var nativeObjectToString = objectProto.toString;
+ 
+	/**
+	 * Converts `value` to a string using `Object.prototype.toString`.
+	 *
+	 * @private
+	 * @param {*} value The value to convert.
+	 * @returns {string} Returns the converted string.
+	 */
+	function objectToString(value) {
+	  return nativeObjectToString.call(value);
+	}
+ 
+	module.exports = objectToString;
+ 
+ 
+/***/ }),
+/* 58 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Creates a unary function that invokes `func` with its argument transformed.
+	 *
+	 * @private
+	 * @param {Function} func The function to wrap.
+	 * @param {Function} transform The argument transform.
+	 * @returns {Function} Returns the new function.
+	 */
+	function overArg(func, transform) {
+	  return function(arg) {
+	    return func(transform(arg));
+	  };
+	}
+ 
+	module.exports = overArg;
+ 
+ 
+/***/ }),
+/* 59 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * A specialized version of `_.indexOf` which performs strict equality
+	 * comparisons of values, i.e. `===`.
+	 *
+	 * @private
+	 * @param {Array} array The array to inspect.
+	 * @param {*} value The value to search for.
+	 * @param {number} fromIndex The index to search from.
+	 * @returns {number} Returns the index of the matched value, else `-1`.
+	 */
+	function strictIndexOf(array, value, fromIndex) {
+	  var index = fromIndex - 1,
+	      length = array.length;
+ 
+	  while (++index < length) {
+	    if (array[index] === value) {
+	      return index;
+	    }
+	  }
+	  return -1;
+	}
+ 
+	module.exports = strictIndexOf;
+ 
+ 
+/***/ }),
+/* 60 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var asciiToArray = __webpack_require__(34),
+	    hasUnicode = __webpack_require__(51),
+	    unicodeToArray = __webpack_require__(61);
+ 
+	/**
+	 * Converts `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function stringToArray(string) {
+	  return hasUnicode(string)
+	    ? unicodeToArray(string)
+	    : asciiToArray(string);
+	}
+ 
+	module.exports = stringToArray;
+ 
+ 
+/***/ }),
+/* 61 */
+/***/ (function(module, exports) {
+ 
+	/** Used to compose unicode character classes. */
+	var rsAstralRange = '\\ud800-\\udfff',
+	    rsComboMarksRange = '\\u0300-\\u036f',
+	    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
+	    rsComboSymbolsRange = '\\u20d0-\\u20ff',
+	    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
+	    rsVarRange = '\\ufe0e\\ufe0f';
+ 
+	/** Used to compose unicode capture groups. */
+	var rsAstral = '[' + rsAstralRange + ']',
+	    rsCombo = '[' + rsComboRange + ']',
+	    rsFitz = '\\ud83c[\\udffb-\\udfff]',
+	    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
+	    rsNonAstral = '[^' + rsAstralRange + ']',
+	    rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
+	    rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
+	    rsZWJ = '\\u200d';
+ 
+	/** Used to compose unicode regexes. */
+	var reOptMod = rsModifier + '?',
+	    rsOptVar = '[' + rsVarRange + ']?',
+	    rsOptJoin = '(?:' + rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
+	    rsSeq = rsOptVar + reOptMod + rsOptJoin,
+	    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';
+ 
+	/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
+	var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');
+ 
+	/**
+	 * Converts a Unicode `string` to an array.
+	 *
+	 * @private
+	 * @param {string} string The string to convert.
+	 * @returns {Array} Returns the converted array.
+	 */
+	function unicodeToArray(string) {
+	  return string.match(reUnicode) || [];
+	}
+ 
+	module.exports = unicodeToArray;
+ 
+ 
+/***/ }),
+/* 62 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Performs a
+	 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+	 * comparison between two values to determine if they are equivalent.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to compare.
+	 * @param {*} other The other value to compare.
+	 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+	 * @example
+	 *
+	 * var object = { 'a': 1 };
+	 * var other = { 'a': 1 };
+	 *
+	 * _.eq(object, object);
+	 * // => true
+	 *
+	 * _.eq(object, other);
+	 * // => false
+	 *
+	 * _.eq('a', 'a');
+	 * // => true
+	 *
+	 * _.eq('a', Object('a'));
+	 * // => false
+	 *
+	 * _.eq(NaN, NaN);
+	 * // => true
+	 */
+	function eq(value, other) {
+	  return value === other || (value !== value && other !== other);
+	}
+ 
+	module.exports = eq;
+ 
+ 
+/***/ }),
+/* 63 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toString = __webpack_require__(10);
+ 
+	/**
+	 * Used to match `RegExp`
+	 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+	 */
+	var reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
+	    reHasRegExpChar = RegExp(reRegExpChar.source);
+ 
+	/**
+	 * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
+	 * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to escape.
+	 * @returns {string} Returns the escaped string.
+	 * @example
+	 *
+	 * _.escapeRegExp('[lodash](https://lodash.com/)');
+	 * // => '\[lodash\]\(https://lodash\.com/\)'
+	 */
+	function escapeRegExp(string) {
+	  string = toString(string);
+	  return (string && reHasRegExpChar.test(string))
+	    ? string.replace(reRegExpChar, '\\$&')
+	    : string;
+	}
+ 
+	module.exports = escapeRegExp;
+ 
+ 
+/***/ }),
+/* 64 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsArguments = __webpack_require__(37),
+	    isObjectLike = __webpack_require__(7);
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/** Built-in value references. */
+	var propertyIsEnumerable = objectProto.propertyIsEnumerable;
+ 
+	/**
+	 * Checks if `value` is likely an `arguments` object.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
+	 *  else `false`.
+	 * @example
+	 *
+	 * _.isArguments(function() { return arguments; }());
+	 * // => true
+	 *
+	 * _.isArguments([1, 2, 3]);
+	 * // => false
+	 */
+	var isArguments = baseIsArguments(function() { return arguments; }()) ? baseIsArguments : function(value) {
+	  return isObjectLike(value) && hasOwnProperty.call(value, 'callee') &&
+	    !propertyIsEnumerable.call(value, 'callee');
+	};
+ 
+	module.exports = isArguments;
+ 
+ 
+/***/ }),
+/* 65 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	/* WEBPACK VAR INJECTION */(function(module) {var root = __webpack_require__(1),
+	    stubFalse = __webpack_require__(70);
+ 
+	/** Detect free variable `exports`. */
+	var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;
+ 
+	/** Detect free variable `module`. */
+	var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;
+ 
+	/** Detect the popular CommonJS extension `module.exports`. */
+	var moduleExports = freeModule && freeModule.exports === freeExports;
+ 
+	/** Built-in value references. */
+	var Buffer = moduleExports ? root.Buffer : undefined;
+ 
+	/* Built-in method references for those with the same name as other `lodash` methods. */
+	var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;
+ 
+	/**
+	 * Checks if `value` is a buffer.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.3.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
+	 * @example
+	 *
+	 * _.isBuffer(new Buffer(2));
+	 * // => true
+	 *
+	 * _.isBuffer(new Uint8Array(2));
+	 * // => false
+	 */
+	var isBuffer = nativeIsBuffer || stubFalse;
+ 
+	module.exports = isBuffer;
+ 
+	/* WEBPACK VAR INJECTION */}.call(exports, __webpack_require__(20)(module)))
+ 
+/***/ }),
+/* 66 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseKeys = __webpack_require__(41),
+	    getTag = __webpack_require__(49),
+	    isArguments = __webpack_require__(64),
+	    isArray = __webpack_require__(15),
+	    isArrayLike = __webpack_require__(16),
+	    isBuffer = __webpack_require__(65),
+	    isPrototype = __webpack_require__(13),
+	    isTypedArray = __webpack_require__(67);
+ 
+	/** `Object#toString` result references. */
+	var mapTag = '[object Map]',
+	    setTag = '[object Set]';
+ 
+	/** Used for built-in method references. */
+	var objectProto = Object.prototype;
+ 
+	/** Used to check objects for own properties. */
+	var hasOwnProperty = objectProto.hasOwnProperty;
+ 
+	/**
+	 * Checks if `value` is an empty object, collection, map, or set.
+	 *
+	 * Objects are considered empty if they have no own enumerable string keyed
+	 * properties.
+	 *
+	 * Array-like values such as `arguments` objects, arrays, buffers, strings, or
+	 * jQuery-like collections are considered empty if they have a `length` of `0`.
+	 * Similarly, maps and sets are considered empty if they have a `size` of `0`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is empty, else `false`.
+	 * @example
+	 *
+	 * _.isEmpty(null);
+	 * // => true
+	 *
+	 * _.isEmpty(true);
+	 * // => true
+	 *
+	 * _.isEmpty(1);
+	 * // => true
+	 *
+	 * _.isEmpty([1, 2, 3]);
+	 * // => false
+	 *
+	 * _.isEmpty({ 'a': 1 });
+	 * // => false
+	 */
+	function isEmpty(value) {
+	  if (value == null) {
+	    return true;
+	  }
+	  if (isArrayLike(value) &&
+	      (isArray(value) || typeof value == 'string' || typeof value.splice == 'function' ||
+	        isBuffer(value) || isTypedArray(value) || isArguments(value))) {
+	    return !value.length;
+	  }
+	  var tag = getTag(value);
+	  if (tag == mapTag || tag == setTag) {
+	    return !value.size;
+	  }
+	  if (isPrototype(value)) {
+	    return !baseKeys(value).length;
+	  }
+	  for (var key in value) {
+	    if (hasOwnProperty.call(value, key)) {
+	      return false;
+	    }
+	  }
+	  return true;
+	}
+ 
+	module.exports = isEmpty;
+ 
+ 
+/***/ }),
+/* 67 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseIsTypedArray = __webpack_require__(40),
+	    baseUnary = __webpack_require__(44),
+	    nodeUtil = __webpack_require__(56);
+ 
+	/* Node.js helper references. */
+	var nodeIsTypedArray = nodeUtil && nodeUtil.isTypedArray;
+ 
+	/**
+	 * Checks if `value` is classified as a typed array.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category Lang
+	 * @param {*} value The value to check.
+	 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
+	 * @example
+	 *
+	 * _.isTypedArray(new Uint8Array);
+	 * // => true
+	 *
+	 * _.isTypedArray([]);
+	 * // => false
+	 */
+	var isTypedArray = nodeIsTypedArray ? baseUnary(nodeIsTypedArray) : baseIsTypedArray;
+ 
+	module.exports = isTypedArray;
+ 
+ 
+/***/ }),
+/* 68 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * Gets the last element of `array`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 0.1.0
+	 * @category Array
+	 * @param {Array} array The array to query.
+	 * @returns {*} Returns the last element of `array`.
+	 * @example
+	 *
+	 * _.last([1, 2, 3]);
+	 * // => 3
+	 */
+	function last(array) {
+	  var length = array == null ? 0 : array.length;
+	  return length ? array[length - 1] : undefined;
+	}
+ 
+	module.exports = last;
+ 
+ 
+/***/ }),
+/* 69 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseRepeat = __webpack_require__(42),
+	    isIterateeCall = __webpack_require__(53),
+	    toInteger = __webpack_require__(72),
+	    toString = __webpack_require__(10);
+ 
+	/**
+	 * Repeats the given string `n` times.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 3.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to repeat.
+	 * @param {number} [n=1] The number of times to repeat the string.
+	 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+	 * @returns {string} Returns the repeated string.
+	 * @example
+	 *
+	 * _.repeat('*', 3);
+	 * // => '***'
+	 *
+	 * _.repeat('abc', 2);
+	 * // => 'abcabc'
+	 *
+	 * _.repeat('abc', 0);
+	 * // => ''
+	 */
+	function repeat(string, n, guard) {
+	  if ((guard ? isIterateeCall(string, n, guard) : n === undefined)) {
+	    n = 1;
+	  } else {
+	    n = toInteger(n);
+	  }
+	  return baseRepeat(toString(string), n);
+	}
+ 
+	module.exports = repeat;
+ 
+ 
+/***/ }),
+/* 70 */
+/***/ (function(module, exports) {
+ 
+	/**
+	 * This method returns `false`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.13.0
+	 * @category Util
+	 * @returns {boolean} Returns `false`.
+	 * @example
+	 *
+	 * _.times(2, _.stubFalse);
+	 * // => [false, false]
+	 */
+	function stubFalse() {
+	  return false;
+	}
+ 
+	module.exports = stubFalse;
+ 
+ 
+/***/ }),
+/* 71 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toNumber = __webpack_require__(73);
+ 
+	/** Used as references for various `Number` constants. */
+	var INFINITY = 1 / 0,
+	    MAX_INTEGER = 1.7976931348623157e+308;
+ 
+	/**
+	 * Converts `value` to a finite number.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.12.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {number} Returns the converted number.
+	 * @example
+	 *
+	 * _.toFinite(3.2);
+	 * // => 3.2
+	 *
+	 * _.toFinite(Number.MIN_VALUE);
+	 * // => 5e-324
+	 *
+	 * _.toFinite(Infinity);
+	 * // => 1.7976931348623157e+308
+	 *
+	 * _.toFinite('3.2');
+	 * // => 3.2
+	 */
+	function toFinite(value) {
+	  if (!value) {
+	    return value === 0 ? value : 0;
+	  }
+	  value = toNumber(value);
+	  if (value === INFINITY || value === -INFINITY) {
+	    var sign = (value < 0 ? -1 : 1);
+	    return sign * MAX_INTEGER;
+	  }
+	  return value === value ? value : 0;
+	}
+ 
+	module.exports = toFinite;
+ 
+ 
+/***/ }),
+/* 72 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var toFinite = __webpack_require__(71);
+ 
+	/**
+	 * Converts `value` to an integer.
+	 *
+	 * **Note:** This method is loosely based on
+	 * [`ToInteger`](http://www.ecma-international.org/ecma-262/7.0/#sec-tointeger).
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to convert.
+	 * @returns {number} Returns the converted integer.
+	 * @example
+	 *
+	 * _.toInteger(3.2);
+	 * // => 3
+	 *
+	 * _.toInteger(Number.MIN_VALUE);
+	 * // => 0
+	 *
+	 * _.toInteger(Infinity);
+	 * // => 1.7976931348623157e+308
+	 *
+	 * _.toInteger('3.2');
+	 * // => 3
+	 */
+	function toInteger(value) {
+	  var result = toFinite(value),
+	      remainder = result % 1;
+ 
+	  return result === result ? (remainder ? result - remainder : result) : 0;
+	}
+ 
+	module.exports = toInteger;
+ 
+ 
+/***/ }),
+/* 73 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var isObject = __webpack_require__(6),
+	    isSymbol = __webpack_require__(19);
+ 
+	/** Used as references for various `Number` constants. */
+	var NAN = 0 / 0;
+ 
+	/** Used to match leading and trailing whitespace. */
+	var reTrim = /^\s+|\s+$/g;
+ 
+	/** Used to detect bad signed hexadecimal string values. */
+	var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;
+ 
+	/** Used to detect binary string values. */
+	var reIsBinary = /^0b[01]+$/i;
+ 
+	/** Used to detect octal string values. */
+	var reIsOctal = /^0o[0-7]+$/i;
+ 
+	/** Built-in method references without a dependency on `root`. */
+	var freeParseInt = parseInt;
+ 
+	/**
+	 * Converts `value` to a number.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category Lang
+	 * @param {*} value The value to process.
+	 * @returns {number} Returns the number.
+	 * @example
+	 *
+	 * _.toNumber(3.2);
+	 * // => 3.2
+	 *
+	 * _.toNumber(Number.MIN_VALUE);
+	 * // => 5e-324
+	 *
+	 * _.toNumber(Infinity);
+	 * // => Infinity
+	 *
+	 * _.toNumber('3.2');
+	 * // => 3.2
+	 */
+	function toNumber(value) {
+	  if (typeof value == 'number') {
+	    return value;
+	  }
+	  if (isSymbol(value)) {
+	    return NAN;
+	  }
+	  if (isObject(value)) {
+	    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
+	    value = isObject(other) ? (other + '') : other;
+	  }
+	  if (typeof value != 'string') {
+	    return value === 0 ? value : +value;
+	  }
+	  value = value.replace(reTrim, '');
+	  var isBinary = reIsBinary.test(value);
+	  return (isBinary || reIsOctal.test(value))
+	    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
+	    : (reIsBadHex.test(value) ? NAN : +value);
+	}
+ 
+	module.exports = toNumber;
+ 
+ 
+/***/ }),
+/* 74 */
+/***/ (function(module, exports, __webpack_require__) {
+ 
+	var baseToString = __webpack_require__(11),
+	    castSlice = __webpack_require__(45),
+	    charsEndIndex = __webpack_require__(46),
+	    stringToArray = __webpack_require__(60),
+	    toString = __webpack_require__(10);
+ 
+	/** Used to match leading and trailing whitespace. */
+	var reTrimEnd = /\s+$/;
+ 
+	/**
+	 * Removes trailing whitespace or specified characters from `string`.
+	 *
+	 * @static
+	 * @memberOf _
+	 * @since 4.0.0
+	 * @category String
+	 * @param {string} [string=''] The string to trim.
+	 * @param {string} [chars=whitespace] The characters to trim.
+	 * @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
+	 * @returns {string} Returns the trimmed string.
+	 * @example
+	 *
+	 * _.trimEnd('  abc  ');
+	 * // => '  abc'
+	 *
+	 * _.trimEnd('-_-abc-_-', '_-');
+	 * // => '-_-abc'
+	 */
+	function trimEnd(string, chars, guard) {
+	  string = toString(string);
+	  if (string && (guard || chars === undefined)) {
+	    return string.replace(reTrimEnd, '');
+	  }
+	  if (!string || !(chars = baseToString(chars))) {
+	    return string;
+	  }
+	  var strSymbols = stringToArray(string),
+	      end = charsEndIndex(strSymbols, stringToArray(chars)) + 1;
+ 
+	  return castSlice(strSymbols, 0, end).join('');
+	}
+ 
+	module.exports = trimEnd;
+ 
+ 
+/***/ })
+/******/ ])
+});
+;
\ No newline at end of file
diff --git a/src/utils/utils-custom.js b/src/utils/utils-custom.js
index cbd1923..be31708 100644
--- a/src/utils/utils-custom.js
+++ b/src/utils/utils-custom.js
@@ -4,44 +4,51 @@
    * @return {String}  selfId  褰撳墠缁勪欢id
    */
   static getSubModules (components, selfId, supId) {
-    let modules = components.map(item => {
+    let modules = []
+    components.forEach(item => {
       if (item.uuid === selfId || item.type === 'navbar') {
-        return {
-          children: null
-        }
+        return
       } else if (item.format) { // 鏁版嵁鏍煎紡锛屽瓨鍦ㄦ暟鎹簮
-        return {
+        modules.push({
           value: item.uuid,
           label: item.name,
           disabled: supId === item.uuid
-        }
+        })
       } else if (item.type === 'tabs') {
-        let _item = {
-          type: 'tabs',
-          value: item.uuid,
-          label: item.name,
-          children: item.subtabs.map(f_tab => {
-            let subItem = {
-              type: 'tab',
-              value: f_tab.uuid,
-              label: f_tab.label,
-              children: this.getSubModules(f_tab.components, selfId)
-            }
-
-            if (!subItem.children || subItem.children.length === 0) {
-              return {children: null}
-            }
-            return subItem
+        if (item.subtype === 'tabletabs') {
+          item.subtabs.forEach(tab => {
+            modules.push({
+              value: tab.components[0].uuid,
+              label: tab.label,
+              disabled: supId === tab.components[0].uuid
+            })
           })
+        } else {
+          let _item = {
+            type: 'tabs',
+            value: item.uuid,
+            label: item.name,
+            children: item.subtabs.map(f_tab => {
+              let subItem = {
+                type: 'tab',
+                value: f_tab.uuid,
+                label: f_tab.label,
+                children: this.getSubModules(f_tab.components, selfId, supId)
+              }
+  
+              if (subItem.children.length === 0) {
+                return {children: null}
+              }
+              return subItem
+            })
+          }
+  
+          _item.children = _item.children.filter(t => t.children !== null)
+  
+          if (_item.children.length > 0) {
+            modules.push(_item)
+          }
         }
-
-        _item.children = _item.children.filter(t => t.children !== null)
-
-        if (_item.children.length === 0) {
-          return {children: null}
-        }
-
-        return _item
       } else if (item.type === 'group') {
         let _item = {
           value: item.uuid,
@@ -54,7 +61,8 @@
             } else if (f_tab.format) {
               return {
                 value: f_tab.uuid,
-                label: f_tab.name
+                label: f_tab.name,
+                disabled: supId === f_tab.uuid
               }
             }
             return {
@@ -65,23 +73,12 @@
 
         _item.children = _item.children.filter(t => t.children !== null)
 
-        if (_item.children.length === 0) {
-          return {children: null}
-        }
-
-        return _item
-      } else {
-        return {
-          children: null
+        if (_item.children.length > 0) {
+          modules.push(_item)
         }
       }
     })
 
-    modules = modules.filter(mod => mod.children !== null)
-
-    if (modules.length === 0) {
-      return null
-    }
     return modules
   }
 
@@ -174,12 +171,11 @@
    * @description 鑾峰彇涓婄骇妯″潡
    * @return {String}  selfId  褰撳墠缁勪欢id
    */
-  static getSupModules (components, selfId) {
-    let modules = components.map(item => {
+  static getSupModules (components, selfId, interfaces) {
+    let modules = []
+    components.forEach(item => {
       if (item.uuid === selfId) {
-        return {
-          children: null
-        }
+
       } else if (item.switchable) { // 鏁版嵁鍙垏鎹�
         let disabled = false
         if (item.type === 'card') {
@@ -187,41 +183,51 @@
         } else if (item.type === 'table') {
           disabled = item.wrap.tableType === ''
         }
-        return {
+        modules.push({
           value: item.uuid,
           label: item.name,
           disabled: disabled
-        }
+        })
       } else if (item.type === 'form') { // 鏁版嵁鏍煎紡锛屽瓨鍦ㄦ暟鎹簮
-        return {
+        modules.push({
           value: item.uuid,
           label: item.name
-        }
+        })
       } else if (item.type === 'tabs') {
-        let _item = {
-          value: item.uuid,
-          label: item.name,
-          children: item.subtabs.map(f_tab => {
-            let subItem = {
-              value: f_tab.uuid,
-              label: f_tab.label,
-              children: this.getSupModules(f_tab.components, selfId)
-            }
+        if (item.subtype === 'tabletabs') {
+          item.subtabs.forEach(tab => {
+            if (tab.components[0].uuid === selfId) return
 
-            if (!subItem.children || subItem.children.length === 0) {
-              return {children: null}
-            }
-            return subItem
+            modules.push({
+              value: tab.components[0].uuid,
+              label: tab.label,
+              disabled: tab.components[0].wrap.tableType === ''
+            })
           })
+        } else {
+          let _item = {
+            value: item.uuid,
+            label: item.name,
+            children: item.subtabs.map(f_tab => {
+              let subItem = {
+                value: f_tab.uuid,
+                label: f_tab.label,
+                children: this.getSupModules(f_tab.components, selfId)
+              }
+  
+              if (subItem.children.length === 0) {
+                return {children: null}
+              }
+              return subItem
+            })
+          }
+  
+          _item.children = _item.children.filter(t => t.children !== null)
+  
+          if (_item.children.length > 0) {
+            modules.push(_item)
+          }
         }
-
-        _item.children = _item.children.filter(t => t.children !== null)
-
-        if (_item.children.length === 0) {
-          return {children: null}
-        }
-
-        return _item
       } else if (item.type === 'group') {
         let _item = {
           value: item.uuid,
@@ -232,9 +238,16 @@
                 children: null
               }
             } else if (f_tab.switchable) {
+              let disabled = false
+              if (f_tab.type === 'card') {
+                disabled = f_tab.wrap.cardType === ''
+              } else if (f_tab.type === 'table') {
+                disabled = f_tab.wrap.tableType === ''
+              }
               return {
                 value: f_tab.uuid,
-                label: f_tab.name
+                label: f_tab.name,
+                disabled: disabled
               }
             }
             return {
@@ -245,24 +258,47 @@
 
         _item.children = _item.children.filter(t => t.children !== null)
 
-        if (_item.children.length === 0) {
-          return {children: null}
-        }
-
-        return _item
-      } else {
-        return {
-          children: null
+        if (_item.children.length > 0) {
+          modules.push(_item)
         }
       }
     })
 
-    modules = modules.filter(mod => mod.children !== null)
-
-    if (modules.length === 0) {
-      return null
+    if (interfaces && interfaces.length > 0) {
+      interfaces.forEach(item => {
+        modules.push({
+          value: item.uuid,
+          label: item.name
+        })
+      })
     }
+
     return modules
+  }
+
+  /**
+   * @description 鑾峰彇涓婄骇妯″潡
+   * @return {String}  selfId  褰撳墠缁勪欢id
+   */
+  static checkSupModules (modules, supId) {
+    let has = false
+
+    let check = (list) => {
+      list.forEach(m => {
+        if (has) return
+        if (supId === m.value) {
+          has = true
+          return
+        }
+        if (m.children) {
+          check(m.children)
+        }
+      })
+    }
+
+    check(modules)
+   
+    return has
   }
 
   /**
@@ -340,73 +376,6 @@
   }
 
   /**
-   * @description 鑾峰彇鍒犻櫎鎸夐挳Id
-   * @return {String}  name
-   */
-  static getDelButtonIds (card) {
-    let appType = sessionStorage.getItem('appType')
-    let uuids = []
-
-    if (appType === 'mob') return uuids
-
-    const getUuids = (item) => {
-      if (item.type === 'tabs') {
-        item.subtabs.forEach(tab => {
-          tab.components.forEach(c => {
-            getUuids(c)
-          })
-        })
-      } else if (item.type === 'group') {
-        item.components.forEach(c => {
-          getUuids(c)
-        })
-      } else {
-        item.action && item.action.forEach(act => {
-          if (act.origin || (appType === 'pc' && act.OpenType !== 'popview')) return
-
-          uuids.push(act.uuid)
-        })
-
-        if (['card', 'carousel', 'timeline'].includes(item.type) || (item.type === 'table' && item.subtype === 'tablecard')) {
-          item.subcards.forEach(_card => {
-            _card.elements && _card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (appType === 'pc' && cell.OpenType !== 'popview') return
-  
-              uuids.push(cell.uuid)
-            })
-            _card.backElements && _card.backElements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (appType === 'pc' && cell.OpenType !== 'popview') return
-  
-              uuids.push(cell.uuid)
-            })
-          })
-        } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
-          item.cols && item.cols.forEach(col => {
-            if (col.type !== 'action') return
-            col.elements && col.elements.forEach(cell => {
-              if (appType === 'pc' && cell.OpenType !== 'popview') return
-  
-              uuids.push(cell.uuid)
-            })
-          })
-        } else if (item.type === 'balcony') {
-          item.elements && item.elements.forEach(cell => {
-            if (appType === 'pc' && cell.OpenType !== 'popview') return
-
-            uuids.push(cell.uuid)
-          })
-        }
-      }
-    }
-
-    getUuids(card)
-
-    return uuids
-  }
-
-  /**
    * @description 鐢熸垚32浣島uid string + 鏃堕棿
    * @return {String}  uuid
    */
@@ -431,6 +400,10 @@
         return item
       }
 
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
       uuids[item.uuid] = this.getuuid()
       item.uuid = uuids[item.uuid]
 
@@ -452,7 +425,7 @@
           }
           return cell
         })
-      } else if (['card', 'carousel', 'timeline'].includes(item.type) || (item.type === 'table' && item.subtype === 'tablecard')) {
+      } else if (['card', 'carousel', 'timeline'].includes(item.type)) {
         if (item.wrap.datatype === 'public' && uuids[item.wrap.publicId]) {
           item.wrap.publicId = uuids[item.wrap.publicId]
         }
@@ -512,7 +485,7 @@
             return cell
           })
         }
-      } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable') && item.cols) {
+      } else if (item.type === 'table' && item.cols) {
         let loopCol = (col) => {
           col.subcols = col.subcols.map(c => {
             c.uuid = this.getuuid()
@@ -650,6 +623,9 @@
         if (em) {
           item.setting.supModule = ''
         }
+        if (item.wrap && item.wrap.supModule) {
+          item.wrap.supModule = item.setting.supModule
+        }
       }
 
       if (item.wrap && item.wrap.doubleClick) {
@@ -676,9 +652,13 @@
   * @description 閲嶇疆缁勪欢閰嶇疆
   * @return {String}  item 缁勪欢淇℃伅
   */
-  static resetComponentConfig = (item, copyBtns, uuids = {}) => {
+  static resetComponentConfig = (item, uuids = {}) => {
     if (item.type === 'navbar') {
       return item
+    }
+
+    if (item.subtype === 'tablecard') { // 鍏煎
+      item.type = 'card'
     }
 
     let _uuid = this.getuuid()
@@ -705,7 +685,7 @@
         cell.uuid = this.getuuid()
         return cell
       })
-    } else if (['card', 'carousel', 'timeline'].includes(item.type) || (item.type === 'table' && item.subtype === 'tablecard')) {
+    } else if (['card', 'carousel', 'timeline'].includes(item.type)) {
       item.subcards.forEach(card => {
         card.uuid = this.getuuid()
         if (card.elements) {
@@ -713,14 +693,7 @@
             card.elements = card.elements.filter(b => b.OpenType !== 'popview' && b.OpenType !== 'funcbutton')
           }
           card.elements = card.elements.map(cell => {
-            let _uuid = this.getuuid()
-            if (cell.OpenType === 'popview' && copyBtns) {
-              let _cell = JSON.parse(JSON.stringify(cell))
-              _cell.$originUuid = _cell.uuid
-              _cell.uuid = _uuid
-              copyBtns.set(_uuid, _cell)
-            }
-            cell.uuid = _uuid
+            cell.uuid = this.getuuid()
             return cell
           })
         }
@@ -729,14 +702,7 @@
             card.elements = card.elements.filter(b => b.OpenType !== 'popview' && b.OpenType !== 'funcbutton')
           }
           card.backElements = card.backElements.map(cell => {
-            let _uuid = this.getuuid()
-            if (cell.OpenType === 'popview' && copyBtns) {
-              let _cell = JSON.parse(JSON.stringify(cell))
-              _cell.$originUuid = _cell.uuid
-              _cell.uuid = _uuid
-              copyBtns.set(_uuid, _cell)
-            }
-            cell.uuid = _uuid
+            cell.uuid = this.getuuid()
             return cell
           })
         }
@@ -747,18 +713,11 @@
           item.elements = item.elements.filter(b => b.OpenType !== 'popview' && b.OpenType !== 'funcbutton')
         }
         item.elements = item.elements.map(cell => {
-          let _uuid = this.getuuid()
-          if (cell.OpenType === 'popview' && copyBtns) {
-            let _cell = JSON.parse(JSON.stringify(cell))
-            _cell.$originUuid = _cell.uuid
-            _cell.uuid = _uuid
-            copyBtns.set(_uuid, _cell)
-          }
-          cell.uuid = _uuid
+          cell.uuid = this.getuuid()
           return cell
         })
       }
-    } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable') && item.cols) {
+    } else if (item.type === 'table' && item.cols) {
       let loopCol = (col) => {
         col.subcols = col.subcols.map(c => {
           c.uuid = this.getuuid()
@@ -796,14 +755,7 @@
             col.elements = col.elements.filter(c => c.OpenType !== 'popview' && c.OpenType !== 'funcbutton')
           }
           col.elements = col.elements.map(cell => {
-            let _uuid = this.getuuid()
-            if (cell.OpenType === 'popview' && copyBtns) {
-              let _cell = JSON.parse(JSON.stringify(cell))
-              _cell.$originUuid = _cell.uuid
-              _cell.uuid = _uuid
-              copyBtns.set(_uuid, _cell)
-            }
-            cell.uuid = _uuid
+            cell.uuid = this.getuuid()
             return cell
           })
         }
@@ -831,9 +783,7 @@
       })
     }
 
-    if (item.btnlog) {
-      item.btnlog = null
-    }
+    delete item.btnlog
 
     let oriUids = {}
     if (item.action) {
@@ -844,13 +794,6 @@
         let _uuid = this.getuuid()
 
         oriUids[cell.uuid] = _uuid
-
-        if (cell.OpenType === 'popview' && copyBtns) {
-          let _cell = JSON.parse(JSON.stringify(cell))
-          _cell.$originUuid = _cell.uuid
-          _cell.uuid = _uuid
-          copyBtns.set(_uuid, _cell)
-        }
 
         cell.uuid = _uuid
 
@@ -890,16 +833,10 @@
     }
 
     if (item.setting && item.setting.supModule && item.setting.supModule[0] !== 'empty') {
-      let em = false
-      item.setting.supModule = item.setting.supModule.map(c => {
-        if (!uuids[c]) {
-          em = true
-        }
-        return uuids[c] || ''
-      })
-      if (em) {
-        item.setting.supModule = ''
-      }
+      item.setting.supModule = ''
+    }
+    if (item.wrap && item.wrap.supModule) {
+      item.wrap.supModule = ''
     }
 
     if (item.wrap && item.wrap.doubleClick) {
@@ -935,15 +872,34 @@
 }
 
 /**
+ * @description 鑾峰彇鍥捐〃楂樺害
+ */
+export function getHeight (val) {
+  if (typeof(val) === 'string') {
+    if (val.indexOf('px') > -1) {
+      val = parseFloat(val)
+    } else if (val.indexOf('vw') > -1) {
+      val = parseFloat(val)
+      val = document.body.clientWidth * val / 100
+    } else if (val.indexOf('vh') > -1) {
+      val = parseFloat(val)
+      val = document.body.clientHeight * val / 100
+    }
+  }
+
+  return parseInt(val || 400) - 30
+}
+
+/**
  * @description 鑾峰彇琛ㄥ悕
  */
-export function getTables (config) {
+export function getTables (config, pops) {
   let tables = []
   let cuts = []
   let cutreg = /(from|update|insert\s+into)\s+(@db@)?[a-z_]+/ig
   let trimreg = /(from|update|insert\s+into)\s+(@db@)?/ig
 
-  if (!config.wrap || !config.wrap.datatype || config.wrap.datatype === 'dynamic') {
+  if (config.setting && (!config.wrap || !config.wrap.datatype || config.wrap.datatype === 'dynamic')) {
     if (config.setting.interType === 'system') {
       if (config.setting.execute !== 'false') {
         let tbs = config.setting.dataresource.match(cutreg)
@@ -954,7 +910,7 @@
         let tbs = script.sql.match(cutreg)
         tbs && cuts.push(...tbs)
       })
-    } else {
+    } else if (config.setting.tableName) {
       let tb = config.setting.tableName.replace(/@db@|\s+/ig, '')
       if (/[a-z_]+/ig.test(tb)) {
         tables.push(tb)
@@ -962,48 +918,95 @@
     }
   }
 
+  config.search && config.search.forEach(cell => {
+    if (cell.resourceType === '1' && cell.dataSource) {
+      let tbs = cell.dataSource.match(cutreg)
+      tbs && cuts.push(...tbs)
+    }
+  })
+
   let action = []
 
   if (config.type === 'form') {
     config.subcards.forEach(item => {
       action.push(item.subButton)
+      item.fields && item.fields.forEach(cell => {
+        if (cell.resourceType === '1' && cell.dataSource) {
+          let tbs = cell.dataSource.match(cutreg)
+          tbs && cuts.push(...tbs)
+        }
+      })
     })
   } else if (config.subcards) {
     config.subcards.forEach(item => {
       item.elements.forEach(cell => {
-        if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
+        if (cell.eleType !== 'button') return
+        if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
           action.push(cell)
+        } else if (cell.OpenType === 'popview') {
+          if (pops) {
+            pops.push({...cell, parentId: config.uuid})
+          } else if (cell.config && cell.config.$tables) {
+            tables.push(...cell.config.$tables)
+          }
         }
       })
   
       if (item.backElements && item.setting.type === 'multi') {
         item.backElements.forEach(cell => {
-          if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
+          if (cell.eleType !== 'button') return
+          if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
             action.push(cell)
+          } else if (cell.OpenType === 'popview') {
+            if (pops) {
+              pops.push({...cell, parentId: config.uuid})
+            } else if (cell.config && cell.config.$tables) {
+              tables.push(...cell.config.$tables)
+            }
           }
         })
       }
     })
   }
+
   config.cols && config.cols.forEach(col => {
     if (col.type === 'action') {
       col.elements.forEach(cell => {
         if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
           action.push(cell)
+        } else if (cell.OpenType === 'popview') {
+          if (pops) {
+            pops.push({...cell, parentId: config.uuid})
+          } else if (cell.config && cell.config.$tables) {
+            tables.push(...cell.config.$tables)
+          }
         }
       })
     }
   })
 
   config.elements && config.elements.forEach(cell => {
-    if (cell.eleType === 'button' && ['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
+    if (cell.eleType !== 'button') return
+    if (['form', 'pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
       action.push(cell)
+    } else if (cell.OpenType === 'popview') {
+      if (pops) {
+        pops.push({...cell, parentId: config.uuid})
+      } else if (cell.config && cell.config.$tables) {
+        tables.push(...cell.config.$tables)
+      }
     }
   })
 
   config.action && config.action.forEach(cell => {
     if (['pop', 'prompt', 'exec', 'excelIn', 'excelOut'].includes(cell.OpenType)) {
       action.push(cell)
+    } else if (cell.OpenType === 'popview') {
+      if (pops) {
+        pops.push({...cell, parentId: config.uuid})
+      } else if (cell.config && cell.config.$tables) {
+        tables.push(...cell.config.$tables)
+      }
     }
   })
 
@@ -1041,6 +1044,14 @@
         tbs && cuts.push(...tbs)
       }
     } else {
+      if (btn.OpenType === 'pop' && btn.modal && btn.modal.fields) {
+        btn.modal.fields.forEach(cell => {
+          if (cell.resourceType === '1' && cell.dataSource) {
+            let tbs = cell.dataSource.match(cutreg)
+            tbs && cuts.push(...tbs)
+          }
+        })
+      }
       if (btn.intertype === 'inner') return
       if (btn.intertype === 'outer' || btn.intertype === 'custom') {
         if (btn.procMode === 'system' && btn.verify) {
diff --git a/src/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index bcbd86f..b085f16 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -64,9 +64,17 @@
           param.rduri = setting.interface
         }
       } else {
-        if (setting.sysInterface === 'true' && window.GLOB.mainSystemApi) {
-          param.rduri = window.GLOB.mainSystemApi
-        } else if (setting.sysInterface !== 'true') {
+        if (setting.sysInterface === 'true') {
+          if (window.GLOB.mainSystemApi) {
+            param.rduri = window.GLOB.mainSystemApi
+          }
+        } else if (setting.sysInterface === 'external') {
+          if (window.GLOB.systemType === 'production') {
+            param.$token = setting.exProInterface || ''
+          } else {
+            param.$token = setting.exInterface || ''
+          }
+        } else {
           if (window.GLOB.systemType === 'production' && setting.proInterface) {
             param.rduri = setting.proInterface
           } else {
@@ -206,12 +214,12 @@
     let DateCount = ''
 
     if (_dataresource && setting.laypage && orderBy && !id) {
-      LText = ` select top ${pageSize} ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable where rows > ${pageSize * (pageIndex - 1)} order by tmptable.rows `
-      DateCount = `select count(1) as total from ${_dataresource} ${_search}`
+      LText = `/*system_query*/select top ${pageSize} ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable where rows > ${pageSize * (pageIndex - 1)} order by tmptable.rows `
+      DateCount = `/*system_query*/select count(1) as total from ${_dataresource} ${_search}`
     } else if (_dataresource && orderBy) {
-      LText = ` select ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows `
+      LText = `/*system_query*/select ${arrFields} from (select ${arrFields} ,ROW_NUMBER() over(order by ${orderBy}) as rows from ${_dataresource} ${_search}) tmptable order by tmptable.rows `
     } else if (_dataresource) {
-      LText = ` select ${arrFields} from ${_dataresource} ${_search}  `
+      LText = `/*system_query*/select ${arrFields} from ${_dataresource} ${_search}  `
     }
 
     if (_customScript) {
@@ -393,7 +401,7 @@
       })
     }
 
-    let LText = ` select ${statFields.map(col => `isnull(sum(${col.field}),0) as ${col.field}`).join(',')} from ${_dataresource} ${_search} `
+    let LText = ` /*system_query*/select ${statFields.map(col => `isnull(sum(${col.field}),0) as ${col.field}`).join(',')} from ${_dataresource} ${_search} `
 
     if (_customScript) {
       LText = `${LText}
@@ -859,6 +867,9 @@
     BID: BID
   }
 
+  if (config.MenuName) {
+    param.menuname = config.MenuName
+  }
   // if (transaction) {
   //   param.func = 'sPC_Get_structured_data_try'
   // }
diff --git a/src/utils/utils.js b/src/utils/utils.js
index 293b254..3b79df9 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -59,6 +59,25 @@
   }
 
   /**
+   * @description 鑾峰彇鍥捐〃楂樺害
+   */
+  static getHeight (val) {
+    if (typeof(val) === 'string') {
+      if (val.indexOf('px') > -1) {
+        val = parseFloat(val)
+      } else if (val.indexOf('vw') > -1) {
+        val = parseFloat(val)
+        val = document.body.clientWidth * val / 100
+      } else if (val.indexOf('vh') > -1) {
+        val = parseFloat(val)
+        val = document.body.clientHeight * val / 100
+      }
+    }
+  
+    return parseInt(val || 400) - 30
+  }
+
+  /**
    * @description 鏁版嵁婧愬悕绉帮紝鐢ㄤ簬缁熶竴鏌ヨ
    * @return {String}  name
    */
@@ -789,10 +808,8 @@
     arrfield = arrfield.join(',')
 
     if (item.orderBy) {
-      // sql = `select ${item.type === 'checkcard' ? 'top 20' : ''} ${arrfield} from (select distinct ${arrfield},${item.orderBy} as orderfield from ${_datasource} ) a order by orderfield ${item.orderType}`
       sql = `select distinct ${arrfield},${item.orderBy} as orderfield from ${_datasource} order by orderfield ${item.orderType}`
     } else {
-      // sql = `select ${item.type === 'checkcard' ? 'top 20' : ''} ${arrfield} from (select distinct ${arrfield} from ${_datasource}) a`
       sql = `select distinct ${arrfield} from ${_datasource}`
     }
 
@@ -1931,6 +1948,11 @@
       values.push('@BID@')
     }
 
+    if (!keys.includes('typename')) {
+      keys.push('typename')
+      values.push('@typename@')
+    }
+
     keys = keys.join(',')
     values = values.join(',')
     _insertsql = `insert into ${btn.sql} (${keys}) select ${values};`
@@ -1988,6 +2010,9 @@
         _form.push('FiYear=@FiYear')
       }
     }
+    if (!_arr.includes('typename')) {
+      _form.push('typename=@typename@')
+    }
     _form = _form.join(',')
 
     let _ID = '=@ID@'
@@ -2040,7 +2065,7 @@
 
     _sql += `
       /* 榛樿sql */
-      insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${btn.sql} 鏁版嵁: ${_msg}${primaryKey}='+@ID@,200),@userid@,@username,@fullname
+      insert into snote (remark,createuserid,CreateUser,CreateStaff,typename) select left('鍒犻櫎琛�:${btn.sql} 鏁版嵁: ${_msg}${primaryKey}='+@ID@,200),@userid@,@username,@fullname,@typename@
       delete ${btn.sql} where ${primaryKey}${_ID};`
   } else if (_actionType === 'insertOrUpdate') {
     _sql += `
@@ -2095,6 +2120,7 @@
   _sql = _sql.replace(/@SessionUid@/ig, `'${localStorage.getItem('SessionUid') || ''}'`)
   _sql = _sql.replace(/@UserID@/ig, `'${sessionStorage.getItem('UserID') || ''}'`)
   _sql = _sql.replace(/@Appkey@/ig, `'${window.GLOB.appkey || ''}'`)
+  _sql = _sql.replace(/@typename@/ig, `'admin'`)
 
   if (window.GLOB.debugger === true || (window.debugger === true && options.sysType !== 'cloud')) {
     // _sql = _sql.replace(/\n\s{8}/ig, '\n')
@@ -2134,6 +2160,7 @@
 
     if (mark.field[1] === 'static') {
       contrastVal = mark.contrastValue
+      originVal = originVal + ''
     } else {
       contrastVal = record[mark.field[2]]
     }
diff --git a/src/views/appmanage/submutilform/index.jsx b/src/views/appmanage/submutilform/index.jsx
index 59809f0..18265b2 100644
--- a/src/views/appmanage/submutilform/index.jsx
+++ b/src/views/appmanage/submutilform/index.jsx
@@ -138,20 +138,20 @@
                 initialValue: card ? card.css : 'bg_black_style_blue'
               })(
                 <Select>
-                  <Select.Option value="bg_black_style_blue"><span className="color-block" style={{background: '#1890ff'}}></span>钃濊壊</Select.Option>
-                  <Select.Option value="bg_black_style_red"><span className="color-block" style={{background: '#f5222d'}}></span>绾㈣壊</Select.Option>
-                  <Select.Option value="bg_black_style_orange_red"><span className="color-block" style={{background: '#fa541c'}}></span>姗欑孩鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_orange"><span className="color-block" style={{background: '#fa8c16'}}></span>姗欒壊</Select.Option>
-                  <Select.Option value="bg_black_style_orange_yellow"><span className="color-block" style={{background: '#faad14'}}></span>姗欓粍鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_yellow"><span className="color-block" style={{background: '#fadb14'}}></span>榛勮壊</Select.Option>
-                  <Select.Option value="bg_black_style_yellow_green"><span className="color-block" style={{background: '#a0d911'}}></span>榛勭豢鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_green"><span className="color-block" style={{background: '#52c41a'}}></span>缁胯壊</Select.Option>
-                  <Select.Option value="bg_black_style_cyan"><span className="color-block" style={{background: '#13c2c2'}}></span>闈掕壊</Select.Option>
-                  <Select.Option value="bg_black_style_blue_purple"><span className="color-block" style={{background: '#2f54eb'}}></span>钃濈传鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_purple"><span className="color-block" style={{background: '#722ed1'}}></span>绱壊</Select.Option>
-                  <Select.Option value="bg_black_style_magenta"><span className="color-block" style={{background: '#eb2f96'}}></span>娲嬬孩鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_grass_green"><span className="color-block" style={{background: '#aeb303'}}></span>鑽夌豢鑹�</Select.Option>
-                  <Select.Option value="bg_black_style_deep_red"><span className="color-block" style={{background: '#c32539'}}></span>娣辩孩鑹�</Select.Option>
+                  <Select.Option value="bg_black_style_blue"><span className="color-block" style={{background: '#1890ff'}}></span>钃濊壊锛�#1890ff锛�</Select.Option>
+                  <Select.Option value="bg_black_style_red"><span className="color-block" style={{background: '#f5222d'}}></span>绾㈣壊锛�#f5222d锛�</Select.Option>
+                  <Select.Option value="bg_black_style_orange_red"><span className="color-block" style={{background: '#fa541c'}}></span>姗欑孩鑹诧紙#fa541c锛�</Select.Option>
+                  <Select.Option value="bg_black_style_orange"><span className="color-block" style={{background: '#fa8c16'}}></span>姗欒壊锛�#fa8c16锛�</Select.Option>
+                  <Select.Option value="bg_black_style_orange_yellow"><span className="color-block" style={{background: '#faad14'}}></span>姗欓粍鑹诧紙#faad14锛�</Select.Option>
+                  <Select.Option value="bg_black_style_yellow"><span className="color-block" style={{background: '#fadb14'}}></span>榛勮壊锛�#fadb14锛�</Select.Option>
+                  <Select.Option value="bg_black_style_yellow_green"><span className="color-block" style={{background: '#a0d911'}}></span>榛勭豢鑹诧紙#a0d911锛�</Select.Option>
+                  <Select.Option value="bg_black_style_green"><span className="color-block" style={{background: '#52c41a'}}></span>缁胯壊锛�#52c41a锛�</Select.Option>
+                  <Select.Option value="bg_black_style_cyan"><span className="color-block" style={{background: '#13c2c2'}}></span>闈掕壊锛�#13c2c2锛�</Select.Option>
+                  <Select.Option value="bg_black_style_blue_purple"><span className="color-block" style={{background: '#2f54eb'}}></span>钃濈传鑹诧紙#2f54eb锛�</Select.Option>
+                  <Select.Option value="bg_black_style_purple"><span className="color-block" style={{background: '#722ed1'}}></span>绱壊锛�#722ed1锛�</Select.Option>
+                  <Select.Option value="bg_black_style_magenta"><span className="color-block" style={{background: '#eb2f96'}}></span>娲嬬孩鑹诧紙#eb2f96锛�</Select.Option>
+                  <Select.Option value="bg_black_style_grass_green"><span className="color-block" style={{background: '#aeb303'}}></span>鑽夌豢鑹诧紙#aeb303锛�</Select.Option>
+                  <Select.Option value="bg_black_style_deep_red"><span className="color-block" style={{background: '#c32539'}}></span>娣辩孩鑹诧紙#c32539锛�</Select.Option>
                 </Select>
               )}
             </Form.Item>
diff --git a/src/views/billprint/index.jsx b/src/views/billprint/index.jsx
index c64ce5d..bd9e5b0 100644
--- a/src/views/billprint/index.jsx
+++ b/src/views/billprint/index.jsx
@@ -26,6 +26,7 @@
 const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 const TimeLine = asyncComponent(() => import('@/tabviews/custom/components/timeline/normal-timeline'))
 const Balcony = asyncComponent(() => import('@/tabviews/custom/components/card/balcony'))
+const DebugTable = asyncComponent(() => import('@/tabviews/debugtable'))
 
 class BillPrint extends Component {
   state = {
@@ -38,6 +39,7 @@
     tempId: '',
     config: null,
     urlParam: null,
+    visible: false,
     auto: true
   }
 
@@ -62,7 +64,9 @@
           tempId: param.tempId,
           urlParam: param
         }, () => {
-          this.getMenuParam()
+          setTimeout(() => {
+            this.getMenuParam()
+          }, 200)
         })
       } catch (e) {
         notification.warning({
@@ -78,6 +82,53 @@
     return !is(fromJS(this.state), fromJS(nextState))
   }
 
+  componentDidMount() {
+    const _this = this
+
+    Object.defineProperty(window, 'debug', {
+      configurable: true,
+      enumerable: true,
+      set(value) {
+        if (value + '' === 'false') {
+          window.debugger = false
+          window.GLOB.breakpoint = false
+          sessionStorage.removeItem('breakpoint')
+        } else {
+          window.debugger = true
+          window.GLOB.breakpoint = value + ''
+          sessionStorage.setItem('breakpoint', value)
+        }
+        _this.debugChange()
+      }
+    })
+
+    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 || !keyCode) return
+      
+      let _shortcut = `${preKey}+${keyCode}`
+
+      if (window.GLOB.breakpoint && _shortcut === 'ctrl+67') {
+        window.debugger = false
+        window.GLOB.breakpoint = false
+        sessionStorage.removeItem('breakpoint')
+        
+        _this.debugChange()
+      }
+    }
+  }
+
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
    */
@@ -85,6 +136,10 @@
     this.setState = () => {
       return
     }
+  }
+
+  debugChange = () => {
+    this.setState({visible: !this.state.visible})
   }
 
   getTouristMsg = () => {
@@ -146,11 +201,7 @@
 
     window.GLOB.dataFormat = false // 鎵撳嵃鍘婚櫎姘村嵃
 
-    if (window.GLOB.mainSystemApi) { // 浠庡崟鐐圭櫥褰曟湇鍔″櫒鍙栨墦鍗伴厤缃俊鎭�
-      _param.rduri = window.GLOB.mainSystemApi
-    }
-
-    Api.genericInterface(_param).then(result => {
+    Api.getCacheConfig(_param).then(result => {
       if (result.status) {
         let config = ''
 
@@ -262,7 +313,11 @@
           if (component.search) component.search = []
           component.data = [] // 鍒濆鍖栨暟鎹负绌�
 
-          if (component.type === 'table' && component.subtype === 'normaltable') {
+          if (component.subtype === 'tablecard') { // 鍏煎
+            component.type = 'card'
+          }
+
+          if (component.type === 'table') {
             let getColumns = (cols) => {
               return cols.map(item => {
                 if (item.type === 'colspan') {
@@ -629,10 +684,30 @@
     })
   }
 
+  canvasToImage(canvas) {
+    let image = new Image()
+    image.src = canvas.toDataURL('image/jpg')
+    image.style = 'width:100%;height:100%;position:absolute;z-index:1;left:0px;top:0px;'
+    return image
+  }
+
   print = () => {
     const { config, printing } = this.state
-
+    
     if (printing) return
+
+    let qrcodes = document.getElementsByClassName('qrcode-box')
+
+    for (let i = 0; i < qrcodes.length; i++) {
+      let canvas = qrcodes[i].getElementsByTagName('canvas')[0]
+
+      if (canvas) {
+        let img = this.canvasToImage(canvas)
+  
+        canvas.remove()
+        qrcodes[i].append(img)
+      }
+    }
 
     let jubuData = document.getElementById('bill-print').innerHTML
 
@@ -684,69 +759,75 @@
 
   getComponents = (components) => {
     return components.map(item => {
+      let style = null
+
+      if (item.style && item.style.clear === 'left') {
+        style = {clear: 'left'}
+      }
+
       if (item.type === 'bar' || item.type === 'line') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvBarAndLine config={item} initdata={item.data} mainSearch={[]} />
           </Col>
         )
       } else if (item.type === 'pie') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvPie config={item} initdata={item.data} mainSearch={[]} />
           </Col>
         )
       } else if (item.type === 'scatter') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvScatter config={item} initdata={item.data} mainSearch={[]}/>
           </Col>
         )
       } else if (item.type === 'dashboard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <AntvDashboard config={item} initdata={item.data} mainSearch={[]}/>
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'datacard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <DataCard config={item} initdata={item.data} mainSearch={[]} />
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'propcard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <PropCard config={item} initdata={item.data} mainSearch={[]} />
           </Col>
         )
-      } else if (item.type === 'table' && item.subtype === 'tablecard') {
+      } else if (item.type === 'card' && item.subtype === 'tablecard') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TableCard config={item} initdata={item.data} mainSearch={[]}/>
           </Col>
         )
       } else if (item.type === 'table' && item.subtype === 'normaltable') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <NormalTable config={item} initdata={item.data} mainSearch={[]}/>
           </Col>
         )
       } else if (item.type === 'code') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <SandBox config={item} initdata={item.data} mainSearch={[]}/>
           </Col>
         )
       } else if (item.type === 'balcony') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <Balcony config={item} initdata={item.data}/>
           </Col>
         )
       } else if (item.type === 'timeline') {
         return (
-          <Col span={item.width} key={item.uuid}>
+          <Col span={item.width} style={style} key={item.uuid}>
             <TimeLine config={item} initdata={item.data}/>
           </Col>
         )
@@ -766,6 +847,7 @@
           {pages.map((components, index) => (<div className={'print-page' + (auto ? ' auto' : '')} key={index} style={{...config.style, overflow: 'hidden', boxSizing: 'border-box'}}><Row>{this.getComponents(components)}</Row></div>))}
         </div> : null}
         {viewlost ? <NotFount msg={this.state.lostmsg} /> : null}
+        {config && window.GLOB.breakpoint ? <DebugTable /> : null}
         {pages && !loadingview && !viewlost ? <div className="print-button"><Button icon="printer" size="large" shape="circle" onClick={this.print}></Button></div> : null}
         {!loadingview && !viewlost ? <div className="refresh-button"><Button icon="reload" size="large" shape="circle" onClick={this.reload}></Button></div> : null}
       </div>
diff --git a/src/views/billprint/index.scss b/src/views/billprint/index.scss
index 9c88608..8c8a1ae 100644
--- a/src/views/billprint/index.scss
+++ b/src/views/billprint/index.scss
@@ -35,6 +35,14 @@
   .ant-empty {
     display: none;
   }
+  .debugtable {
+    .ant-table-placeholder {
+      display: block;
+    }
+    .ant-empty {
+      display: block;
+    }
+  }
 }
 
 .print-page {
diff --git a/src/views/imdesign/index.jsx b/src/views/imdesign/index.jsx
index 2d6eeab..47ab0c1 100644
--- a/src/views/imdesign/index.jsx
+++ b/src/views/imdesign/index.jsx
@@ -188,41 +188,39 @@
   }
 
   getAppPictures = () => {
-    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
-
-    Api.getSystemConfig({
+    if (sessionStorage.getItem('app_pictures')) return
+    
+    let deffers = []
+    let param = {
       func: 's_url_db_adduptdel',
       PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
       PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-      typecharone: 'image',
       type: 'search'
-    }).then(res => {
-      if (res.status) {
-        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
-      }
+    }
+    deffers = [new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
+          resolve(res.data)
+        })
+      }, 500)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1000)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1500)
+    })]
 
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'video',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
-        }
-      })
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'color',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
-        }
-      })
+    Promise.all(deffers).then(response => {
+      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
+      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
+      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
     })
   }
 
diff --git a/src/views/interface/history/index.jsx b/src/views/interface/history/index.jsx
index a35551f..0db24fe 100644
--- a/src/views/interface/history/index.jsx
+++ b/src/views/interface/history/index.jsx
@@ -222,7 +222,7 @@
       interface: window.GLOB.baseurl + 'webapi/dostars',
       method: 'POST',
       params: [],
-      raw: "{\n \"func\":\"******\",\n \"LoginUID\":\"******\",\n \"UserID\":\"******\",\n \"nonc\":\"" + Utils.getguid() + "\",\n \"t\":" + parseInt(new Date().getTime() / 1000) + "\n}",
+      raw: "{\n \"func\":\"******\",\n \"LoginUID\":\"" + (sessionStorage.getItem('LoginUID') || "******") + "\",\n \"UserID\":\"" + (sessionStorage.getItem('UserID') || "******") + "\",\n \"nonc\":\"" + Utils.getguid() + "\",\n \"t\":" + parseInt(new Date().getTime() / 1000) + "\n}",
       uuid: 'dologon'
     }
     MKEmitter.emit('useInterface', m)
diff --git a/src/views/main/index.jsx b/src/views/main/index.jsx
index a6d2317..a87b0c5 100644
--- a/src/views/main/index.jsx
+++ b/src/views/main/index.jsx
@@ -61,7 +61,7 @@
           {isSideMenu && navBar !== 'menu_board_navigation' ? <Sidemenu key="sidemenu"/> : null}
           {isSideMenu ? <Tabview key="tabview"/> : null}
           {!isSideMenu ? <Breadview key="breadview"/> : null}
-          <QueryLog />
+          {window.GLOB.systemType === 'production' ? <QueryLog /> : null}
         </ConfigProvider>
         <ImgScale />
       </div>
diff --git a/src/views/menudesign/homeform/index.jsx b/src/views/menudesign/homeform/index.jsx
index f865639..5ab9965 100644
--- a/src/views/menudesign/homeform/index.jsx
+++ b/src/views/menudesign/homeform/index.jsx
@@ -13,15 +13,18 @@
     updateConfig: PropTypes.func
   }
 
-  state = {
-    menulist: []
-  }
+  state = {}
 
   UNSAFE_componentWillMount () {
-    let _param = {func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}
-    _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
+    if (sessionStorage.getItem('thdMenuList') && sessionStorage.getItem('fstMenuList')) {
 
-    Api.getSystemConfig(_param).then(result => {
+    } else {
+      this.getMenus()
+    }
+  }
+
+  getMenus = () => {
+    Api.getSystemConfig({func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}).then(result => {
       if (result.status) {
         let thdMenuList = []
         let menulist = result.fst_menu.map(fst => {
diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index 510bace..687d793 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -4,7 +4,7 @@
 import { is, fromJS } from 'immutable'
 import moment from 'moment'
 import HTML5Backend from 'react-dnd-html5-backend'
-import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button, Typography } from 'antd'
+import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button, Typography, Spin } from 'antd'
 import { DoubleLeftOutlined, DoubleRightOutlined, EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'
 import html2canvas from 'html2canvas'
 import md5 from 'md5'
@@ -12,10 +12,9 @@
 import Api from '@/api'
 import options from '@/store/options.js'
 import Utils, { setGLOBFuncs } from '@/utils/utils.js'
-// import antdEnUS from 'antd/es/locale/en_US'
 import antdZhCN from 'antd/es/locale/zh_CN'
 import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
+import { getTables } from '@/utils/utils-custom.js'
 import asyncComponent from '@/utils/asyncComponent'
 
 import '@/assets/css/design.scss'
@@ -28,6 +27,7 @@
 
 const MenuForm = asyncComponent(() => import('./menuform'))
 const HomeForm = asyncComponent(() => import('./homeform'))
+const PopView = asyncComponent(() => import('./popview'))
 const Header = asyncComponent(() => import('@/menu/header'))
 const MenuShell = asyncComponent(() => import('@/menu/menushell'))
 const PrintMenuForm = asyncComponent(() => import('./printmenuform'))
@@ -59,16 +59,16 @@
     MenuName: '',
     MenuNo: '',
     delButtons: [],
-    copyButtons: [],
-    activeKey: 'basedata',
     menuloading: false,
     oriConfig: null,
     config: null,
     customComponents: [],
     comloading: false,
-    settingshow: true,
+    settingshow: sessionStorage.getItem('settingshow') !== 'false',
     eyeopen: false,
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
+    view: '',
+    popConfig: null,
+    needUpdate: false
   }
 
   UNSAFE_componentWillMount() {
@@ -84,7 +84,7 @@
 
       this.setState({
         MenuType: param.MenuType,
-        MenuId: param.MenuId,
+        MenuId: param.MenuId || param.MenuID,
         ParentId: param.ParentId || '',
         MenuName: param.MenuName || '',
         MenuNo: param.MenuNo || '',
@@ -105,9 +105,6 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('delButtons', this.delButtons)
-    MKEmitter.addListener('modalStatus', this.modalStatus)
-    MKEmitter.addListener('copyButtons', this.copyButtons)
     MKEmitter.addListener('changePopview', this.initPopview)
     MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
@@ -145,16 +142,32 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
         }
 
         let node = document.getElementById('save-modal-config')
+        if (!node) {
+          node = document.getElementById('save-pop-config')
+        }
         if (!node) {
           node = document.getElementById('save-config')
         }
@@ -174,20 +187,38 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('delButtons', this.delButtons)
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-    MKEmitter.removeListener('copyButtons', this.copyButtons)
     MKEmitter.removeListener('changePopview', this.initPopview)
     MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
   }
 
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
+  triggerMenuSave = () => {
+    if (this.state.view === 'popview') return
+
+    this.submitConfig()
   }
 
-  triggerMenuSave = () => {
-    this.submitConfig()
+  submitPopConfig = (btnconfig) => {
+    let parents = {[btnconfig.ParentId]: true}
+    let popbtns = {[btnconfig.uuid]: fromJS(btnconfig).toJS()}
+    let config = fromJS(this.state.config).toJS()
+
+    config.components = this.setPopView(config.components, parents, popbtns)
+
+    this.setState({ config }, () => {
+      this.submitConfig()
+    })
+  }
+
+  closePop = () => {
+    const {config} = this.state
+
+    sessionStorage.setItem('editMenuType', 'menu')
+
+    window.GLOB.urlFields = config.urlFields || []
+    window.GLOB.customMenu = config
+
+    this.setState({view: '', popConfig: null})
   }
 
   getPrintTemp = () => {
@@ -235,41 +266,39 @@
   }
 
   getAppPictures = () => {
-    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
+    if (sessionStorage.getItem('app_pictures')) return
     
-    Api.getSystemConfig({
+    let deffers = []
+    let param = {
       func: 's_url_db_adduptdel',
       PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
       PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-      typecharone: 'image',
       type: 'search'
-    }).then(res => {
-      if (res.status) {
-        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
-      }
+    }
+    deffers = [new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
+          resolve(res.data)
+        })
+      }, 500)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1000)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1500)
+    })]
 
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'video',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
-        }
-      })
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'color',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
-        }
-      })
+    Promise.all(deffers).then(response => {
+      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
+      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
+      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
     })
   }
 
@@ -321,31 +350,41 @@
     this.setState({customComponents: coms})
   }
 
-  delButtons = (items) => {
-    this.setState({ delButtons: [...this.state.delButtons, ...items] })
-  }
-
-  copyButtons = (items) => {
-    this.setState({copyButtons: [...this.state.copyButtons, ...items]})
-  }
-
   initPopview = (card, btn) => {
-    const { oriConfig, config } = this.state
+    const { config } = this.state
 
-    if (!is(fromJS(oriConfig), fromJS(config))) {
+    if (!this.checkBase()) {
       notification.warning({
         top: 92,
-        message: '閰嶇疆宸蹭慨鏀癸紝璇蜂繚瀛橈紒',
+        message: '璇峰畬鍠勫熀鏈俊鎭紒',
         duration: 5
       })
       return
     }
 
     let _btn = fromJS(btn).toJS()
-    _btn.MenuName = config.MenuName + '-' + card.name + '-' + btn.label
-    _btn.ParentMenuID = config.uuid
 
-    this.props.history.push('/popdesign/' + window.btoa(window.encodeURIComponent((JSON.stringify(_btn)))))
+    if (_btn.config) {
+      _btn.config.uuid = _btn.uuid
+      _btn.config.MenuID = _btn.uuid
+      _btn.config.ParentId = card.uuid
+      _btn.config.MenuName = _btn.label
+    } else {
+      _btn.config = {
+        uuid: _btn.uuid,
+        MenuID: _btn.uuid,
+        ParentId: card.uuid,
+        enabled: false,
+        MenuName: _btn.label,
+        tables: config.tables || [],
+        Template: 'CustomPage',
+        components: [],
+        viewType: 'popview',
+        style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+      }
+    }
+
+    this.setState({view: 'popview', popConfig: _btn})
   }
 
   closeView = () => {
@@ -391,7 +430,7 @@
 
         if (!config) {
           config = {
-            version: 1.0,
+            version: 2.0,
             uuid: MenuId,
             MenuID: MenuId,
             parentId: ParentId,
@@ -437,11 +476,20 @@
         config.open_edition = result.open_edition || ''
         window.GLOB.urlFields = config.urlFields || []
 
-        this.setState({
-          oriConfig: config,
-          config: fromJS(config).toJS()
-        })
-        window.GLOB.customMenu = config
+        if (config.version !== 2.0) {
+          this.setState({
+            oriConfig: fromJS(config).toJS(),
+            comloading: true,
+            needUpdate: true
+          })
+          this.updatePage(config)
+        } else {
+          this.setState({
+            oriConfig: fromJS(config).toJS(),
+            config: config
+          })
+          window.GLOB.customMenu = config
+        }
       } else {
         notification.warning({
           top: 92,
@@ -452,8 +500,181 @@
     })
   }
 
-  getMenuMessage = (delButtons, tbs) => {
-    const { config } = this.state
+  collectTB = (components, popBtns) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          delete tab.floor
+          delete tab.hasSearch
+          delete tab.parentId
+
+          tab.components = this.collectTB(tab.components, popBtns)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.collectTB(item.components, popBtns)
+      } else {
+        item.$tables = getTables(item, popBtns)
+      }
+
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
+      delete item.tabId
+      delete item.parentId
+      delete item.btnlog
+      delete item.floor
+      delete item.dataName
+
+      return item
+    })
+  }
+
+  setPopView = (components, parents, popbtns) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components = this.setPopView(tab.components, parents, popbtns)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.setPopView(item.components, parents, popbtns)
+      } else if (parents[item.uuid]) {
+        this.setpopConfig(item, popbtns)
+      }
+      return item
+    })
+  }
+
+  setpopConfig = (config, popbtns) => {
+    config.subcards && config.subcards.forEach(item => {
+      item.elements.forEach(cell => {
+        if (cell.eleType !== 'button') return
+        if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+          cell.config = popbtns[cell.uuid]
+        }
+      })
+    })
+
+    config.cols && config.cols.forEach(col => {
+      if (col.type === 'action') {
+        col.elements.forEach(cell => {
+          if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+            cell.config = popbtns[cell.uuid]
+          }
+        })
+      }
+    })
+
+    config.elements && config.elements.forEach(cell => {
+      if (cell.eleType !== 'button') return
+      if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+        cell.config = popbtns[cell.uuid]
+      }
+    })
+  
+    config.action && config.action.forEach(cell => {
+      if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+        cell.config = popbtns[cell.uuid]
+      }
+    })
+
+    config.$tables = getTables(config)
+  }
+
+  updatePage = (config) => {
+    let popBtns = []
+
+    config.components = this.collectTB(config.components, popBtns)
+    config.version = 2.0
+
+    if (popBtns.length === 0) {
+      this.setState({
+        config: config,
+        comloading: false,
+        needUpdate: true
+      })
+      return
+    }
+
+    Promise.all(popBtns.map((pop, i) => {
+      return new Promise(resolve => {
+        let param = {
+          func: 'sPC_Get_LongParam',
+          MenuID: pop.uuid
+        }
+    
+        setTimeout(() => {
+          Api.getSystemConfig(param).then(res => {
+            let _config = null
+            try {
+              _config = res.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(res.LongParam))) : null
+            } catch (e) {
+              console.warn('Parse Failure')
+              _config = null
+            }
+
+            if (_config && _config.Template !== 'CustomPage') {
+              _config = null
+            }
+            if (_config) {
+              _config.uuid = pop.uuid
+              _config.MenuID = pop.uuid
+              _config.ParentId = pop.parentId
+              _config.MenuName = pop.label
+
+              delete _config.MenuNo
+              delete _config.open_edition
+              delete _config.version
+
+              _config.components = this.collectTB(_config.components)
+            } else {
+              _config = {
+                uuid: pop.uuid,
+                MenuID: pop.uuid,
+                ParentId: pop.parentId,
+                enabled: false,
+                MenuName: pop.label,
+                tables: config.tables || [],
+                Template: 'CustomPage',
+                components: [],
+                viewType: 'popview',
+                style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+              }
+            }
+
+            resolve(_config)
+          })
+        }, i * 40)
+      })
+    })).then(results => {
+      let bts = []
+      results = results.map(res => {
+        let tbs = []
+        bts.push(...this.getMenuMessage(res, tbs))
+
+        res.$tables = Array.from(new Set(tbs))
+
+        return res
+      })
+
+      bts = bts.map(bt => bt.replace(/select\s'/, '').replace(/'\sas.*/, ''))
+      bts.push(...popBtns.map(pop => pop.uuid))
+
+      let parents = {}
+      let popbtns = {}
+
+      results.forEach(item => {
+        parents[item.ParentId] = true
+        popbtns[item.uuid] = item
+      })
+
+      config.components = this.setPopView(config.components, parents, popbtns)
+
+      this.setState({ delButtons: bts, config, comloading: false })
+    })
+  }
+
+  getMenuMessage = (config, tbs) => {
     let buttons = []
     let _sort = 1
 
@@ -462,89 +683,47 @@
         if (item.$tables) {
           tbs.push(...item.$tables)
         }
+        if (item.action && item.action.length > 0) {
+          item.action.forEach(btn => {
+            if (btn.hidden === 'true') return
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
+            _sort++
+          })
+        }
         if (item.type === 'tabs') {
           item.subtabs.forEach(tab => {
             traversal(tab.components)
           })
         } else if (item.type === 'group') {
           traversal(item.components)
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
+        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
               if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              if (cell.hidden === 'true') return
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
               _sort++
             })
             card.backElements && card.backElements.forEach(cell => {
               if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
-            })
-          })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              if (cell.hidden === 'true') return
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
               _sort++
             })
           })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
             if (cell.eleType !== 'button') return
-            if (cell.hidden === 'true') {
-              delButtons.push(cell.uuid)
-              return
-            }
-            buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+            if (cell.hidden === 'true') return
+            buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
             _sort++
           })
-        } else if (item.type === 'line' || item.type === 'bar' || item.type === 'chart') {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-        } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
+        } else if (item.type === 'table') {
           item.cols && item.cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements.forEach(btn => {
-              if (btn.hidden === 'true') {
-                delButtons.push(btn.uuid)
-                return
-              }
-              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+              if (btn.hidden === 'true') return
+              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
               _sort++
             })
           })
@@ -554,31 +733,38 @@
 
     traversal(config.components)
 
+    if (config.interfaces) {
+      config.interfaces.forEach(item => {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+      })
+    }
+
     return buttons
   }
 
-  submitConfig = () => {
-    const { MenuType, copyButtons } = this.state
-    let config = fromJS(this.state.config).toJS()
+  checkBase = () => {
+    const { MenuType, config } = this.state
 
     if (MenuType === 'billPrint' && config.printPage === 'page' && !config.everyPCount) {
+      return false
+    } else if (MenuType === 'home' && (config.cacheUseful === 'true' && !config.cacheTime)) {
+      return false
+    } else if (MenuType === 'custom' && (!config.MenuName || !config.MenuNo || !config.fstMenuId || !config.parentId || (config.cacheUseful === 'true' && !config.cacheTime))) {
+      return false
+    }
+    return true
+  }
+
+  submitConfig = () => {
+    const { MenuType } = this.state
+    let config = fromJS(this.state.config).toJS()
+
+    if (!this.checkBase()) {
       notification.warning({
         top: 92,
         message: '璇峰畬鍠勫熀鏈俊鎭紒',
-        duration: 5
-      })
-      return
-    } else if (MenuType === 'home' && (config.cacheUseful === 'true' && !config.cacheTime)) {
-      notification.warning({
-        top: 92,
-        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
-        duration: 5
-      })
-      return
-    } else if (MenuType === 'custom' && (!config.MenuName || !config.MenuNo || !config.fstMenuId || !config.parentId || (config.cacheUseful === 'true' && !config.cacheTime))) {
-      notification.warning({
-        top: 92,
-        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
         duration: 5
       })
       return
@@ -594,8 +780,7 @@
       }
 
       let tbs = []
-      let delButtons = fromJS(this.state.delButtons).toJS()
-      let btns = this.getMenuMessage(delButtons, tbs)
+      let btns = this.getMenuMessage(config, tbs)
       let arr = []
       tbs = tbs.filter(tb => {
         let _tb = tb.toLowerCase()
@@ -654,24 +839,14 @@
         ParentID: config.uuid,
         MenuNo: config.MenuNo,
         Template: 'CustomPage',
-        PageParam: '',
-        LongParam: '',
-        LText: []
+        button_proc_edition: 'Y'
       }
 
-      let btnIds = '' // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
-      if (MenuType !== 'billPrint') {
-        btnParam.LText = btns
-        btnParam.LText = btnParam.LText.join(' union all ')
+      btnParam.LText = btns.join(' union all ')
 
-        btnIds = btnParam.LText
-
-        btnParam.LText = Utils.formatOptions(btnParam.LText)
-        btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-        btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
-      } else {
-        btnParam.LText = ''
-      }
+      btnParam.LText = Utils.formatOptions(btnParam.LText)
+      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
 
       new Promise(resolve => {
         if (MenuType === 'billPrint') { // 鎵撳嵃鐢熸垚椤甸潰鏁堟灉鍥�
@@ -695,206 +870,86 @@
                   Remark: '',
                   temp_type: 'billprint',
                 }).then(response => {
-                  if (response.status) {
-                    resolve(true)
-                  } else {
-                    notification.warning({
-                      top: 92,
-                      message: response.message,
-                      duration: 5
-                    })
-                    resolve(false)
-                  }
+                  resolve(response)
                 })
               } else {
-                notification.warning({
-                  top: 92,
-                  message: result.ErrMesg,
-                  duration: 5
-                })
-                resolve(false)
+                resolve(result)
               }
             })
           })
         } else {
-          resolve(true)
+          resolve({status: true})
         }
-      }).then(res => { // 鎸夐挳鍒犻櫎
-        if (!res) return
+      }).then(res => { // 椤甸潰淇濆瓨
+        if (!res || !res.status) return res
 
-        if (delButtons.length === 0) {
+        return Api.getSystemConfig(param)
+      }).then(res => { // 鎸夐挳鍒犻櫎
+        if (!res || !res.status) return res
+
+        if (MenuType !== 'billPrint') { // 鍩烘湰淇℃伅鏀瑰彉鏃讹紝閫氱煡鑿滃崟鍒楄〃鏇存柊
+          localStorage.setItem('menuUpdate', new Date().getTime() + ',' + config.uuid)
+        }
+        config.open_edition = res.open_edition || ''
+        this.setState({
+          config,
+          oriConfig: fromJS(config).toJS(),
+          needUpdate: false
+        })
+
+        if (this.state.delButtons.length === 0) {
           return {
             status: true
           }
         } else {
           let _param = {
             func: 'sPC_MainMenu_Del',
-            MenuID: delButtons.join(',')
+            MenuID: this.state.delButtons.join(',')
           }
           return Api.getSystemConfig(_param)
         }
-      }).then(res => { // 椤甸潰淇濆瓨
-        if (!res) return
-
-        if (res.status) {
-          return Api.getSystemConfig(param)
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
       }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
-        if (!res) return
+        if (!res || !res.status) return res
 
-        if (res.status) {
-          if (MenuType !== 'billPrint') { // 鍩烘湰淇℃伅鏀瑰彉鏃讹紝閫氱煡鑿滃崟鍒楄〃鏇存柊
-            let ori = this.state.oriConfig
-            if (config.MenuName !== ori.MenuName || config.MenuNo !== ori.MenuNo || config.parentId !== ori.parentId) {
-              localStorage.setItem('menuUpdate', new Date().getTime())
-            }
-          }
-          config.open_edition = res.open_edition || ''
-          this.setState({
-            config,
-            oriConfig: fromJS(config).toJS(),
-          })
+        this.setState({
+          delButtons: []
+        })
 
-          if (btnParam.LText) {
-            return Api.getSystemConfig(btnParam)
-          } else {
-            return {
-              status: true
-            }
-          }
+        if (MenuType !== 'billPrint') {
+          return Api.getSystemConfig(btnParam)
         } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => { // 鎸夐挳澶嶅埗
-        if (!res) return
-        if (!res.status) {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-
-        if (copyButtons.length === 0) {
           return {
             status: true
           }
-        } else {
-          return new Promise(resolve => {
-            let deffers = copyButtons.map(item => {
-              return new Promise(resolve => {
-                if (btnIds.indexOf(item.uuid) === -1) { // 澶嶅埗鐨勬寜閽凡鍒犻櫎
-                  resolve({
-                    status: true
-                  })
-                  return
-                }
-
-                Api.getSystemConfig({
-                  func: 'sPC_Get_LongParam',
-                  MenuID: item.$originUuid
-                }).then(result => {
-                  if (result.status) {
-                    let _conf = ''
-              
-                    try {
-                      _conf = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : ''
-                    } catch (e) {
-                      console.warn('Parse Failure')
-                      _conf = ''
-                    }
-                    
-                    if (_conf) {
-                      _conf.components = MenuUtils.resetConfig(_conf.components)
-                      _conf.uuid = item.uuid
-                      _conf.MenuID = item.uuid
-                      _conf.Template = 'CustomPage'
-                      _conf.enabled = false
-                    } else {
-                      resolve({
-                        status: true
-                      })
-                      return
-                    }
-
-                    let _param = {
-                      func: 'sPC_ButtonParam_AddUpt',
-                      ParentID: config.uuid,
-                      MenuID: item.uuid,
-                      MenuNo: '',
-                      Template: 'CustomPage',
-                      MenuName: item.label,
-                      PageParam: JSON.stringify({Template: 'CustomPage'}),
-                      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_conf)))
-                    }
-            
-                    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) {
-                notification.warning({
-                  top: 92,
-                  message: error.message,
-                  duration: 5
-                })
-                resolve(false)
-              } else {
-                resolve({
-                  status: true
-                })
-              }
-            })
-          })
         }
       }).then(res => {
-        if (res && res.status) {
-          this.setState({
-            delButtons: [],
-            copyButtons: [],
-            menuloading: false
-          })
+        this.setState({
+          menuloading: false
+        })
+
+        if (!res) return
+
+        if (res.status) {
           notification.success({
             top: 92,
             message: '淇濆瓨鎴愬姛',
             duration: 2
           })
+          MKEmitter.emit('completeSave')
         } else {
-          this.setState({
-            menuloading: false
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
           })
         }
-        MKEmitter.emit('completeSave')
       })
     }, 300 + (+sessionStorage.getItem('mkDelay')))
   }
 
   getRoleFields = () => {
-    if (sessionStorage.getItem('sysRoles') || sessionStorage.getItem('permFuncField')) return
+    if (sessionStorage.getItem('sysRoles')) return
+
     Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
       if (res.status) {
         let _permFuncField = []
@@ -983,7 +1038,8 @@
         tree: '鏍戝舰鍒楄〃',
         chart: '鑷畾涔夊浘琛�',
         editor: '瀵屾枃鏈�',
-        group: '鍒嗙粍'
+        group: '鍒嗙粍',
+        iframe: 'iframe'
       }
       let subforbid = {
         editable: '鍙紪杈戣〃鏍�',
@@ -1047,6 +1103,7 @@
 
   changeSetting = () => {
     this.setState({settingshow: !this.state.settingshow})
+    sessionStorage.setItem('settingshow', '' + !this.state.settingshow)
 
     setTimeout(() => {
       MKEmitter.emit('tabsChange', 'all')
@@ -1054,19 +1111,19 @@
   }
 
   render () {
-    const { activeKey, comloading, MenuType, MenuId, config, settingshow, ParentId, menuloading, customComponents, eyeopen } = this.state
+    const { view, comloading, MenuType, MenuId, config, settingshow, ParentId, menuloading, customComponents, eyeopen, needUpdate } = this.state
 
     return (
       <ConfigProvider locale={_locale}>
-        <div className={'pc-menu-view ' + (MenuType || '')}>
-          <Header />
-          <DndProvider backend={HTML5Backend}>
+        <Header />
+        <DndProvider backend={HTML5Backend}>
+          {view !== 'popview' ? <div className={'pc-menu-view ' + (MenuType || '')}>
             <div className="menu-body">
               <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
                 <div className="draw">
                   {settingshow ? <DoubleLeftOutlined onClick={this.changeSetting}/> : <DoubleRightOutlined onClick={this.changeSetting}/>}
                 </div>
-                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+                <Collapse accordion defaultActiveKey="basedata" bordered={false}>
                   {/* 鍩烘湰淇℃伅 */}
                   <Panel header="鍩烘湰淇℃伅" key="basedata">
                     {/* 鑿滃崟淇℃伅 */}
@@ -1121,19 +1178,19 @@
                     <StyleCombControlButton menu={config} />
                     <PasteController insert={this.insert} />
                     <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config && config.enabled} onChange={this.onEnabledChange} />
-                    <Button type="primary" id="save-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
+                    <Button type="primary" id="save-config" className={needUpdate ? 'update-tip' : ''} onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
                     <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
                   </div>
                 } style={{ width: '100%' }}>
-                  {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
+                  {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : <Spin className="loading-config" size="large" />}
                 </Card>
               </div>
             </div>
-          </DndProvider>
-          <StyleController />
-          <StyleCombController />
-          <ModalController />
-        </div>
+          </div> : <PopView btn={this.state.popConfig} save={this.submitPopConfig} cancel={this.closePop}/>}
+        </DndProvider>
+        <ModalController />
+        <StyleController />
+        <StyleCombController />
       </ConfigProvider>
     )
   }
diff --git a/src/views/menudesign/index.scss b/src/views/menudesign/index.scss
index 4c68c97..6368ca8 100644
--- a/src/views/menudesign/index.scss
+++ b/src/views/menudesign/index.scss
@@ -5,12 +5,29 @@
 .pc-menu-view {
   background: #000;
   min-height: 100vh;
+  .loading-config {
+    position: absolute;
+    left: calc(50% - 22px);
+    top: calc(50vh - 70px);
+    z-index: 10;
+  }
   .mk-hidden {
     text-decoration: line-through!important;
     span {
       text-decoration: line-through!important;
     }
   }
+  .update-tip::after {
+    content: ' ';
+    display: inline-block;
+    position: absolute;
+    top: -4px;
+    right: -4px;
+    width: 8px;
+    height: 8px;
+    background-color: red;
+    border-radius: 8px;
+  }
   .eye-open {
     .component-name {
       display: block;
diff --git a/src/views/menudesign/menuform/index.jsx b/src/views/menudesign/menuform/index.jsx
index 5f217c5..dc39e55 100644
--- a/src/views/menudesign/menuform/index.jsx
+++ b/src/views/menudesign/menuform/index.jsx
@@ -26,7 +26,57 @@
   }
 
   UNSAFE_componentWillMount () {
+    if (sessionStorage.getItem('thdMenuList') && sessionStorage.getItem('fstMenuList')) {
+      this.setMenus()
+    } else {
+      this.getMenus()
+    }
+  }
+
+  setMenus = () => {
     const { MenuId, config } = this.props
+
+    let menulist = sessionStorage.getItem('fstMenuList')
+    let thdMenuList = sessionStorage.getItem('thdMenuList')
+
+    menulist = JSON.parse(menulist)
+    thdMenuList = JSON.parse(thdMenuList)
+
+    let thdMenu = null
+
+    thdMenuList.forEach(trd => {
+      if (MenuId === trd.MenuID) {
+        thdMenu = trd
+      }
+    })
+
+    let smenulist = []
+
+    if (thdMenu) {
+      menulist.forEach(item => {
+        if (item.MenuID === thdMenu.FstId) {
+          smenulist = item.children
+        }
+      })
+    }
+
+    this.props.updateConfig({...config, fstMenuId: thdMenu ? thdMenu.FstId : ''})
+
+    this.setState({
+      fstMenuId: thdMenu ? thdMenu.FstId : '',
+      menulist,
+      smenulist
+    }, () => {
+      this.props.form.setFieldsValue({
+        fstMenuId: thdMenu ? thdMenu.FstId : '',
+        parentId: thdMenu ? thdMenu.ParentId : ''
+      })
+    })
+  }
+
+  getMenus = () => {
+    const { MenuId, config } = this.props
+
     Api.getSystemConfig({func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}).then(result => {
       if (result.status) {
         let thdMenu = null
@@ -64,12 +114,7 @@
                     value: trd.MenuID,
                     label: trd.MenuName,
                     type: 'CommonTable',
-                    // disabled: trd.MenuID === MenuId
                     disabled: false
-                  }
-
-                  if (MenuId === trd.MenuID) {
-                    thdMenu = trdItem
                   }
 
                   if (trd.PageParam) {
@@ -79,6 +124,10 @@
                     } catch (e) {
 
                     }
+                  }
+
+                  if (MenuId === trd.MenuID) {
+                    thdMenu = trdItem
                   }
 
                   thdMenuList.push(trdItem)
@@ -93,11 +142,13 @@
         })
 
         let smenulist = []
-        menulist.forEach(item => {
-          if (thdMenu && (item.MenuID === thdMenu.FstId)) {
-            smenulist = item.children
-          }
-        })
+        if (thdMenu) {
+          menulist.forEach(item => {
+            if (item.MenuID === thdMenu.FstId) {
+              smenulist = item.children
+            }
+          })
+        }
         sessionStorage.setItem('fstMenuList', JSON.stringify(menulist))
         sessionStorage.setItem('thdMenuList', JSON.stringify(thdMenuList))
         this.props.updateConfig({...config, fstMenuId: thdMenu ? thdMenu.FstId : ''})
diff --git a/src/views/menudesign/popview/index.jsx b/src/views/menudesign/popview/index.jsx
new file mode 100644
index 0000000..7e63303
--- /dev/null
+++ b/src/views/menudesign/popview/index.jsx
@@ -0,0 +1,297 @@
+import React, { Component } from 'react'
+import { is, fromJS } from 'immutable'
+import PropTypes from 'prop-types'
+import { notification, Modal, Collapse, Card, Switch, Button } from 'antd'
+import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'
+
+import MKEmitter from '@/utils/events.js'
+import asyncComponent from '@/utils/asyncComponent'
+
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+
+const MenuForm = asyncComponent(() => import('./menuform'))
+const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
+const Modulecell = asyncComponent(() => import('@/menu/modulecell'))
+const MenuShell = asyncComponent(() => import('@/menu/menushell'))
+const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
+const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
+const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
+const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+
+class PopViewDesign extends Component {
+  static propTpyes = {
+    btn: PropTypes.object,
+    save: PropTypes.func,
+    cancel: PropTypes.func
+  }
+
+  state = {
+    menuloading: false,
+    oriConfig: null,
+    config: null,
+    comloading: false,
+    eyeopen: false
+  }
+
+  UNSAFE_componentWillMount() {
+    const { btn } = this.props
+    sessionStorage.setItem('editMenuType', 'popview')
+
+    let config = fromJS(btn.config).toJS()
+
+    window.GLOB.urlFields = []               // url鍙橀噺
+    window.GLOB.customMenu = config          // 淇濆瓨鑿滃崟淇℃伅
+
+    this.setState({config: config, oriConfig: fromJS(config).toJS()})
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('completeSave', this.completeSave)
+    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('completeSave', this.completeSave)
+    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
+  }
+
+  closeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!is(fromJS(oriConfig), fromJS(config))) {
+      const _this = this
+
+      confirm({
+        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
+        content: '',
+        onOk() {
+          _this.props.cancel()
+        },
+        onCancel() {}
+      })
+    } else {
+      this.props.cancel()
+    }
+  }
+
+  getMenuMessage = (config) => {
+    let tbs = []
+
+    let traversal = (components) => {
+      components.forEach(item => {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            traversal(tab.components)
+          })
+        } else if (item.type === 'group') {
+          traversal(item.components)
+        }
+      })
+    }
+
+    traversal(config.components)
+
+    config.$tables = Array.from(new Set(tbs))
+  }
+
+  completeSave = () => {
+    this.setState({
+      oriConfig: fromJS(this.state.config).toJS(),
+      menuloading: false
+    })
+  }
+
+  submitConfig = () => {
+    let config = fromJS(this.state.config).toJS()
+
+    if (config.cacheUseful === 'true' && !config.cacheTime) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勭紦瀛樿缃紒',
+        duration: 5
+      })
+      return
+    }
+
+    if (config.enabled && this.verifyConfig()) {
+      config.enabled = false
+    }
+
+    this.getMenuMessage(config)
+
+    this.setState({
+      config: config,
+      menuloading: true
+    })
+
+    window.GLOB.customMenu = config
+
+    this.props.save(fromJS(config).toJS())
+  }
+
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    if (!config.enabled && this.verifyConfig(true)) {
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: !config.enabled}
+    })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+
+    let check = (components) => {
+      components.forEach(item => {
+        if (error) return
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            check(tab.components)
+          })
+          return
+        } else if (item.type === 'group') {
+          check(item.components)
+          return
+        } else if (!item.errors || item.errors.length === 0) {
+          return
+        }
+        
+        item.errors.forEach(err => {
+          if (err.level !== 0 || error) return
+          error = `缁勪欢銆�${item.name}銆�${err.detail}`
+        })
+      })
+    }
+
+    check(config.components)
+
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
+  }
+
+  // 鏇存柊閰嶇疆淇℃伅
+  updateConfig = (config) => {
+    this.setState({
+      config: config
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  resetConfig = (config) => {
+    this.setState({
+      config,
+      comloading: true
+    }, () => {
+      this.setState({
+        comloading: false
+      })
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  /**
+   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
+   */
+  updatetable = (config) => {
+    this.setState({ config })
+
+    window.GLOB.customMenu = config
+  }
+
+  insert = (item) => {
+    let config = fromJS(this.state.config).toJS()
+
+    config.components.push(item)
+
+    this.setState({config})
+
+    window.GLOB.customMenu = config
+    
+    notification.success({
+      top: 92,
+      message: '绮樿创鎴愬姛锛�',
+      duration: 2
+    })
+  }
+
+  render () {
+    const { comloading, config, menuloading, eyeopen } = this.state
+
+    return (
+      <div className="pc-poper-view">
+        <div className="menu-body">
+          <div className="menu-setting">
+            <Collapse accordion defaultActiveKey="basedata" bordered={false}>
+              {/* 鍩烘湰淇℃伅 */}
+              <Panel header="鍩烘湰淇℃伅" key="basedata">
+                {/* 鑿滃崟淇℃伅 */}
+                <MenuForm config={config} updateConfig={this.updateConfig}/>
+                {/* 琛ㄥ悕娣诲姞 */}
+                <TableComponent config={config} updatetable={this.updatetable}/>
+              </Panel>
+              {/* 缁勪欢娣诲姞 */}
+              <Panel header="缁勪欢" key="component">
+                <SourceWrap MenuType="" />
+              </Panel>
+              <Panel header="鍏冪礌" key="element">
+                <Modulecell />
+              </Panel>
+              <Panel header="椤甸潰鏍峰紡" key="background">
+                <BgController config={config} updateConfig={this.updateConfig} />
+              </Panel>
+            </Collapse>
+          </div>
+          <div className={'menu-view' + (menuloading ? ' saving' : '') + (eyeopen ? ' eye-open' : '')}>
+            <Card title={
+              <div> {config.MenuName} </div>
+            } bordered={false} extra={
+              <div>
+                <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 缁勪欢鍚�</Button>
+                <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
+                <StyleCombControlButton menu={config} />
+                <PasteController insert={this.insert} />
+                <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config.enabled} onChange={this.onEnabledChange} />
+                <Button type="primary" id="save-pop-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
+                <Button type="default" onClick={this.closeView}>杩斿洖</Button>
+              </div>
+            } style={{ width: '100%' }}>
+              {!comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
+            </Card>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default PopViewDesign
\ No newline at end of file
diff --git a/src/views/popdesign/index.scss b/src/views/menudesign/popview/index.scss
similarity index 100%
rename from src/views/popdesign/index.scss
rename to src/views/menudesign/popview/index.scss
diff --git a/src/views/popdesign/menuform/index.jsx b/src/views/menudesign/popview/menuform/index.jsx
similarity index 70%
rename from src/views/popdesign/menuform/index.jsx
rename to src/views/menudesign/popview/menuform/index.jsx
index 6906e4d..a925865 100644
--- a/src/views/popdesign/menuform/index.jsx
+++ b/src/views/menudesign/popview/menuform/index.jsx
@@ -1,6 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Input, Tooltip, InputNumber } from 'antd'
+import { Form, Row, Col, Radio, Tooltip, InputNumber } from 'antd'
 import { QuestionCircleOutlined } from '@ant-design/icons'
 
 // import './index.scss'
@@ -9,16 +9,6 @@
   static propTpyes = {
     config: PropTypes.object,
     updateConfig: PropTypes.func
-  }
-
-  // 鑿滃崟鍚嶇О
-  changeName = (e) => {
-    this.props.updateConfig({...this.props.config, MenuName: e.target.value})
-  }
-
-  // 鑿滃崟鍙傛暟
-  changeNo = (e) => {
-    this.props.updateConfig({...this.props.config, MenuNo: e.target.value})
   }
   
   changeCacheDay = (val) => {
@@ -55,32 +45,6 @@
     return (
       <Form {...formItemLayout}>
         <Row>
-        <Col span={24}>
-            <Form.Item label="鑿滃崟鍚嶇О">
-              {getFieldDecorator('MenuName', {
-                initialValue: config.MenuName,
-                rules: [
-                  {
-                    required: true,
-                    message: '璇疯緭鍏ヨ彍鍗曞悕绉�!'
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" onChange={this.changeName}/>)}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label="鑿滃崟鍙傛暟">
-              {getFieldDecorator('MenuNo', {
-                initialValue: config.MenuNo,
-                rules: [
-                  {
-                    required: true,
-                    message: '璇疯緭鍏ヨ彍鍗曞弬鏁�!'
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" onChange={this.changeNo}/>)}
-            </Form.Item>
-          </Col>
           <Col span={24}>
             <Form.Item label={
               <Tooltip placement="topLeft" title="瀵逛簬涓嶇粡甯告�у彉鍔ㄧ殑淇℃伅锛岀紦瀛樻暟鎹湁鍔╀簬鎻愰珮鏌ヨ鏁堢巼銆�">
diff --git a/src/views/popdesign/menuform/index.scss b/src/views/menudesign/popview/menuform/index.scss
similarity index 100%
rename from src/views/popdesign/menuform/index.scss
rename to src/views/menudesign/popview/menuform/index.scss
diff --git a/src/views/menudesign/printmenuform/index.jsx b/src/views/menudesign/printmenuform/index.jsx
index 181163f..9f9c9bb 100644
--- a/src/views/menudesign/printmenuform/index.jsx
+++ b/src/views/menudesign/printmenuform/index.jsx
@@ -11,13 +11,6 @@
     updateConfig: PropTypes.func
   }
 
-  // changeFirstCount = (val) => {
-  //   if (typeof(val) !== 'number') {
-  //     val = ''
-  //   }
-  //   this.props.updateConfig({...this.props.config, firstCount: val})
-  // }
-
   changeCount = (val) => {
     if (typeof(val) !== 'number') {
       val = ''
@@ -42,13 +35,6 @@
   onPrintPageChange = (val) => {
     this.props.updateConfig({...this.props.config, printPage: val})
   }
-
-  // changeLastCount = (val) => {
-  //   if (typeof(val) !== 'number') {
-  //     val = ''
-  //   }
-  //   this.props.updateConfig({...this.props.config, lastCount: val})
-  // }
 
   pageSizeChange = (val) => {
     this.props.updateConfig({...this.props.config, pageSize: val})
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index bbd3cdb..1eed346 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -12,7 +12,7 @@
 import Utils, { setGLOBFuncs } from '@/utils/utils.js'
 import antdZhCN from 'antd/es/locale/zh_CN'
 import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/utils/utils-custom.js'
+import MenuUtils, { getTables } from '@/utils/utils-custom.js'
 import asyncComponent from '@/utils/asyncComponent'
 
 import '@/assets/css/design.scss'
@@ -64,11 +64,10 @@
     MenuId: '',
     MenuName: '',
     MenuNo: '',
-    activeKey: 'component',
+    activeKey: 'basedata',
     menuloading: false,
     oriConfig: null,
     config: null,
-    customComponents: [],
     direction: 'vertical',
     settingshow: true,
     controlshow: true,
@@ -76,7 +75,7 @@
     adapters: [],
     viewType: 'menu',
     eyeopen: false,
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
+    needUpdate: false
   }
 
   UNSAFE_componentWillMount() {
@@ -122,7 +121,6 @@
           viewType: /^userbind/.test(param.MenuID) ? 'userbind' : 'menu'
         }, () => {
           this.getMenuParam(param)
-          this.getRelationMenus()
         })
       }
     } catch (e) {
@@ -149,19 +147,9 @@
       document.getElementById('mk-mob-design-view').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh; height: 100vh; background: #fff;">鏈簲鐢ㄦ病鏈塒C绔〉闈㈢殑缂栬緫鏉冮檺锛岃鑱旂郴绠$悊鍛橈紒</div>'
       return
     }
-    MKEmitter.addListener('modalStatus', this.modalStatus)
     MKEmitter.addListener('triggerMenuSave', this.submitConfig)
     MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
-    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     setTimeout(() => {
-      if (sessionStorage.getItem('app_custom_components')) {
-        let list = sessionStorage.getItem('app_custom_components')
-        list = JSON.parse(list)
-
-        this.setCustomComponent(list)
-      } else {
-        this.updateCustomComponent()
-      }
       this.getAppPictures()
       this.getSmStemp()
       this.getRoleFields()
@@ -187,10 +175,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -216,53 +217,49 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
     MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
     MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
-    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   getSmStemp = () => {
-    let _sql = `select聽ID,TemplateCode,SignName+'_'+describe as SignName from (select * from bd_msn_sms_temp where deleted=0 and status=20 ) a 
-      inner join (select openid from sapp where id='${window.GLOB.appkey}') b 
-      on a.openid=b.openid`
-
-    _sql = Utils.formatOptions(_sql)
-
-    let param = {
-      func: 'sPC_Get_SelectedList',
-      LText: _sql,
-      obj_name: 'data',
-      arr_field: 'ID,TemplateCode,SignName'
-    }
-    
-    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
-    param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp) // 浜戠鏁版嵁楠岃瘉
-    
-    Api.getSystemConfig(param).then(res => {
-      let msgs = []
-      if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
-        })
-      } else if (res.data) {
-        msgs = res.data
+    if (!sessionStorage.getItem('msgTemplate')) {
+      let _sql = `select聽ID,TemplateCode,SignName+'_'+describe as SignName from (select * from bd_msn_sms_temp where deleted=0 and status=20 ) a 
+        inner join (select openid from sapp where id='${window.GLOB.appkey}') b 
+        on a.openid=b.openid`
+  
+      _sql = Utils.formatOptions(_sql)
+  
+      let param = {
+        func: 'sPC_Get_SelectedList',
+        LText: _sql,
+        obj_name: 'data',
+        arr_field: 'ID,TemplateCode,SignName'
       }
-      sessionStorage.setItem('msgTemplate', JSON.stringify(msgs))
-    })
+      
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
+      param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp) // 浜戠鏁版嵁楠岃瘉
+      
+      Api.getSystemConfig(param).then(res => {
+        let msgs = []
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+        } else if (res.data) {
+          msgs = res.data
+        }
+        sessionStorage.setItem('msgTemplate', JSON.stringify(msgs))
+      })
+    }
   }
 
   changeEditMenu = (menu) => {
     const { oriConfig, config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -369,90 +366,40 @@
   }
 
   getAppPictures = () => {
-    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
-
-    Api.getSystemConfig({
+    if (sessionStorage.getItem('app_pictures')) return
+    
+    let deffers = []
+    let param = {
       func: 's_url_db_adduptdel',
       PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
       PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-      typecharone: 'image',
       type: 'search'
-    }).then(res => {
-      if (res.status) {
-        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
-      }
-
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'video',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
-        }
-      })
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'color',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
-        }
-      })
-    })
-  }
-
-  updateCustomComponent = () => {
-    Api.getSystemConfig({
-      func: 's_get_custom_components',
-      typename: sessionStorage.getItem('typename'),
-      typecharone: ''
-    }).then(res => {
-      if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
+    }
+    deffers = [new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
+          resolve(res.data)
         })
-      } else if (res.cus_list) {
-        sessionStorage.setItem('app_custom_components', JSON.stringify(res.cus_list))
-        this.setCustomComponent(res.cus_list)
-      }
+      }, 500)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1000)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1500)
+    })]
+
+    Promise.all(deffers).then(response => {
+      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
+      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
+      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
     })
-  }
-
-  setCustomComponent = (cus_list) => {
-    let coms = []
-
-    cus_list.forEach(item => {
-      let config = ''
-
-      try {
-        config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
-      } catch (e) {
-        console.warn('Parse Failure')
-        config = ''
-      }
-
-      if (!config || !item.c_name) return
-
-      window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
-      coms.push({
-        uuid: item.c_id,
-        type: 'menu',
-        title: item.c_name,
-        url: item.images,
-        component: config.type,
-        subtype: config.subtype,
-        config
-      })
-    })
-    this.setState({customComponents: coms})
   }
 
   closeView = () => {
@@ -460,7 +407,7 @@
 
     if (!config) {
       window.close()
-    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    } else if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       confirm({
         title: '閰嶇疆淇℃伅鏈繚瀛橈紝纭畾鍏抽棴鍚楋紵',
         content: '',
@@ -479,7 +426,7 @@
 
     if (!config) {
       window.history.back()
-    } else if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    } else if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       confirm({
         title: '閰嶇疆淇℃伅鏈繚瀛橈紝纭畾鍚庨��鍚楋紵',
         content: '',
@@ -516,7 +463,6 @@
         this.getCopyParam(urlParam)
       } else {
         let config = null
-        let isCreate = false
 
         try {
           config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
@@ -531,15 +477,13 @@
         }
 
         if (!config) {
-          isCreate = true
           config = {
-            version: 1.0,
+            version: 2.0,
             uuid: MenuId,
             MenuID: MenuId,
             Template: 'webPage',
             enabled: false,
             MenuName: urlParam.MenuName || '',
-            // MenuNo: urlParam.MenuNo || '',
             MenuNo: '',
             tables: [],
             components: [],
@@ -556,59 +500,43 @@
         config.open_edition = result.open_edition || ''
         window.GLOB.urlFields = config.urlFields || []
 
-        let indeComs = []
+        if (config.version !== 2.0) {
+          config.components = this.collectTB(config.components)
+          config.version = 2.0
+
+          this.setState({
+            needUpdate: true
+          })
+        }
+
+        let navItem = null
         config.components.forEach(item => {
           if (item.type === 'navbar') {
-            indeComs.push(fromJS(item).toJS())
+            navItem = fromJS(item).toJS()
           }
         })
 
-        if (indeComs.length === 0) {
+        if (!navItem) {
           this.setState({
-            oriConfig: isCreate ? null : config,
-            config: fromJS(config).toJS(),
-            activeKey: isCreate ? 'basedata' : 'component',
+            oriConfig: fromJS(config).toJS(),
+            config: config,
             loading: false
           })
           window.GLOB.customMenu = config
         } else {
-          this.jointComponents(config, indeComs, isCreate)
+          this.jointComponents(config, navItem)
         }
       }
     })
     this.getAppMenus()
   }
 
-  getRelationMenus = () => {
-    const { MenuId } = this.state
-    
-    let param = {
-      func: 's_get_menu_used_list',
-      TypeCharOne: sessionStorage.getItem('kei_no'),
-      typename: sessionStorage.getItem('typename'),
-      par_menuid: MenuId,
-      // used_menuid: MenuId
-    }
-
-    Api.getSystemConfig(param).then(result => {
-      if (!result.status) {
-        notification.warning({
-          top: 92,
-          message: result.message,
-          duration: 5
-        })
-        return
-      }
-    })
-  }
-
   setUserBindMenu = (config, result) => {
     const { MenuId } = this.state
-    let isCreate = !config
 
     if (!config) {
       config = {
-        version: 1.0,
+        version: 2.0,
         uuid: MenuId,
         MenuID: MenuId,
         Template: 'webPage',
@@ -673,9 +601,8 @@
     config.open_edition = result.open_edition || ''
 
     this.setState({
-      oriConfig: isCreate ? null : config,
-      config: fromJS(config).toJS(),
-      activeKey: isCreate ? 'basedata' : 'component',
+      oriConfig: fromJS(config).toJS(),
+      config: config,
       loading: false
     })
     window.GLOB.customMenu = config
@@ -763,7 +690,7 @@
       
       if (!config) {
         config = {
-          version: 1.0,
+          version: 2.0,
           uuid: MenuId,
           MenuID: MenuId,
           Template: 'webPage',
@@ -788,6 +715,16 @@
         }
 
         config.components = MenuUtils.resetConfig(config.components, uuids, urlParam.clearMenu)
+
+        if (config.version !== 2.0) {
+          config.components = this.collectTB(config.components)
+          config.version = 2.0
+
+          this.setState({
+            needUpdate: true
+          })
+        }
+
         config.enabled = false
         message.success('澶嶅埗鎴愬姛锛屼繚瀛樺悗鐢熸晥銆�')
       }
@@ -799,14 +736,14 @@
       // config.MenuNo = urlParam.MenuNo || ''
       config.MenuNo = ''
 
-      let indeComs = []
+      let navItem = null
       config.components.forEach(item => {
         if (item.type === 'navbar') {
-          indeComs.push(fromJS(item).toJS())
+          navItem = fromJS(item).toJS()
         }
       })
 
-      if (indeComs.length === 0) {
+      if (!navItem) {
         this.setState({
           oriConfig: null,
           config: fromJS(config).toJS(),
@@ -815,37 +752,27 @@
 
         window.GLOB.customMenu = config
       } else {
-        this.jointComponents(config, indeComs, true)
+        this.jointComponents(config, navItem)
       }
     })
   }
 
-  jointComponents = (config, indeComs, isCreate) => {
-    let deffers = indeComs.map(item => {
-      return new Promise(resolve => {
-        Api.getSystemConfig({
-          func: 'sPC_Get_LongParam',
-          TypeCharOne: sessionStorage.getItem('kei_no'),
-          typename: sessionStorage.getItem('typename'),
-          MenuID: item.uuid
-        }).then(res => {
-          res.uuid = item.uuid
-
-          if (!res.status) {
-            notification.warning({
-              top: 92,
-              message: res.message,
-              duration: 5
-            })
-          }
-          
-          resolve(res)
+  jointComponents = (config, navItem) => {
+    Api.getSystemConfig({
+      func: 'sPC_Get_LongParam',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: sessionStorage.getItem('typename'),
+      MenuID: navItem.uuid
+    }).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
         })
-      })
-    })
-    Promise.all(deffers).then(result => {
-      let _conf = {}
-      result.forEach(res => {
+
+        config.components = config.components.filter(item => item.type !== 'navbar')
+      } else {
         let _config = null
         try {
           _config = res.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(res.LongParam))) : null
@@ -856,40 +783,56 @@
 
         if (_config) {
           _config.open_edition = res.open_edition || ''
-          _conf[res.uuid] = _config
-          window.GLOB.CacheIndependent.set(res.uuid, fromJS(_config).toJS())
+          config.components = config.components.map(item => {
+            if (item.type === 'navbar') {
+              return _config
+            }
+            return item
+          })
+
+          window.GLOB.CacheIndependent.set(navItem.uuid, fromJS(_config).toJS())
+        } else {
+          config.components = config.components.filter(item => item.type !== 'navbar')
         }
-      })
-
-      let _length = config.components.length
-      config.components = config.components.map(item => {
-        if (item.type === 'navbar') {
-          if (_conf[item.uuid]) {
-            item = _conf[item.uuid]
-          } else {
-            item = null
-          }
-        }
-        return item
-      })
-
-      config.components = config.components.filter(Boolean)
-
-      if (_length > config.components.length) {
-        notification.warning({
-          top: 92,
-          message: '閮ㄥ垎缁勪欢宸插垹闄わ紒',
-          duration: 5
-        })
       }
-
+      
       this.setState({
-        oriConfig: isCreate ? null : fromJS(config).toJS(),
-        activeKey: isCreate ? 'basedata' : 'component',
+        oriConfig: fromJS(config).toJS(),
         config: config,
         loading: false
       })
+
       window.GLOB.customMenu = config
+    })
+  }
+
+  collectTB = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          delete tab.floor
+          delete tab.hasSearch
+          delete tab.parentId
+
+          tab.components = this.collectTB(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.collectTB(item.components)
+      } else if (!['search', 'navbar', 'login', 'topbar', 'officialAccount'].includes(item.type)) {
+        item.$tables = getTables(item)
+      }
+
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
+      delete item.tabId
+      delete item.parentId
+      delete item.btnlog
+      delete item.floor
+      delete item.dataName
+
+      return item
     })
   }
 
@@ -903,15 +846,21 @@
           tbs.push(...item.$tables)
         }
 
+        if (item.plot && item.plot.permission !== 'true') {
+          return
+        } else if (['login', 'navbar', 'topbar'].includes(item.type) || item.subtype === 'commonbar') {
+          return
+        } else if (!['tabs', 'group'].includes(item.type) && item.wrap && item.wrap.permission !== 'true') {
+          return
+        }
+
         let m = {
           key: item.uuid,
           title: item.name,
           children: []
         }
 
-        if (item.type === 'topbar' || item.type === 'login' || item.type === 'navbar' || (item.type === 'menubar' && item.subtype === 'commonbar')) {
-          return null
-        } else if (item.type === 'tabs') {
+        if (item.type === 'tabs') {
           let tabs = []
           let mm = []
           item.subtabs.forEach(tab => {
@@ -938,7 +887,7 @@
             list.push(...mm)
           }
           
-          return null
+          return
         } else if (item.type === 'group') {
           m.children = traversal(item.components)
 
@@ -948,12 +897,8 @@
             list.push(...m.children)
           }
           
-          return null
-        } if (item.plot && item.plot.permission !== 'true') {
-          return null
-        } if (item.wrap && item.wrap.permission !== 'true') {
-          return null
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+          return
+        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
           item.action && item.action.forEach(btn => {
             if (btn.hidden === 'true') return
 
@@ -964,20 +909,7 @@
           })
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') return
-
-              m.children.push({
-                key: cell.uuid,
-                title: cell.label,
-              })
-            })
-          })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') return
+              if (cell.eleType !== 'button' || cell.hidden === 'true') return
 
               m.children.push({
                 key: cell.uuid,
@@ -987,8 +919,7 @@
           })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
-            if (cell.eleType !== 'button') return
-            if (cell.hidden === 'true') return
+            if (cell.eleType !== 'button' || cell.hidden === 'true') return
 
             m.children.push({
               key: cell.uuid,
@@ -1015,7 +946,7 @@
               }
             })
           }
-        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        } else if (item.type === 'table') {
           item.action && item.action.forEach(btn => {
             if (btn.hidden === 'true') return
 
@@ -1044,6 +975,14 @@
     }
 
     let trees = traversal(config.components)
+
+    if (config.interfaces) {
+      config.interfaces.forEach(item => {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+      })
+    }
 
     return trees
   }
@@ -1084,7 +1023,6 @@
                 menus.push({
                   MenuID: m.uuid,
                   MenuName: m.setting.name,
-                  // MenuNo: m.setting.MenuNo
                   MenuNo: ''
                 })
               }
@@ -1098,7 +1036,7 @@
           })
         } else if (item.type === 'group') {
           traversal(item.components)
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
           item.action && item.action.forEach(btn => {
             if (btn.linkmenu && menuObj[btn.linkmenu]) {
               menus.push(menuObj[btn.linkmenu])
@@ -1126,21 +1064,6 @@
               }
             })
           })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            if (card.setting.click === 'menu' && menuObj[card.setting.menu]) {
-              menus.push(menuObj[card.setting.menu])
-            }
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-
-              if (cell.linkmenu && menuObj[cell.linkmenu]) {
-                menus.push(menuObj[cell.linkmenu])
-              } else if (cell.openmenu && menuObj[cell.openmenu]) {
-                menus.push(menuObj[cell.openmenu])
-              }
-            })
-          })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
             if (cell.eleType !== 'button') return
@@ -1157,7 +1080,7 @@
               menus.push(menuObj[m.subButton.linkmenu])
             }
           })
-        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        } else if (item.type === 'table') {
           item.action && item.action.forEach(btn => {
             if (btn.linkmenu && menuObj[btn.linkmenu]) {
               menus.push(menuObj[btn.linkmenu])
@@ -1216,7 +1139,7 @@
           })
         } else if (item.type === 'group') {
           item.components = traversal(item.components)
-        } else if (['card', 'carousel', 'timeline'].includes(item.type) || (item.type === 'table' && item.subtype === 'tablecard')) {
+        } else if (['card', 'carousel', 'timeline'].includes(item.type)) {
           item.subcards.forEach(card => {
             card.miniStyle = this.transferStyle(card.style)
             card.elements = card.elements.map(cell => {
@@ -1233,7 +1156,7 @@
             menu.miniStyle = this.transferStyle(menu.style)
             return menu
           })
-        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        } else if (item.type === 'table') {
           let getCols = (cols) => {
             return cols.map(col => {
               if (col.type === 'colspan') {
@@ -1404,196 +1327,175 @@
       param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
       param.secretkey = Utils.encrypt('', param.timestamp)
 
-      new Promise(resolve => {
-        let _config = fromJS(config).toJS()
-        let indeComs = []
-        _config.components = _config.components.map(item => {
-          if (item.type === 'navbar') {
-            indeComs.push(item)
-            return {
-              type: 'navbar',
-              uuid: item.uuid
-            }
+      let _config = fromJS(config).toJS()
+      let NavBar = null
+      _config.components = _config.components.map(item => {
+        if (item.type === 'navbar') {
+          NavBar = fromJS(item).toJS()
+          return {
+            type: 'navbar',
+            uuid: item.uuid
           }
-          return item
-        })
+        }
+        return item
+      })
 
-        param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+      param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
 
-        if (indeComs.length === 0) {
-          resolve(true)
+      new Promise(resolve => {
+        if (!NavBar) {
+          resolve({status: true})
         } else {
-          let new_open_edition = {}
-          let deffers = indeComs.map(item => {
-            return new Promise(resolve => {
-              let _item = window.GLOB.CacheIndependent.get(item.uuid)
-              if (_item && is(fromJS(_item), fromJS(item))) {
-                new_open_edition[item.uuid] = item.open_edition || ''
-                resolve()
-                return
-              }
-
-              let roles = {
-                type: 'navbar',
-                version: '1.0',
-                key: item.uuid,
-                title: item.name,
-                children: []
-              }
-      
-              if (item.wrap.permission === 'true') {
-                roles.children = item.menus.map(menu => {
-                  return {
-                    key: menu.MenuID,
-                    title: menu.name
-                  }
-                })
-              } else {
-                roles.pass = true
-              }
-
-              let _param = {
-                func: 'sPC_TrdMenu_AddUpt',
-                FstID: 'mk_app',
-                SndID: 'mk_app',
-                ParentID: 'mk_app',
-                MenuID: item.uuid,
-                MenuNo: item.wrap.MenuNo || Utils.getuuid(),
-                EasyCode: '',
-                Template: item.type,
-                TypeCharOne: sessionStorage.getItem('kei_no'),
-                Typename: sessionStorage.getItem('typename'),
-                MenuName: item.name || '',
-                PageParam: JSON.stringify({Template: item.type}),
-                open_edition: _item ? (_item.open_edition || '') : '',
-                menus_rolelist: window.btoa(window.encodeURIComponent(JSON.stringify(roles))),
-                LText: '',
-                LTexttb: ''
-              }
-
-              _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(item)))
-              _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              _param.secretkey = Utils.encrypt('', _param.timestamp)
-              
-              Api.getSystemConfig(_param).then(res => {
-                if (!res.status) {
-                  notification.warning({
-                    top: 92,
-                    message: res.message,
-                    duration: 5
-                  })
-                  this.setState({ menuloading: false })
-                  return
-                }
-
-                new_open_edition[item.uuid] = res.open_edition || ''
-                resolve()
-              })
-            })
-          })
-          Promise.all(deffers).then(() => {
-            let appViewList = sessionStorage.getItem('appViewList')
-            appViewList = JSON.parse(appViewList)
-            let _appViewList = fromJS(appViewList).toJS()
-            let appIndeList = appViewList.map(item => item.keys_id).join(',')
-
-            config.components = config.components.map(item => {
-              if (item.type === 'navbar') {
-                item.open_edition = new_open_edition[item.uuid] || ''
-                window.GLOB.CacheIndependent.set(item.uuid, fromJS(item).toJS())
-
-                if (appIndeList.indexOf(item.uuid) === -1) {
-                  appViewList.unshift({
-                    appkey: window.GLOB.appkey || '',
-                    bid: sessionStorage.getItem('appId') || '',
-                    kei_no: sessionStorage.getItem('kei_no') || '',
-                    keys_id: item.uuid,
-                    keys_type: 'navbar',
-                    remark: item.name
-                  })
-                } else {
-                  appViewList = appViewList.map(view => {
-                    if (view.keys_id === item.uuid) {
-                      view.remark = item.name
-                    }
-                    return view
-                  })
-                }
-              }
-              return item
-            })
-
-            if (!is(fromJS(appViewList), fromJS(_appViewList))) {
-              let param = {
-                func: 's_kei_link_keyids_addupt',
-                BID: sessionStorage.getItem('appId'),
-                exec_type: 'y',
-                LText: ''
-              }
+          let _item = window.GLOB.CacheIndependent.get(NavBar.uuid) || {}
+          NavBar.open_edition = _item.open_edition || ''
+          if (is(fromJS(_item), fromJS(NavBar))) {
+            resolve({status: true})
+          } else {
+            let roles = {
+              type: 'navbar',
+              version: '1.0',
+              key: NavBar.uuid,
+              title: NavBar.name,
+              children: []
+            }
     
-              param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
-              param.LText = param.LText.join(' union all ')
-              param.LText = Utils.formatOptions(param.LText)
-        
-              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              param.secretkey = Utils.encrypt('', param.timestamp)
-    
-              Api.getSystemConfig(param).then(result => {
-                if (!result.status) {
-                  notification.warning({
-                    top: 92,
-                    message: result.message,
-                    duration: 5
-                  })
-                  this.setState({ menuloading: false })
-                } else {
-                  sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
-                  resolve(true)
+            if (NavBar.wrap.permission === 'true') {
+              roles.children = NavBar.menus.map(menu => {
+                return {
+                  key: menu.MenuID,
+                  title: menu.name
                 }
               })
             } else {
-              resolve(true)
+              roles.pass = true
             }
-          })
+
+            let _param = {
+              func: 'sPC_TrdMenu_AddUpt',
+              FstID: 'mk_app',
+              SndID: 'mk_app',
+              ParentID: 'mk_app',
+              MenuID: NavBar.uuid,
+              MenuNo: NavBar.wrap.MenuNo || Utils.getuuid(),
+              EasyCode: '',
+              Template: NavBar.type,
+              TypeCharOne: sessionStorage.getItem('kei_no'),
+              Typename: sessionStorage.getItem('typename'),
+              MenuName: NavBar.name || '',
+              PageParam: JSON.stringify({Template: NavBar.type}),
+              open_edition: _item ? (_item.open_edition || '') : '',
+              menus_rolelist: window.btoa(window.encodeURIComponent(JSON.stringify(roles))),
+              LText: '',
+              LTexttb: ''
+            }
+
+            _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(NavBar)))
+            _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+            _param.secretkey = Utils.encrypt('', _param.timestamp)
+            
+            Api.getSystemConfig(_param).then(res => {
+              if (res.status) {
+                NavBar.open_edition = res.open_edition || ''
+                window.GLOB.CacheIndependent.set(NavBar.uuid, fromJS(NavBar).toJS())
+
+                let appViewList = sessionStorage.getItem('appViewList')
+                let _appViewList = JSON.parse(appViewList)
+
+                let index = _appViewList.findIndex(item => item.keys_id === NavBar.uuid)
+                
+                if (index === -1) {
+                  _appViewList.unshift({
+                    appkey: window.GLOB.appkey || '',
+                    bid: sessionStorage.getItem('appId') || '',
+                    kei_no: sessionStorage.getItem('kei_no') || '',
+                    keys_id: NavBar.uuid,
+                    keys_type: 'navbar',
+                    remark: NavBar.name
+                  })
+                } else {
+                  _appViewList = _appViewList.map(item => {
+                    if (item.keys_id === NavBar.uuid && item.remark !== NavBar.name) {
+                      item.remark = NavBar.name
+                    }
+                    return item
+                  })
+                }
+
+                let viewList = JSON.stringify(_appViewList)
+
+                if (appViewList !== viewList) {
+                  let kparam = {
+                    func: 's_kei_link_keyids_addupt',
+                    BID: sessionStorage.getItem('appId'),
+                    exec_type: 'y',
+                    LText: ''
+                  }
+        
+                  kparam.LText = _appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+                  kparam.LText = kparam.LText.join(' union all ')
+                  kparam.LText = Utils.formatOptions(kparam.LText)
+            
+                  kparam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+                  kparam.secretkey = Utils.encrypt('', kparam.timestamp)
+        
+                  Api.getSystemConfig(kparam).then(result => {
+                    if (result.status) {
+                      sessionStorage.setItem('appViewList', viewList)
+                    }
+
+                    resolve(result)
+                  })
+                } else {
+                  resolve(res)
+                }
+              } else {
+                resolve(res)
+              }
+            })
+          }
         }
       }).then(res => { // 椤甸潰淇濆瓨
-        if (!res) return
+        if (!res || !res.status) return res
 
         return Api.getSystemConfig(param)
       }).then(res => {
+        this.setState({
+          menuloading: false
+        })
+
         if (!res) return
 
         if (res.status) {
           config.open_edition = res.open_edition || ''
-          
+
           this.setState({
             config,
             oriConfig: fromJS(config).toJS(),
-            menuloading: false
+            needUpdate: false
           })
+
+          localStorage.setItem('mobUpdate', new Date().getTime() + ',' + config.uuid)
 
           notification.success({
             top: 92,
             message: '淇濆瓨鎴愬姛',
             duration: 2
           })
+          MKEmitter.emit('completeSave')
         } else {
           notification.warning({
             top: 92,
             message: res.message,
             duration: 5
           })
-          this.setState({
-            menuloading: false
-          })
         }
-        MKEmitter.emit('completeSave')
       })
     }, 300 + (+sessionStorage.getItem('mkDelay')))
   }
 
   getRoleFields = () => {
-    if (sessionStorage.getItem('sysRoles') || sessionStorage.getItem('permFuncField')) return
+    if (sessionStorage.getItem('sysRoles')) return
+
     Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
       if (res.status) {
         let _permFuncField = []
@@ -1678,10 +1580,6 @@
     }
 
     check(config.components, 1, 'view')
-
-    // if (!error && viewType === 'userbind' && config.components.filter(item => item.type === 'login').length === 0) {
-    //   error = '鐢ㄦ埛缁戝畾椤甸潰蹇呴』娣诲姞鐧诲綍銆�'
-    // }
 
     if (!error && searchSum > 1) {
       error = '鎼滅储缁勪欢涓庡鑸爮鐨勬悳绱㈠姛鑳戒笉鍙悓鏃朵娇鐢ㄣ��'
@@ -1769,7 +1667,7 @@
   refreshView = () => {
     const { oriConfig, config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1789,7 +1687,7 @@
   setHomeView = () => {
     const { oriConfig, config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1848,7 +1746,7 @@
   setLoginView = () => {
     const { oriConfig, config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1942,7 +1840,7 @@
 
 
   render () {
-    const { viewType, comloading, loading, settingshow, controlshow, activeKey, MenuId, config, menuloading, customComponents, adapters, eyeopen } = this.state
+    const { viewType, comloading, loading, settingshow, controlshow, activeKey, MenuId, config, menuloading, adapters, eyeopen, needUpdate } = this.state
 
     return (
       <ConfigProvider locale={antdZhCN}>
@@ -1978,9 +1876,6 @@
                   <Panel header="鍏冪礌" key="element">
                     <Modulecell />
                   </Panel>
-                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" className="cuscomponent" key="cuscomponent">
-                    <SourceWrap components={customComponents} />
-                  </Panel> : null}
                   <Panel header={'椤甸潰鏍峰紡'} key="background">
                     {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                   </Panel>
@@ -1993,7 +1888,7 @@
                 {!controlshow ? <DoubleLeftOutlined onClick={() => {this.setState({controlshow: true})}}/> : null}
               </div>
               <div className="wrap">
-                <Button type="primary" onClick={this.submitConfig} id="save-config" loading={menuloading}>淇濆瓨</Button>
+                <Button type="primary" className={needUpdate ? 'update-tip' : ''} onClick={this.submitConfig} id="save-config" loading={menuloading}>淇濆瓨</Button>
                 <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config && config.enabled} onChange={this.onEnabledChange} />
                 <ArrowLeftOutlined title="鍚庨��" className="back-view" onClick={this.backView}/>
                 <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 缁勪欢鍚�</Button>
diff --git a/src/views/mobdesign/index.scss b/src/views/mobdesign/index.scss
index 0dfd9d5..11cb042 100644
--- a/src/views/mobdesign/index.scss
+++ b/src/views/mobdesign/index.scss
@@ -9,6 +9,17 @@
       display: none;
     }
   }
+  .update-tip::after {
+    content: ' ';
+    display: inline-block;
+    position: absolute;
+    top: -4px;
+    right: -4px;
+    width: 8px;
+    height: 8px;
+    background-color: red;
+    border-radius: 8px;
+  }
   .component-name {
     position: absolute;
     z-index: 9;
@@ -304,4 +315,8 @@
 
 body {
   overflow-y: hidden;
+}
+
+.user-component-wrap {
+  display: none!important;
 }
\ No newline at end of file
diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx
index f3a98e3..fa764b8 100644
--- a/src/views/pcdesign/index.jsx
+++ b/src/views/pcdesign/index.jsx
@@ -10,7 +10,7 @@
 
 import Api from '@/api'
 import Utils, { setGLOBFuncs } from '@/utils/utils.js'
-// import antdEnUS from 'antd/es/locale/en_US'
+import { getTables } from '@/utils/utils-custom.js'
 import antdZhCN from 'antd/es/locale/zh_CN'
 import MKEmitter from '@/utils/events.js'
 import MenuUtils from '@/utils/utils-custom.js'
@@ -24,6 +24,8 @@
 const { Paragraph } = Typography
 
 const MenuForm = asyncComponent(() => import('./menuform'))
+const Header = asyncComponent(() => import('@/menu/header'))
+const PopView = asyncComponent(() => import('@/views/menudesign/popview'))
 const Transfer = asyncComponent(() => import('@/pc/transfer'))
 const Versions = asyncComponent(() => import('@/menu/versions'))
 const MenuShell = asyncComponent(() => import('@/pc/menushell'))
@@ -59,18 +61,17 @@
     MenuId: '',
     MenuName: '',
     MenuNo: '',
-    delButtons: [],
-    copyButtons: [],
     activeKey: 'basedata',
     menuloading: false,
     oriConfig: null,
     config: null,
-    customComponents: [],
     settingshow: sessionStorage.getItem('settingshow') !== 'false',
     controlshow: sessionStorage.getItem('controlshow') !== 'false',
     comloading: false,
     eyeopen: false,
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
+    view: '',
+    popConfig: null,
+    needUpdate: false
   }
 
   UNSAFE_componentWillMount() {
@@ -78,7 +79,6 @@
 
     sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
 
-    window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
     window.GLOB.TabsMap = new Map()          // 缂撳瓨鐢ㄦ埛鎿嶄綔鐨勬爣绛鹃〉
     window.GLOB.CacheIndependent = new Map()
     window.GLOB.urlFields = []               // url鍙橀噺
@@ -126,22 +126,11 @@
       document.getElementById('mk-pc-design-view').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh; height: 100vh; background: #fff;">鏈簲鐢ㄦ病鏈塒C绔〉闈㈢殑缂栬緫鏉冮檺锛岃鑱旂郴绠$悊鍛橈紒</div>'
       return
     }
-    MKEmitter.addListener('delButtons', this.delButtons)
-    MKEmitter.addListener('modalStatus', this.modalStatus)
-    MKEmitter.addListener('copyButtons', this.copyButtons)
     MKEmitter.addListener('changePopview', this.initPopview)
     MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
     MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
-    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
-    setTimeout(() => {
-      if (sessionStorage.getItem('app_custom_components')) {
-        let list = sessionStorage.getItem('app_custom_components')
-        list = JSON.parse(list)
 
-        this.setCustomComponent(list)
-      } else {
-        this.updateCustomComponent()
-      }
+    setTimeout(() => {
       this.getAppPictures()
       this.getSmStemp()
       this.getRoleFields()
@@ -167,10 +156,23 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
@@ -199,20 +201,14 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('delButtons', this.delButtons)
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
-    MKEmitter.removeListener('copyButtons', this.copyButtons)
     MKEmitter.removeListener('changePopview', this.initPopview)
     MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
     MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
-    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   triggerMenuSave = () => {
+    if (this.state.view === 'popview') return
+
     this.submitConfig()
   }
 
@@ -396,135 +392,146 @@
   }
 
   getAppPictures = () => {
-    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
-
-    Api.getSystemConfig({
+    if (sessionStorage.getItem('app_pictures')) return
+    
+    let deffers = []
+    let param = {
       func: 's_url_db_adduptdel',
       PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
       PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-      typecharone: 'image',
       type: 'search'
-    }).then(res => {
-      if (res.status) {
-        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
-      } else if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
+    }
+    deffers = [new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'image'}).then(res => {
+          resolve(res.data)
         })
-      }
-
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'video',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
-        } else if (!res.status) {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-        }
-      })
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'color',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
-        } else if (!res.status) {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-        }
-      })
-    })
-  }
-
-  updateCustomComponent = () => {
-    Api.getSystemConfig({
-      func: 's_get_custom_components',
-      typename: 'pc',
-      typecharone: ''
-    }).then(res => {
-      if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
+      }, 500)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'video'}).then(res => {
+          resolve(res.data)
         })
-      } else if (res.cus_list) {
-        sessionStorage.setItem('app_custom_components', JSON.stringify(res.cus_list))
-        this.setCustomComponent(res.cus_list)
-      }
+      }, 1000)
+    }), new Promise(resolve => {
+      setTimeout(() => {
+        Api.getSystemConfig({...param, typecharone: 'color'}).then(res => {
+          resolve(res.data)
+        })
+      }, 1500)
+    })]
+
+    Promise.all(deffers).then(response => {
+      sessionStorage.setItem('app_pictures', JSON.stringify(response[0] || []))
+      sessionStorage.setItem('app_videos', JSON.stringify(response[1] || []))
+      sessionStorage.setItem('app_colors', JSON.stringify(response[2] || []))
     })
-  }
-
-  setCustomComponent = (cus_list) => {
-    let coms = []
-
-    cus_list.forEach(item => {
-      let config = ''
-
-      try {
-        config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
-      } catch (e) {
-        console.warn('Parse Failure')
-        config = ''
-      }
-
-      if (!config || !item.c_name) return
-
-      window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
-      coms.push({
-        uuid: item.c_id,
-        type: 'menu',
-        title: item.c_name,
-        url: item.images,
-        component: config.type,
-        subtype: config.subtype,
-        config
-      })
-    })
-    this.setState({customComponents: coms})
-  }
-
-  delButtons = (items) => {
-    this.setState({ delButtons: [...this.state.delButtons, ...items] })
-  }
-
-  copyButtons = (items) => {
-    this.setState({copyButtons: [...this.state.copyButtons, ...items]})
   }
 
   initPopview = (card, btn) => {
-    const { oriConfig, config } = this.state
+    const { config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
-      notification.warning({
-        top: 92,
-        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
-        duration: 5
-      })
+    if (!this.checkBase()) {
       return
     }
 
     let _btn = fromJS(btn).toJS()
-    _btn.MenuName = config.MenuName + '-' + card.name + '-' + btn.label
-    _btn.ParentMenuID = config.uuid
 
-    this.props.history.push('/popdesign/' + window.btoa(window.encodeURIComponent((JSON.stringify(_btn)))))
+    if (_btn.config) {
+      _btn.config.uuid = _btn.uuid
+      _btn.config.MenuID = _btn.uuid
+      _btn.config.ParentId = card.uuid
+      _btn.config.MenuName = _btn.label
+    } else {
+      _btn.config = {
+        uuid: _btn.uuid,
+        MenuID: _btn.uuid,
+        ParentId: card.uuid,
+        enabled: false,
+        MenuName: _btn.label,
+        tables: config.tables || [],
+        Template: 'CustomPage',
+        components: [],
+        viewType: 'popview',
+        style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+      }
+    }
+
+    this.setState({view: 'popview', popConfig: _btn})
+  }
+
+  submitPopConfig = (btnconfig) => {
+    let parents = {[btnconfig.ParentId]: true}
+    let popbtns = {[btnconfig.uuid]: fromJS(btnconfig).toJS()}
+    let config = fromJS(this.state.config).toJS()
+
+    config.components = this.setPopView(config.components, parents, popbtns)
+
+    this.setState({ config }, () => {
+      this.submitConfig()
+    })
+  }
+
+  setPopView = (components, parents, popbtns) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components = this.setPopView(tab.components, parents, popbtns)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.setPopView(item.components, parents, popbtns)
+      } else if (parents[item.uuid]) {
+        this.setpopConfig(item, popbtns)
+      }
+      return item
+    })
+  }
+
+  setpopConfig = (config, popbtns) => {
+    config.subcards && config.subcards.forEach(item => {
+      item.elements.forEach(cell => {
+        if (cell.eleType !== 'button') return
+        if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+          cell.config = popbtns[cell.uuid]
+        }
+      })
+    })
+
+    config.cols && config.cols.forEach(col => {
+      if (col.type === 'action') {
+        col.elements.forEach(cell => {
+          if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+            cell.config = popbtns[cell.uuid]
+          }
+        })
+      }
+    })
+
+    config.elements && config.elements.forEach(cell => {
+      if (cell.eleType !== 'button') return
+      if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+        cell.config = popbtns[cell.uuid]
+      }
+    })
+  
+    config.action && config.action.forEach(cell => {
+      if (cell.OpenType === 'popview' && popbtns[cell.uuid]) {
+        cell.config = popbtns[cell.uuid]
+      }
+    })
+
+    config.$tables = getTables(config)
+  }
+
+  closePop = () => {
+    const {config} = this.state
+
+    sessionStorage.setItem('editMenuType', 'menu')
+
+    window.GLOB.urlFields = config.urlFields || []
+    window.GLOB.customMenu = config
+
+    this.setState({view: '', popConfig: null})
   }
 
   closeView = () => {
@@ -588,7 +595,6 @@
         this.getCopyParam(urlParam)
       } else {
         let config = null
-        let isCreate = false
 
         try {
           config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
@@ -598,9 +604,8 @@
         }
 
         if (!config) {
-          isCreate = true
           config = {
-            version: 1.0,
+            version: 2.0,
             uuid: MenuId,
             MenuID: MenuId,
             Template: 'webPage',
@@ -621,27 +626,66 @@
         config.open_edition = result.open_edition || ''
         window.GLOB.urlFields = config.urlFields || []
 
-        let indeComs = []
+        if (config.version !== 2.0) {
+          config.components = this.collectTB(config.components)
+          config.version = 2.0
+
+          this.setState({
+            needUpdate: true
+          })
+        }
+
+        let navItem = null
         config.components.forEach(item => {
           if (item.type === 'navbar') {
-            indeComs.push(fromJS(item).toJS())
+            navItem = fromJS(item).toJS()
           }
         })
 
-        if (indeComs.length === 0) {
+        if (!navItem) {
           this.setState({
-            oriConfig: isCreate ? null : config,
-            config: fromJS(config).toJS(),
+            oriConfig: fromJS(config).toJS(),
+            config: config,
             loading: false
           })
 
           window.GLOB.customMenu = config
         } else {
-          this.jointComponents(config, indeComs, isCreate)
+          this.jointComponents(config, navItem)
         }
       }
     })
     this.getAppMenus()
+  }
+
+  collectTB = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          delete tab.floor
+          delete tab.hasSearch
+          delete tab.parentId
+
+          tab.components = this.collectTB(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.collectTB(item.components)
+      } else if (!['search', 'navbar', 'login', 'topbar', 'officialAccount'].includes(item.type)) {
+        item.$tables = getTables(item)
+      }
+
+      if (item.subtype === 'tablecard') { // 鍏煎
+        item.type = 'card'
+      }
+
+      delete item.tabId
+      delete item.parentId
+      delete item.btnlog
+      delete item.floor
+      delete item.dataName
+
+      return item
+    })
   }
 
   getAppMenus = () => {
@@ -699,12 +743,6 @@
         })
         this.setState({loading: false})
         return
-      } else if (!result.LongParam) {
-        notification.warning({
-          top: 92,
-          message: '鏈煡璇㈠埌澶嶅埗鑿滃崟閰嶇疆淇℃伅锛�',
-          duration: 5
-        })
       }
 
       let config = null
@@ -715,11 +753,10 @@
         console.warn('Parse Failure')
         config = null
       }
-
       
       if (!config) {
         config = {
-          version: 1.0,
+          version: 2.0,
           uuid: MenuId,
           MenuID: MenuId,
           Template: 'webPage',
@@ -743,6 +780,16 @@
           })
         }
         config.components = MenuUtils.resetConfig(config.components, uuids, urlParam.clearMenu)
+
+        if (config.version !== 2.0) {
+          config.components = this.collectTB(config.components)
+          config.version = 2.0
+
+          this.setState({
+            needUpdate: true
+          })
+        }
+
         config.enabled = false
         message.success('澶嶅埗鎴愬姛锛屼繚瀛樺悗鐢熸晥銆�')
       }
@@ -753,52 +800,42 @@
       config.MenuName = urlParam.MenuName || ''
       config.MenuNo = ''
 
-      let indeComs = []
+      let navItem = null
       config.components.forEach(item => {
         if (item.type === 'navbar') {
-          indeComs.push(fromJS(item).toJS())
+          navItem = fromJS(item).toJS()
         }
       })
 
-      if (indeComs.length === 0) {
+      if (!navItem) {
         this.setState({
-          oriConfig: null,
-          config: fromJS(config).toJS(),
+          oriConfig: fromJS(config).toJS(),
+          config: config,
           loading: false
         })
         window.GLOB.customMenu = config
       } else {
-        this.jointComponents(config, indeComs, true)
+        this.jointComponents(config, navItem)
       }
     })
   }
 
-  jointComponents = (config, indeComs, isCreate) => {
-    let deffers = indeComs.map(item => {
-      return new Promise(resolve => {
-        Api.getSystemConfig({
-          func: 'sPC_Get_LongParam',
-          TypeCharOne: sessionStorage.getItem('kei_no'),
-          typename: 'pc',
-          MenuID: item.uuid
-        }).then(res => {
-          res.uuid = item.uuid
-
-          if (!res.status) {
-            notification.warning({
-              top: 92,
-              message: res.message,
-              duration: 5
-            })
-          }
-          
-          resolve(res)
+  jointComponents = (config, navItem) => {
+    Api.getSystemConfig({
+      func: 'sPC_Get_LongParam',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'pc',
+      MenuID: navItem.uuid
+    }).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
         })
-      })
-    })
-    Promise.all(deffers).then(result => {
-      let _conf = {}
-      result.forEach(res => {
+
+        config.components = config.components.filter(item => item.type !== 'navbar')
+      } else {
         let _config = null
         try {
           _config = res.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(res.LongParam))) : null
@@ -809,35 +846,21 @@
 
         if (_config) {
           _config.open_edition = res.open_edition || ''
-          _conf[res.uuid] = _config
-          window.GLOB.CacheIndependent.set(res.uuid, fromJS(_config).toJS())
+          config.components = config.components.map(item => {
+            if (item.type === 'navbar') {
+              return _config
+            }
+            return item
+          })
+
+          window.GLOB.CacheIndependent.set(navItem.uuid, fromJS(_config).toJS())
+        } else {
+          config.components = config.components.filter(item => item.type !== 'navbar')
         }
-      })
-
-      let _length = config.components.length
-      config.components = config.components.map(item => {
-        if (item.type === 'navbar') {
-          if (_conf[item.uuid]) {
-            item = _conf[item.uuid]
-          } else {
-            item = null
-          }
-        }
-        return item
-      })
-
-      config.components = config.components.filter(Boolean)
-
-      if (_length > config.components.length) {
-        notification.warning({
-          top: 92,
-          message: '閮ㄥ垎缁勪欢宸插垹闄わ紒',
-          duration: 5
-        })
       }
 
       this.setState({
-        oriConfig: isCreate ? null : fromJS(config).toJS(),
+        oriConfig: fromJS(config).toJS(),
         config: config,
         loading: false
       })
@@ -849,7 +872,6 @@
   getMenuMessage = (tbs) => {
     const { config } = this.state
     let nodes = {type: 'view', version: '1.0', key: config.uuid, title: config.MenuName, children: []}
-    let popviews = []
 
     let traversal = (components) => {
       let list = []
@@ -858,14 +880,21 @@
           tbs.push(...item.$tables)
         }
 
+        if (item.plot && item.plot.permission !== 'true') {
+          return
+        } else if (item.type === 'login' || item.type === 'navbar') {
+          return
+        } else if (!['tabs', 'group'].includes(item.type) && item.wrap && item.wrap.permission !== 'true') {
+          return
+        }
+
         let m = {
           key: item.uuid,
           title: item.name,
           children: []
         }
-        if (item.type === 'login' || item.type === 'navbar') {
-          return null
-        } else if (item.type === 'tabs') {
+
+        if (item.type === 'tabs') {
           let tabs = []
           let mm = []
           item.subtabs.forEach(tab => {
@@ -892,7 +921,7 @@
             list.push(...mm)
           }
           
-          return null
+          return
         } else if (item.type === 'group') {
           m.children = traversal(item.components)
 
@@ -902,12 +931,8 @@
             list.push(...m.children)
           }
           
-          return null
-        } if (item.plot && item.plot.permission !== 'true') {
-          return null
-        } if (item.wrap && item.wrap.permission !== 'true') {
-          return null
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+          return
+        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
           item.action && item.action.forEach(btn => {
             if (btn.hidden === 'true') return
 
@@ -915,74 +940,34 @@
               key: btn.uuid,
               title: btn.label,
             })
-            if (btn.OpenType === 'popview') {
-              popviews.push(btn.uuid)
-            }
           })
           item.subcards.forEach(card => {
             card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') return
+              if (cell.eleType !== 'button' || cell.hidden === 'true') return
 
               m.children.push({
                 key: cell.uuid,
                 title: cell.label,
               })
-              if (cell.OpenType === 'popview') {
-                popviews.push(cell.uuid)
-              }
             })
+
             card.backElements && card.backElements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') return
+              if (cell.eleType !== 'button' || cell.hidden === 'true') return
 
               m.children.push({
                 key: cell.uuid,
                 title: cell.label,
               })
-              if (cell.OpenType === 'popview') {
-                popviews.push(cell.uuid)
-              }
-            })
-          })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') return
-
-              m.children.push({
-                key: cell.uuid,
-                title: cell.label,
-              })
-              if (cell.OpenType === 'popview') {
-                popviews.push(cell.uuid)
-              }
             })
           })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
-            if (cell.eleType !== 'button') return
-            if (cell.hidden === 'true') return
+            if (cell.eleType !== 'button' || cell.hidden === 'true') return
 
             m.children.push({
               key: cell.uuid,
               title: cell.label,
             })
-            if (cell.OpenType === 'popview') {
-              popviews.push(cell.uuid)
-            }
-          })
-        } else if (item.type === 'menubar') {
-          if (item.wrap.title) {
-            m.title = item.wrap.title
-          }
-
-          m.children = item.subMenus.map(menu => {
-            return {
-              key: menu.uuid,
-              title: menu.setting.name
-            }
           })
         } else if (item.type === 'form') {
           if (item.subtype !== 'simpleform') {
@@ -993,19 +978,16 @@
               }
             })
           }
-        } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
-          item.action && item.action.forEach(btn => {
+        } else if (item.type === 'table') {
+          item.action.forEach(btn => {
             if (btn.hidden === 'true') return
 
             m.children.push({
               key: btn.uuid,
               title: btn.label,
             })
-            if (btn.OpenType === 'popview') {
-              popviews.push(btn.uuid)
-            }
           })
-          item.cols && item.cols.forEach(col => {
+          item.cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements.forEach(btn => {
               if (btn.hidden === 'true') return
@@ -1014,9 +996,6 @@
                 key: btn.uuid,
                 title: btn.label,
               })
-              if (btn.OpenType === 'popview') {
-                popviews.push(btn.uuid)
-              }
             })
           })
         }
@@ -1030,7 +1009,6 @@
     let trees = traversal(config.components)
 
     nodes.children = trees
-    nodes.popviews = popviews
 
     if (config.components.findIndex(item => item.type === 'login') > -1) {
       nodes.login = true
@@ -1038,6 +1016,14 @@
     }
     if (nodes.children.length === 0) {
       nodes.pass = true
+    }
+
+    if (config.interfaces) {
+      config.interfaces.forEach(item => {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
+        }
+      })
     }
 
     return nodes
@@ -1065,7 +1051,7 @@
           })
         } else if (item.type === 'group') {
           traversal(item.components)
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+        } else if (item.type === 'card' || item.type === 'carousel' || item.type === 'timeline') {
           item.action && item.action.forEach(btn => {
             if (btn.linkmenu && menuObj[btn.linkmenu]) {
               menus.push(menuObj[btn.linkmenu])
@@ -1093,21 +1079,6 @@
               }
             })
           })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            if (card.setting.click === 'menu' && menuObj[card.setting.menu]) {
-              menus.push(menuObj[card.setting.menu])
-            }
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-
-              if (cell.linkmenu && menuObj[cell.linkmenu]) {
-                menus.push(menuObj[cell.linkmenu])
-              } else if (cell.openmenu && menuObj[cell.openmenu]) {
-                menus.push(menuObj[cell.openmenu])
-              }
-            })
-          })
         } else if (item.type === 'balcony') {
           item.elements && item.elements.forEach(cell => {
             if (cell.eleType !== 'button') return
@@ -1124,7 +1095,7 @@
               menus.push(menuObj[m.subButton.linkmenu])
             }
           })
-        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        } else if (item.type === 'table') {
           item.action && item.action.forEach(btn => {
             if (btn.linkmenu && menuObj[btn.linkmenu]) {
               menus.push(menuObj[btn.linkmenu])
@@ -1158,9 +1129,8 @@
     return menus
   }
 
-  submitConfig = () => {
-    const { delButtons, copyButtons } = this.state
-    let config = fromJS(this.state.config).toJS()
+  checkBase = () => {
+    const { config } = this.state
 
     if (!config.MenuName || !config.MenuNo || (config.cacheUseful === 'true' && !config.cacheTime)) {
       notification.warning({
@@ -1173,6 +1143,15 @@
         settingshow: true,
         activeKey: 'basedata'
       })
+      return false
+    }
+    return true
+  }
+
+  submitConfig = () => {
+    let config = fromJS(this.state.config).toJS()
+
+    if (!this.checkBase()) {
       return
     }
 
@@ -1256,201 +1235,159 @@
       param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
       param.secretkey = Utils.encrypt('', param.timestamp)
 
-      new Promise(resolve => {
-        let _config = fromJS(config).toJS()
-        let indeComs = []
-        _config.components = _config.components.map(item => {
-          if (item.type === 'navbar') {
-            indeComs.push(item)
-            return {
-              type: 'navbar',
-              uuid: item.uuid
-            }
+      let _config = fromJS(config).toJS()
+      let NavBar = null
+      _config.components = _config.components.map(item => {
+        if (item.type === 'navbar') {
+          NavBar = fromJS(item).toJS()
+          return {
+            type: 'navbar',
+            uuid: item.uuid
           }
-          return item
-        })
+        }
+        return item
+      })
 
-        param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+      param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
 
-        if (indeComs.length === 0) {
-          resolve(true)
+      new Promise(resolve => {
+        if (!NavBar) {
+          resolve({status: true})
         } else {
-          let new_open_edition = {}
-          let deffers = indeComs.map(item => {
-            return new Promise(resolve => {
-              let _item = window.GLOB.CacheIndependent.get(item.uuid)
-              if (_item && is(fromJS(_item), fromJS(item))) {
-                new_open_edition[item.uuid] = item.open_edition || ''
-                resolve()
-                return
-              }
+          let _item = window.GLOB.CacheIndependent.get(NavBar.uuid) || {}
+          NavBar.open_edition = _item.open_edition || ''
+          if (is(fromJS(_item), fromJS(NavBar))) {
+            resolve({status: true})
+          } else {
+            let roles = {
+              type: 'navbar',
+              version: '1.0',
+              key: NavBar.uuid,
+              title: NavBar.name,
+              children: []
+            }
 
-              let roles = {
-                type: 'navbar',
-                version: '1.0',
-                key: item.uuid,
-                title: item.name,
-                children: []
-              }
-
-              if (item.wrap.permission === 'true') {
-                roles.children = item.menus.map(fst => {
-                  if (fst.property === 'classify' && fst.sublist.length > 0) {
-                    return {
-                      key: fst.MenuID,
-                      title: fst.name,
-                      children: fst.sublist.map(scd => {
-                        if (scd.property === 'classify' && scd.sublist.length > 0) {
-                          return {
-                            key: scd.MenuID,
-                            title: scd.name,
-                            children: scd.sublist.map(thd => {
-                              return { key: thd.MenuID, title: thd.name }
-                            })
-                          }
-                        } else {
-                          return { key: scd.MenuID, title: scd.name }
+            if (NavBar.wrap.permission === 'true') {
+              roles.children = NavBar.menus.map(fst => {
+                if (fst.property === 'classify' && fst.sublist.length > 0) {
+                  return {
+                    key: fst.MenuID,
+                    title: fst.name,
+                    children: fst.sublist.map(scd => {
+                      if (scd.property === 'classify' && scd.sublist.length > 0) {
+                        return {
+                          key: scd.MenuID,
+                          title: scd.name,
+                          children: scd.sublist.map(thd => {
+                            return { key: thd.MenuID, title: thd.name }
+                          })
                         }
-                      })
-                    }
-                  } else {
-                    return { key: fst.MenuID, title: fst.name }
+                      } else {
+                        return { key: scd.MenuID, title: scd.name }
+                      }
+                    })
                   }
-                })
-              } else {
-                roles.pass = true
-              }
-
-              let _param = {
-                func: 'sPC_TrdMenu_AddUpt',
-                FstID: 'mk_app',
-                SndID: 'mk_app',
-                ParentID: 'mk_app',
-                MenuID: item.uuid,
-                MenuNo: item.wrap.MenuNo || Utils.getuuid(),
-                EasyCode: '',
-                Template: item.type,
-                TypeCharOne: sessionStorage.getItem('kei_no'),
-                Typename: 'pc',
-                MenuName: item.name || '',
-                PageParam: JSON.stringify({Template: item.type}),
-                menus_rolelist: window.btoa(window.encodeURIComponent(JSON.stringify(roles))),
-                open_edition: item.open_edition || '',
-                LText: '',
-                LTexttb: ''
-              }
-
-              _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(item)))
-              _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              _param.secretkey = Utils.encrypt('', _param.timestamp)
-
-              Api.getSystemConfig(_param).then(res => {
-                if (!res.status) {
-                  notification.warning({
-                    top: 92,
-                    message: res.message,
-                    duration: 5
-                  })
-                  this.setState({ menuloading: false })
-                  return
-                }
-
-                new_open_edition[item.uuid] = res.open_edition || ''
-                resolve()
-              })
-            })
-          })
-          Promise.all(deffers).then(() => {
-            let appViewList = sessionStorage.getItem('appViewList')
-            appViewList = JSON.parse(appViewList)
-            let _appViewList = fromJS(appViewList).toJS()
-            let appIndeList = appViewList.map(item => item.keys_id).join(',')
-
-            config.components = config.components.map(item => {
-              if (item.type === 'navbar') {
-                item.open_edition = new_open_edition[item.uuid] || ''
-                window.GLOB.CacheIndependent.set(item.uuid, fromJS(item).toJS())
-
-                if (appIndeList.indexOf(item.uuid) === -1) {
-                  appViewList.unshift({
-                    appkey: window.GLOB.appkey || '',
-                    bid: sessionStorage.getItem('appId') || '',
-                    kei_no: sessionStorage.getItem('kei_no') || '',
-                    keys_id: item.uuid,
-                    keys_type: 'navbar',
-                    remark: item.name
-                  })
                 } else {
-                  appViewList = appViewList.map(view => {
-                    if (view.keys_id === item.uuid) {
-                      view.remark = item.name
-                    }
-                    return view
-                  })
-                }
-              }
-              return item
-            })
-
-            if (!is(fromJS(appViewList), fromJS(_appViewList))) {
-              let kparam = {
-                func: 's_kei_link_keyids_addupt',
-                BID: sessionStorage.getItem('appId'),
-                exec_type: 'y',
-                LText: ''
-              }
-    
-              kparam.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
-              kparam.LText = kparam.LText.join(' union all ')
-              kparam.LText = Utils.formatOptions(kparam.LText)
-        
-              kparam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-              kparam.secretkey = Utils.encrypt('', kparam.timestamp)
-    
-              Api.getSystemConfig(kparam).then(result => {
-                if (!result.status) {
-                  notification.warning({
-                    top: 92,
-                    message: result.message,
-                    duration: 5
-                  })
-                  this.setState({ menuloading: false })
-                } else {
-                  sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
-                  resolve(true)
+                  return { key: fst.MenuID, title: fst.name }
                 }
               })
             } else {
-              resolve(true)
+              roles.pass = true
             }
-          })
-        }
-      }).then(res => { // 鎸夐挳鎴栬彍鍗曞垹闄�
-        if (!res) return
 
-        if (delButtons.length === 0) {
-          return { status: true, nonexec: true }
-        } else {
-          let _param = {
-            func: 'sPC_MainMenu_Del',
-            MenuID: delButtons.join(',')
+            let _param = {
+              func: 'sPC_TrdMenu_AddUpt',
+              FstID: 'mk_app',
+              SndID: 'mk_app',
+              ParentID: 'mk_app',
+              MenuID: NavBar.uuid,
+              MenuNo: NavBar.wrap.MenuNo || Utils.getuuid(),
+              EasyCode: '',
+              Template: NavBar.type,
+              TypeCharOne: sessionStorage.getItem('kei_no'),
+              Typename: 'pc',
+              MenuName: NavBar.name || '',
+              PageParam: JSON.stringify({Template: NavBar.type}),
+              menus_rolelist: window.btoa(window.encodeURIComponent(JSON.stringify(roles))),
+              open_edition: NavBar.open_edition,
+              LText: '',
+              LTexttb: ''
+            }
+
+            _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(NavBar)))
+            _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+            _param.secretkey = Utils.encrypt('', _param.timestamp)
+
+            Api.getSystemConfig(_param).then(res => {
+              if (res.status) {
+                NavBar.open_edition = res.open_edition || ''
+                window.GLOB.CacheIndependent.set(NavBar.uuid, fromJS(NavBar).toJS())
+
+                let appViewList = sessionStorage.getItem('appViewList')
+                let _appViewList = JSON.parse(appViewList)
+
+                let index = _appViewList.findIndex(item => item.keys_id === NavBar.uuid)
+                
+                if (index === -1) {
+                  _appViewList.unshift({
+                    appkey: window.GLOB.appkey || '',
+                    bid: sessionStorage.getItem('appId') || '',
+                    kei_no: sessionStorage.getItem('kei_no') || '',
+                    keys_id: NavBar.uuid,
+                    keys_type: 'navbar',
+                    remark: NavBar.name
+                  })
+                } else {
+                  _appViewList = _appViewList.map(item => {
+                    if (item.keys_id === NavBar.uuid && item.remark !== NavBar.name) {
+                      item.remark = NavBar.name
+                    }
+                    return item
+                  })
+                }
+
+                let viewList = JSON.stringify(_appViewList)
+
+                if (appViewList !== viewList) {
+                  let kparam = {
+                    func: 's_kei_link_keyids_addupt',
+                    BID: sessionStorage.getItem('appId'),
+                    exec_type: 'y',
+                    LText: ''
+                  }
+        
+                  kparam.LText = _appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+                  kparam.LText = kparam.LText.join(' union all ')
+                  kparam.LText = Utils.formatOptions(kparam.LText)
+            
+                  kparam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+                  kparam.secretkey = Utils.encrypt('', kparam.timestamp)
+        
+                  Api.getSystemConfig(kparam).then(result => {
+                    if (result.status) {
+                      sessionStorage.setItem('appViewList', viewList)
+                    }
+
+                    resolve(result)
+                  })
+                } else {
+                  resolve(res)
+                }
+              } else {
+                resolve(res)
+              }
+            })
           }
-          return Api.getSystemConfig(_param)
         }
       }).then(res => { // 椤甸潰淇濆瓨
-        if (!res) return
+        if (!res || !res.status) return res
 
-        if (res.status) {
-          return Api.getSystemConfig(param)
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
+        return Api.getSystemConfig(param)
+      }).then(res => {
+        this.setState({
+          menuloading: false
+        })
+
         if (!res) return
 
         if (res.status) {
@@ -1459,135 +1396,24 @@
           this.setState({
             config,
             oriConfig: fromJS(config).toJS(),
+            needUpdate: false
           })
 
-          return {
-            status: true
-          }
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => { // 鎸夐挳澶嶅埗
-        if (!res) return
-        if (!res.status) {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
+          localStorage.setItem('pcUpdate', new Date().getTime() + ',' + config.uuid)
 
-        if (copyButtons.length === 0) {
-          return {
-            status: true
-          }
-        } else {
-          return new Promise(resolve => {
-            let deffers = copyButtons.map(item => {
-              return new Promise(resolve => {
-                if (delButtons.includes(item.uuid)) { // 澶嶅埗鐨勬寜閽凡鍒犻櫎
-                  resolve({
-                    status: true
-                  })
-                  return
-                }
-
-                Api.getSystemConfig({
-                  func: 'sPC_Get_LongParam',
-                  MenuID: item.$originUuid,
-                  TypeCharOne: sessionStorage.getItem('kei_no'),
-                  typename: 'pc',
-                }).then(result => {
-                  if (result.status) {
-                    let _conf = ''
-              
-                    try {
-                      _conf = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : ''
-                    } catch (e) {
-                      console.warn('Parse Failure')
-                      _conf = ''
-                    }
-                    
-                    if (_conf) {
-                      _conf.components = MenuUtils.resetConfig(_conf.components)
-                      _conf.uuid = item.uuid
-                      _conf.MenuID = item.uuid
-                      _conf.Template = 'webPage'
-                      _conf.enabled = false
-                    } else {
-                      resolve({
-                        status: true
-                      })
-                      return
-                    }
-
-                    let _param = {
-                      func: 'sPC_ButtonParam_AddUpt',
-                      ParentID: config.uuid,
-                      MenuID: item.uuid,
-                      MenuNo: '',
-                      Template: 'webPage',
-                      MenuName: item.label,
-                      PageParam: JSON.stringify({Template: 'webPage'}),
-                      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_conf))),
-                      TypeCharOne: sessionStorage.getItem('kei_no'),
-                      Typename: 'pc',
-                    }
-            
-                    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) {
-                notification.warning({
-                  top: 92,
-                  message: error.message,
-                  duration: 5
-                })
-                resolve(false)
-              } else {
-                resolve({
-                  status: true
-                })
-              }
-            })
-          })
-        }
-      }).then(res => {
-        if (res && res.status) {
-          this.setState({
-            delButtons: [],
-            copyButtons: [],
-            menuloading: false
-          })
           notification.success({
             top: 92,
             message: '淇濆瓨鎴愬姛',
             duration: 2
           })
+          MKEmitter.emit('completeSave')
         } else {
-          this.setState({
-            menuloading: false
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
           })
         }
-        MKEmitter.emit('completeSave')
       })
     }, 300 + (+sessionStorage.getItem('mkDelay')))
   }
@@ -1713,9 +1539,7 @@
   }
 
   refreshView = () => {
-    const { oriConfig, config } = this.state
-
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(this.state.oriConfig || {}), fromJS(this.state.config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1733,9 +1557,9 @@
   }
 
   setHomeView = () => {
-    const { oriConfig, config } = this.state
+    const { config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(this.state.oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1792,9 +1616,9 @@
   }
 
   setLoginView = () => {
-    const { oriConfig, config } = this.state
+    const { config } = this.state
 
-    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+    if (!is(fromJS(this.state.oriConfig || {}), fromJS(config || {}))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
@@ -1853,13 +1677,13 @@
   }
 
   render () {
-    const { loading, comloading, activeKey, settingshow, controlshow, MenuId, config, menuloading, customComponents, eyeopen } = this.state
+    const { view, loading, comloading, activeKey, settingshow, controlshow, MenuId, config, menuloading, eyeopen, needUpdate } = this.state
 
     return (
       <ConfigProvider locale={antdZhCN}>
-        <div className={'mk-pc-view '} id="mk-pc-design-view">
-          {loading ? <Spin className="view-spin" size="large" /> : null}
-          <DndProvider backend={HTML5Backend}>
+        <DndProvider backend={HTML5Backend}>
+          {view !== 'popview' ?<div className={'mk-pc-view '} id="mk-pc-design-view">
+            {loading ? <Spin className="view-spin" size="large" /> : null}
             <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
               <div className="draw">
                 {settingshow ? <DoubleLeftOutlined onClick={() => {sessionStorage.setItem('settingshow', 'false'); this.setState({settingshow: false})}}/> : null}
@@ -1887,9 +1711,6 @@
                   <Panel header="鍏冪礌" key="element">
                     <Modulecell />
                   </Panel>
-                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
-                    <SourceWrap components={customComponents} />
-                  </Panel> : null}
                   <Panel header={'椤甸潰鏍峰紡'} key="background">
                     {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                   </Panel>
@@ -1902,7 +1723,7 @@
                 {!controlshow ? <DoubleLeftOutlined onClick={() => {sessionStorage.setItem('controlshow', 'true'); this.setState({controlshow: true})}}/> : null}
               </div>
               <div className="wrap">
-                <Button type="primary" id="save-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
+                <Button type="primary" className={needUpdate ? 'update-tip' : ''} id="save-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
                 <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config && config.enabled} onChange={this.onEnabledChange} />
                 <ArrowLeftOutlined title="鍚庨��" className="back-view" onClick={this.backView}/>
                 <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 缁勪欢鍚�</Button>
@@ -1926,11 +1747,14 @@
             <div className={'menu-body menu-view' + (menuloading ? ' saving' : '') + (eyeopen ? ' eye-open' : '')}>
               {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
             </div>
-          </DndProvider>
-          <StyleController />
-          <StyleCombController />
-          <ModalController />
-        </div>
+          </div> : <>
+            <Header/>
+            <PopView btn={this.state.popConfig} save={this.submitPopConfig} cancel={this.closePop}/>
+          </>}
+        </DndProvider>
+        <StyleController />
+        <StyleCombController />
+        <ModalController />
       </ConfigProvider>
     )
   }
diff --git a/src/views/pcdesign/index.scss b/src/views/pcdesign/index.scss
index 881e06c..d5f059d 100644
--- a/src/views/pcdesign/index.scss
+++ b/src/views/pcdesign/index.scss
@@ -13,6 +13,17 @@
       display: none;
     }
   }
+  .update-tip::after {
+    content: ' ';
+    display: inline-block;
+    position: absolute;
+    top: -4px;
+    right: -4px;
+    width: 8px;
+    height: 8px;
+    background-color: red;
+    border-radius: 8px;
+  }
   .component-name {
     position: absolute;
     z-index: 9;
@@ -251,4 +262,7 @@
 
 body {
   overflow-y: hidden;
+}
+.user-component-wrap {
+  display: none!important;
 }
\ No newline at end of file
diff --git a/src/views/pcdesign/menuform/index.jsx b/src/views/pcdesign/menuform/index.jsx
index 8dd5777..bbad766 100644
--- a/src/views/pcdesign/menuform/index.jsx
+++ b/src/views/pcdesign/menuform/index.jsx
@@ -24,6 +24,8 @@
       this.props.updateConfig({...config, cacheUseful: value})
     } else if (key === 'timeUnit') {
       this.props.updateConfig({...config, timeUnit: value})
+    } else if (key === 'mask') {
+      this.props.updateConfig({...config, mask: value})
     // } else if (key === 'permission') {
     //   this.props.updateConfig({...config, permission: value})
     }
@@ -144,6 +146,23 @@
               )}
             </Form.Item>
           </Col>
+          <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鏁版嵁鍔犺浇鏃剁殑閬僵鍜岀┖鏁版嵁鍥炬爣鏄惁鏄剧ず銆�">
+                <QuestionCircleOutlined className="mk-form-tip" />
+                鏁版嵁鎻愮ず
+              </Tooltip>
+            }>
+              {getFieldDecorator('mask', {
+                initialValue: config.mask || 'true'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('mask', e.target.value)}}>
+                  <Radio value="true">鏄剧ず</Radio>
+                  <Radio value="false">闅愯棌</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
           {config.cacheUseful === 'true' ? <Col span={24}>
             <Form.Item label="鍗曚綅">
               {getFieldDecorator('timeUnit', {
diff --git a/src/views/popdesign/index.jsx b/src/views/popdesign/index.jsx
deleted file mode 100644
index ee36fc4..0000000
--- a/src/views/popdesign/index.jsx
+++ /dev/null
@@ -1,730 +0,0 @@
-import React, { Component } from 'react'
-import { DndProvider } from 'react-dnd'
-import { is, fromJS } from 'immutable'
-import moment from 'moment'
-import HTML5Backend from 'react-dnd-html5-backend'
-import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button } from 'antd'
-import { EyeOutlined, EyeInvisibleOutlined } from '@ant-design/icons'
-
-import Api from '@/api'
-import Utils, { setGLOBFuncs } from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import asyncComponent from '@/utils/asyncComponent'
-// import antdEnUS from 'antd/es/locale/en_US'
-import antdZhCN from 'antd/es/locale/zh_CN'
-
-import '@/assets/css/design.scss'
-import './index.scss'
-
-const _locale = antdZhCN
-const { Panel } = Collapse
-const { confirm } = Modal
-
-const MenuForm = asyncComponent(() => import('./menuform'))
-const Versions = asyncComponent(() => import('@/menu/versions'))
-const Header = asyncComponent(() => import('@/menu/header'))
-const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
-const Modulecell = asyncComponent(() => import('@/menu/modulecell'))
-const MenuShell = asyncComponent(() => import('@/menu/menushell'))
-const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
-const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
-const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
-const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
-const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
-
-const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
-const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
-const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
-
-sessionStorage.setItem('isEditState', 'true')
-
-document.body.className = ''
-
-class PopViewDesign extends Component {
-  state = {
-    btn: null,
-    MenuId: '',
-    delButtons: [],
-    activeKey: 'basedata',
-    menuloading: false,
-    oriConfig: null,
-    config: null,
-    customComponents: [],
-    comloading: false,
-    eyeopen: false
-  }
-
-  UNSAFE_componentWillMount() {
-    sessionStorage.setItem('editMenuType', 'popview')
-
-    window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
-    window.GLOB.TabsMap = new Map()          // 缂撳瓨鐢ㄦ埛鎿嶄綔鐨勬爣绛鹃〉
-    window.GLOB.urlFields = []               // url鍙橀噺
-    window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
-
-    try {
-      let param = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
-
-      this.setState({
-        btn: param,
-        MenuId: param.uuid,
-      }, () => {
-        this.getMenuParam()
-      })
-    } catch (e) {
-      notification.warning({
-        top: 92,
-        message: '鑿滃崟淇℃伅瑙f瀽閿欒锛�',
-        duration: 5
-      })
-    }
-  }
-
-  componentDidMount () {
-    MKEmitter.addListener('delButtons', this.delButtons)
-    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
-    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
-    setTimeout(() => {
-      if (sessionStorage.getItem('app_custom_components')) {
-        let list = sessionStorage.getItem('app_custom_components')
-        list = JSON.parse(list)
-
-        this.setCustomComponent(list)
-      } else {
-        this.updateCustomComponent()
-      }
-      setGLOBFuncs()
-    }, 1000)
-
-    document.onkeydown = (event) => {
-      let e = event || window.event
-      let keyCode = e.keyCode || e.which || e.charCode
-      let preKey = ''
-
-      if (e.ctrlKey) {
-        preKey = 'ctrl'
-      }
-      if (e.shiftKey) {
-        preKey = 'shift'
-      } else if (e.altKey) {
-        preKey = 'alt'
-      }
-      
-      if (!preKey || !keyCode) return
-      
-      let _shortcut = `${preKey}+${keyCode}`
-
-      if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
-          notification.warning({
-            top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
-            duration: 5
-          })
-          return false
-        }
-
-        let node = document.getElementById('save-modal-config')
-        if (!node) {
-          node = document.getElementById('save-pop-config')
-        }
-
-        if (node) {
-          node.click()
-        }
-        return false
-      }
-    }
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  /**
-   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
-   */
-  componentWillUnmount () {
-    this.setState = () => {
-      return
-    }
-    MKEmitter.removeListener('delButtons', this.delButtons)
-    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
-    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
-  }
-
-  updateCustomComponent = () => {
-    Api.getSystemConfig({
-      func: 's_get_custom_components',
-      typename: sessionStorage.getItem('appType'),
-      typecharone: ''
-    }).then(res => {
-      if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
-        })
-      } else if (res.cus_list) {
-        sessionStorage.setItem('app_custom_components', JSON.stringify(res.cus_list))
-        this.setCustomComponent(res.cus_list)
-      }
-    })
-  }
-
-  setCustomComponent = (cus_list) => {
-    let coms = []
-
-    cus_list.forEach(item => {
-      let config = ''
-
-      try {
-        config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
-      } catch (e) {
-        console.warn('Parse Failure')
-        config = ''
-      }
-
-      if (!config || !item.c_name) return
-
-      window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
-      coms.push({
-        uuid: item.c_id,
-        type: 'menu',
-        title: item.c_name,
-        url: item.images,
-        component: config.type,
-        subtype: config.subtype,
-        config
-      })
-    })
-    this.setState({customComponents: coms})
-  }
-
-  delButtons = (items) => {
-    this.setState({delButtons: [...this.state.delButtons, ...items]})
-  }
-
-  closeView = () => {
-    const { oriConfig, config } = this.state
-
-    if (!config) {
-      window.history.back()
-      return
-    }
-
-    if (!is(fromJS(oriConfig), fromJS(config))) {
-      confirm({
-        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
-        content: '',
-        onOk() {
-          window.history.back()
-        },
-        onCancel() {}
-      })
-    } else {
-      window.history.back()
-    }
-  }
-
-  getMenuParam = () => {
-    const { MenuId, btn } = this.state
-
-    let param = {
-      func: 'sPC_Get_LongParam',
-      MenuID: MenuId
-    }
-
-    if (sessionStorage.getItem('appType') === 'pc') {
-      param.TypeCharOne = sessionStorage.getItem('kei_no')
-      param.Typename = 'pc'
-    }
-
-    Api.getSystemConfig(param).then(result => {
-      if (result.status) {
-        let config = null
-
-        try {
-          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
-        } catch (e) {
-          console.warn('Parse Failure')
-          config = null
-        }
-
-        if (!config) {
-          config = {
-            version: 1.0,
-            uuid: MenuId,
-            MenuID: MenuId,
-            Template: 'CustomPage',
-            enabled: false,
-            MenuName: btn.MenuName,
-            MenuNo: '',
-            tables: [],
-            components: [],
-            viewType: 'popview',
-            style: {
-              backgroundColor: '#ffffff', backgroundImage: '',
-              paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'
-            },
-          }
-        } else {
-          config.uuid = MenuId
-          config.MenuID = MenuId
-          config.Template = 'CustomPage'
-        }
-
-        config.open_edition = result.open_edition || ''
-
-        this.setState({
-          oriConfig: config,
-          config: fromJS(config).toJS()
-        })
-
-        window.GLOB.customMenu = config
-      } else {
-        notification.warning({
-          top: 92,
-          message: result.message,
-          duration: 5
-        })
-      }
-    })
-  }
-
-  getMenuMessage = (delButtons) => {
-    const { config } = this.state
-    let buttons = []
-    let _sort = 1
-
-    let traversal = (components) => {
-      components.forEach(item => {
-        if (item.type === 'tabs') {
-          item.subtabs.forEach(tab => {
-            traversal(tab.components)
-          })
-        } else if (item.type === 'group') {
-          traversal(item.components)
-        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-          item.subcards && item.subcards.forEach(card => {
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
-            })
-            card.backElements && card.backElements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
-            })
-          })
-        } else if (item.type === 'carousel' || item.type === 'timeline') {
-          item.subcards.forEach(card => {
-            card.elements && card.elements.forEach(cell => {
-              if (cell.eleType !== 'button') return
-              if (cell.hidden === 'true') {
-                delButtons.push(cell.uuid)
-                return
-              }
-              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
-            })
-          })
-        } else if (item.type === 'balcony') {
-          item.elements && item.elements.forEach(cell => {
-            if (cell.eleType !== 'button') return
-            if (cell.hidden === 'true') {
-              delButtons.push(cell.uuid)
-              return
-            }
-            buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-        } else if (item.type === 'line' || item.type === 'bar' || item.type === 'chart') {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-        } else if (item.type === 'table' && (item.subtype === 'normaltable' || item.subtype === 'editable')) {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-          item.cols && item.cols.forEach(col => {
-            if (col.type !== 'action') return
-            col.elements.forEach(btn => {
-              if (btn.hidden === 'true') {
-                delButtons.push(btn.uuid)
-                return
-              }
-              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
-            })
-          })
-        }
-      })
-    }
-
-    traversal(config.components)
-
-    return buttons
-  }
-
-  filterConfig = (components) => {
-    return components.map(item => {
-      if (item.type === 'tabs') {
-        item.subtabs.forEach(tab => {
-          tab.components = this.filterConfig(tab.components)
-        })
-      } else if (item.type === 'group') {
-        item.components = this.filterConfig(item.components)
-      } else if (item.type === 'table' && item.subtype === 'normaltable') {
-        item.search = item.search.filter(a => !a.origin)
-        item.action = item.action.filter(a => !a.origin)
-        item.cols = item.cols.filter(a => !a.origin)
-      }
-      return item
-    })
-  }
-
-  submitConfig = () => {
-    const { btn } = this.state
-    let config = fromJS(this.state.config).toJS()
-
-    if ((config.cacheUseful === 'true' && !config.cacheTime) || !config.MenuNo || !config.MenuName) {
-      notification.warning({
-        top: 92,
-        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
-        duration: 5
-      })
-      return
-    }
-
-    this.setState({
-      menuloading: true
-    })
-
-    setTimeout(() => {
-      config.components = this.filterConfig(config.components)
-
-      if (config.enabled && this.verifyConfig()) {
-        config.enabled = false
-      }
-
-      let param = {
-        func: 'sPC_ButtonParam_AddUpt',
-        ParentID: btn.ParentMenuID,
-        MenuID: config.uuid,
-        MenuNo: config.MenuNo || '',
-        Template: 'CustomPage',
-        MenuName: config.MenuName,
-        PageParam: JSON.stringify({Template: 'CustomPage'}),
-        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
-        open_edition: config.open_edition
-      }
-
-      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
-        func: 'sPC_Button_AddUpt',
-        Type: 60,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
-        ParentID: config.uuid,
-        MenuNo: config.MenuNo,
-        Template: 'CustomPage',
-        PageParam: '',
-        LongParam: '',
-        LText: []
-      }
-
-      let delButtons = fromJS(this.state.delButtons).toJS()
-      if (sessionStorage.getItem('appType') === 'pc') {
-        param.TypeCharOne = sessionStorage.getItem('kei_no')
-        param.Typename = 'pc'
-
-        btnParam.LText = ''
-      } else {
-        btnParam.LText = this.getMenuMessage(delButtons)
-        btnParam.LText = btnParam.LText.join(' union all ')
-        btnParam.LText = Utils.formatOptions(btnParam.LText)
-        btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-        btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
-      }
-
-      new Promise(resolve => {
-        if (delButtons.length === 0) {
-          resolve({
-            status: true
-          })
-        } else {
-          let _param = {
-            func: 'sPC_MainMenu_Del',
-            MenuID: delButtons.join(',')
-          }
-          Api.getSystemConfig(_param).then(res => {
-            resolve(res)
-          })
-        }
-      }).then(res => {
-        if (res.status) {
-          this.setState({
-            delButtons: []
-          })
-          return Api.getSystemConfig(param)
-        } else {
-          this.setState({
-            menuloading: false
-          })
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => {
-        if (!res) return
-
-        if (res.status) {
-          config.open_edition = res.open_edition || ''
-
-          this.setState({
-            config,
-            oriConfig: fromJS(config).toJS()
-          })
-
-          if (btnParam.LText) {
-            return Api.getSystemConfig(btnParam)
-          } else {
-            return {
-              status: true
-            }
-          }
-        } else {
-          this.setState({
-            menuloading: false
-          })
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => {
-        if (!res) return
-        
-        if (res.status) {
-          notification.success({
-            top: 92,
-            message: '淇濆瓨鎴愬姛',
-            duration: 2
-          })
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-        }
-        this.setState({
-          menuloading: false
-        })
-        MKEmitter.emit('completeSave')
-      })
-    }, 300)
-  }
-
-  onEnabledChange = () => {
-    const { config } = this.state
-
-    if (!config.enabled && this.verifyConfig(true)) {
-      return
-    }
-
-    this.setState({
-      config: {...config, enabled: !config.enabled}
-    })
-  }
-
-  verifyConfig = (show) => {
-    const { config } = this.state
-    let error = ''
-
-    let check = (components) => {
-      components.forEach(item => {
-        if (error) return
-        if (item.type === 'tabs') {
-          item.subtabs.forEach(tab => {
-            check(tab.components)
-          })
-          return
-        } else if (item.type === 'group') {
-          check(item.components)
-          return
-        } else if (!item.errors || item.errors.length === 0) {
-          return
-        }
-        
-        item.errors.forEach(err => {
-          if (err.level !== 0 || error) return
-          error = `缁勪欢銆�${item.name}銆�${err.detail}`
-        })
-      })
-    }
-
-    check(config.components)
-
-    if (show && error) {
-      notification.warning({
-        top: 92,
-        message: error,
-        duration: 5
-      })
-    }
-
-    return error
-  }
-
-  // 鏇存柊閰嶇疆淇℃伅
-  updateConfig = (config) => {
-    this.setState({
-      config: config
-    })
-
-    window.GLOB.customMenu = config
-  }
-
-  resetConfig = (config) => {
-    this.setState({
-      config,
-      comloading: true
-    }, () => {
-      this.setState({
-        comloading: false
-      })
-    })
-
-    window.GLOB.customMenu = config
-  }
-
-  /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config) => {
-    this.setState({ config })
-
-    window.GLOB.customMenu = config
-  }
-
-  insert = (item) => {
-    let config = fromJS(this.state.config).toJS()
-
-    config.components.push(item)
-
-    this.setState({config})
-    window.GLOB.customMenu = config
-    
-    notification.success({
-      top: 92,
-      message: '绮樿创鎴愬姛锛�',
-      duration: 2
-    })
-  }
-
-  refreshConfig = () => {
-    this.setState({
-      delButtons: [],
-      activeKey: 'basedata',
-      menuloading: false,
-      oriConfig: null,
-      config: null,
-    }, () => {
-      this.getMenuParam()
-    })
-  }
-
-  render () {
-    const { activeKey, comloading, config, menuloading, customComponents, MenuId, eyeopen } = this.state
-
-    return (
-      <ConfigProvider locale={_locale}>
-        <div className="pc-poper-view">
-          <Header />
-          <DndProvider backend={HTML5Backend}>
-            <div className="menu-body">
-              <div className="menu-setting">
-                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
-                  {/* 鍩烘湰淇℃伅 */}
-                  <Panel header="鍩烘湰淇℃伅" key="basedata">
-                    {/* 鑿滃崟淇℃伅 */}
-                    {config ? <MenuForm config={config} updateConfig={this.updateConfig}/> : null}
-                    {/* 琛ㄥ悕娣诲姞 */}
-                    {config ? <TableComponent config={config} updatetable={this.updatetable}/> : null}
-                  </Panel>
-                  {/* 缁勪欢娣诲姞 */}
-                  <Panel header="缁勪欢" key="component">
-                    <SourceWrap MenuType="" />
-                  </Panel>
-                  <Panel header="鍏冪礌" key="element">
-                    <Modulecell />
-                  </Panel>
-                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
-                    <SourceWrap components={customComponents} MenuType="" />
-                  </Panel> : null}
-                  <Panel header="椤甸潰鏍峰紡" key="background">
-                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
-                  </Panel>
-                </Collapse>
-              </div>
-              <div className={'menu-view' + (menuloading ? ' saving' : '') + (eyeopen ? ' eye-open' : '')}>
-                <Card title={
-                  <div> {config && config.MenuName} </div>
-                } bordered={false} extra={
-                  <div>
-                    <Button className="mk-border-purple" onClick={() => this.setState({eyeopen: !eyeopen})}>{!eyeopen ? <EyeOutlined /> : <EyeInvisibleOutlined />} 缁勪欢鍚�</Button>
-                    <Versions MenuId={MenuId} open_edition={config ? config.open_edition : ''} updateConfig={this.refreshConfig}/>
-                    <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
-                    <StyleCombControlButton menu={config} />
-                    <PasteController insert={this.insert} />
-                    {config ? <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config.enabled} onChange={this.onEnabledChange} /> : null}
-                    <Button type="primary" id="save-pop-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
-                    <Button type="default" onClick={this.closeView}>杩斿洖</Button>
-                  </div>
-                } style={{ width: '100%' }}>
-                  {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
-                </Card>
-              </div>
-            </div>
-          </DndProvider>
-          <StyleController />
-          <StyleCombController />
-          <ModalController />
-        </div>
-      </ConfigProvider>
-    )
-  }
-}
-
-export default PopViewDesign
\ No newline at end of file
diff --git a/src/views/rolemanage/index.jsx b/src/views/rolemanage/index.jsx
index 55b7111..f51b6dc 100644
--- a/src/views/rolemanage/index.jsx
+++ b/src/views/rolemanage/index.jsx
@@ -398,10 +398,6 @@
       lang: app.lang
     }
 
-    if (app.typename === 'pc' && record.nodes && record.nodes.popviews && record.nodes.popviews.length > 0) {
-      param.MenuID = param.MenuID + ',' + record.nodes.popviews.join(',')
-    }
-
     let _param = {
       func: 's_kei_link_keyids_addupt',
       BID: app.ID,
@@ -537,10 +533,6 @@
                   }
 
                   delete item.menus_rolelist
-                }
-
-                if (targetKeys.includes(item.MenuID) && item.nodes && item.nodes.popviews && item.nodes.popviews.length > 0) {
-                  list = [...list, ...item.nodes.popviews]
                 }
                 
                 return item
diff --git a/src/views/tabledesign/index.jsx b/src/views/tabledesign/index.jsx
index 3ebbafb..d368f6b 100644
--- a/src/views/tabledesign/index.jsx
+++ b/src/views/tabledesign/index.jsx
@@ -6,11 +6,13 @@
 import HTML5Backend from 'react-dnd-html5-backend'
 import { ConfigProvider, notification, Modal, Collapse, Card, Switch, Button, Typography } from 'antd'
 import { DoubleLeftOutlined, DoubleRightOutlined } from '@ant-design/icons'
+import md5 from 'md5'
 
 import Api from '@/api'
 import Utils, { setGLOBFuncs } from '@/utils/utils.js'
 import antdZhCN from 'antd/es/locale/zh_CN'
 import MKEmitter from '@/utils/events.js'
+import { getTables } from '@/utils/utils-custom.js'
 import SourceElement from '@/templates/zshare/dragsource'
 import asyncComponent from '@/utils/asyncComponent'
 import Source from './source'
@@ -24,6 +26,8 @@
 const _locale = antdZhCN
 
 const MenuForm = asyncComponent(() => import('./menuform'))
+const PopView = asyncComponent(() => import('./popview'))
+const TableNodes = asyncComponent(() => import('@/menu/tablenodes'))
 const TableSource = asyncComponent(() => import('./tablesource'))
 const Header = asyncComponent(() => import('@/menu/header'))
 const MenuShell = asyncComponent(() => import('@/menu/tableshell'))
@@ -31,9 +35,12 @@
 const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
 const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
 const Versions = asyncComponent(() => import('@/menu/versions'))
+const Transfer = asyncComponent(() => import('@/menu/transfer'))
+const Unattended = asyncComponent(() => import('@/templates/zshare/unattended'))
 const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
 const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+const PasteBaseTable = asyncComponent(() => import('@/menu/components/share/pastebasetable'))
 
 sessionStorage.setItem('isEditState', 'true')
 sessionStorage.setItem('appType', '')          // 搴旂敤绫诲瀷
@@ -49,10 +56,10 @@
     menuloading: false,
     oriConfig: null,
     config: null,
-    customComponents: [],
     comloading: false,
-    settingshow: true,
-    modalStatus: false       // 寮圭獥鏄惁寮�鍚紝鍒ゆ柇ctrl+s鏄惁鍙敤
+    settingshow: sessionStorage.getItem('settingshow') !== 'false',
+    view: null,
+    popConfig: null
   }
 
   UNSAFE_componentWillMount() {
@@ -88,20 +95,9 @@
   }
 
   componentDidMount () {
-    MKEmitter.addListener('modalStatus', this.modalStatus)
     MKEmitter.addListener('changePopview', this.initPopview)
     MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
-    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     setTimeout(() => {
-      if (sessionStorage.getItem('app_custom_components')) {
-        let list = sessionStorage.getItem('app_custom_components')
-        list = JSON.parse(list)
-
-        this.setCustomComponent(list)
-      } else {
-        this.updateCustomComponent()
-      }
-      this.getAppPictures()
       this.getPrintTemp()
       this.getRoleFields()
       setGLOBFuncs()
@@ -126,16 +122,32 @@
       let _shortcut = `${preKey}+${keyCode}`
 
       if (_shortcut === 'ctrl+83') {
-        if (this.state.modalStatus) {
+        let modals = document.querySelectorAll('.mk-pop-modal')
+        let msg = null
+        for (let i = 0; i < modals.length; i++) {
+          if (msg) {
+            break
+          }
+
+          let node = modals[i].querySelector('.mk-com-name')
+
+          if (node) {
+            msg = node.innerText
+          }
+        }
+        if (msg) {
           notification.warning({
             top: 92,
-            message: '璇蜂繚瀛�' + this.state.modalStatus,
+            message: '璇蜂繚瀛�' + msg,
             duration: 5
           })
           return false
         }
 
         let node = document.getElementById('save-modal-config')
+        if (!node) {
+          node = document.getElementById('save-pop-config')
+        }
         if (!node) {
           node = document.getElementById('save-config')
         }
@@ -155,17 +167,13 @@
     this.setState = () => {
       return
     }
-    MKEmitter.removeListener('modalStatus', this.modalStatus)
     MKEmitter.removeListener('changePopview', this.initPopview)
     MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
-    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
-  }
-
-  modalStatus = (val) => {
-    this.setState({modalStatus: val})
   }
 
   triggerMenuSave = () => {
+    if (this.state.view === 'popview') return
+    
     this.submitConfig()
   }
 
@@ -213,110 +221,102 @@
     }
   }
 
-  getAppPictures = () => {
-    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
-    
-    Api.getSystemConfig({
-      func: 's_url_db_adduptdel',
-      PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-      PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-      typecharone: 'image',
-      type: 'search'
-    }).then(res => {
-      if (res.status) {
-        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
-      }
-
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'video',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
-        }
-      })
-      Api.getSystemConfig({
-        func: 's_url_db_adduptdel',
-        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
-        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
-        typecharone: 'color',
-        type: 'search'
-      }).then(res => {
-        if (res.status) {
-          sessionStorage.setItem('app_colors', JSON.stringify(res.data || []))
-        }
-      })
-    })
-  }
-
-  updateCustomComponent = () => {
-    Api.getSystemConfig({
-      func: 's_get_custom_components',
-      typename: '',
-      typecharone: ''
-    }).then(res => {
-      if (!res.status) {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
-        })
-      } else if (res.cus_list) {
-        sessionStorage.setItem('app_custom_components', JSON.stringify(res.cus_list))
-        this.setCustomComponent(res.cus_list)
-      }
-    })
-  }
-
-  setCustomComponent = (cus_list) => {
-    let coms = []
-
-    cus_list.forEach(item => {
-      let config = ''
-
-      try {
-        config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
-      } catch (e) {
-        console.warn('Parse Failure')
-        config = ''
-      }
-
-      if (!config || !item.c_name) return
-
-      window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
-      coms.push({
-        uuid: item.c_id,
-        type: 'menu',
-        title: item.c_name,
-        url: item.images,
-        component: config.type,
-        subtype: config.subtype,
-        config
-      })
-    })
-    this.setState({customComponents: coms})
-  }
-
   initPopview = (card, btn) => {
-    const { oriConfig, config } = this.state
+    const { config } = this.state
 
-    if (!is(fromJS(oriConfig), fromJS(config))) {
-      notification.warning({
-        top: 92,
-        message: '閰嶇疆宸蹭慨鏀癸紝璇蜂繚瀛橈紒',
-        duration: 5
-      })
+    if (!this.checkBase()) {
       return
     }
 
     let _btn = fromJS(btn).toJS()
-    _btn.MenuName = config.MenuName + '-' + card.name + '-' + btn.label
-    _btn.ParentMenuID = config.uuid
 
-    this.props.history.push('/popdesign/' + window.btoa(window.encodeURIComponent((JSON.stringify(_btn)))))
+    if (_btn.config) {
+      _btn.config.uuid = _btn.uuid
+      _btn.config.MenuID = _btn.uuid
+      _btn.config.ParentId = card.uuid
+      _btn.config.MenuName = _btn.label
+    } else {
+      _btn.config = {
+        uuid: _btn.uuid,
+        MenuID: _btn.uuid,
+        ParentId: card.uuid,
+        enabled: false,
+        MenuName: _btn.label,
+        tables: config.tables || [],
+        Template: 'BaseTable',
+        components: [{
+          uuid: Utils.getuuid(),
+          type: 'table',
+          width: 24,
+          name: '涓昏〃',
+          subtype: 'basetable',
+          isNew: true
+        }],
+        viewType: 'popview',
+        style: { backgroundColor: '#ffffff', backgroundImage: '', paddingTop: '16px', paddingBottom: '40px', paddingLeft: '16px', paddingRight: '16px' }
+      }
+    }
+
+    this.setState({view: 'popview', popConfig: _btn})
+  }
+
+  submitPopConfig = (btnconfig) => {
+    let config = fromJS(this.state.config).toJS()
+
+    config.components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          if (btnconfig.ParentId !== tab.components[0].uuid) return
+          
+          tab.components[0].action.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.uuid === btnconfig.uuid) {
+              btn.config = btnconfig
+            }
+          })
+          tab.components[0].cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              if (btn.OpenType === 'popview' && btn.uuid === btnconfig.uuid) {
+                btn.config = btnconfig
+              }
+            })
+          })
+
+          tab.components[0].$tables = getTables(tab.components[0])
+        })
+      } else if (item.uuid === btnconfig.ParentId) {
+        item.action.forEach(btn => {
+          if (btn.OpenType === 'popview' && btn.uuid === btnconfig.uuid) {
+            btn.config = btnconfig
+          }
+        })
+        item.cols.forEach(col => {
+          if (col.type !== 'action') return
+          col.elements.forEach(btn => {
+            if (btn.OpenType === 'popview' && btn.uuid === btnconfig.uuid) {
+              btn.config = btnconfig
+            }
+          })
+        })
+
+        item.$tables = getTables(item)
+      }
+    })
+
+    this.setState({ config }, () => {
+      this.submitConfig()
+    })
+  }
+
+  closePop = () => {
+    const { config } = this.state
+
+    sessionStorage.setItem('editMenuType', 'menu')
+
+    window.GLOB.urlFields = config.urlFields || []
+    window.GLOB.customMenu = config
+
+    this.setState({view: '', popConfig: null})
   }
 
   closeView = () => {
@@ -383,15 +383,21 @@
               }
             ],
             viewType: 'menu',
-            style: {
-              backgroundColor: '#ffffff', backgroundImage: '',
-              paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'
-            },
+            style: { backgroundColor: '#ffffff', paddingTop: '16px', paddingBottom: '80px', paddingLeft: '16px', paddingRight: '16px'},
           }
         } else {
           config.uuid = MenuId
           config.MenuID = MenuId
           config.Template = 'BaseTable'
+          config.components.forEach(item => {
+            if (item.type === 'tabs') {
+              item.subtabs.forEach(tab => {
+                tab.components[0].name = tab.label
+              })
+            } else {
+              item.name = '涓昏〃'
+            }
+          })
         }
 
         config.open_edition = result.open_edition || ''
@@ -412,48 +418,68 @@
     })
   }
 
-  getMenuMessage = (delButtons) => {
+  getMenuMessage = (tbs) => {
     const { config } = this.state
     let buttons = []
     let _sort = 1
 
-    let traversal = (components) => {
-      components.forEach(item => {
-        if (item.type === 'tabs') {
-          item.subtabs.forEach(tab => {
-            traversal(tab.components)
+    config.components.forEach(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          if (tab.components[0].$tables) {
+            tbs.push(...tab.components[0].$tables)
+          }
+
+          if (tab.permission !== 'true' || tab.hide === 'true') return
+
+          buttons.push(`select '${tab.components[0].uuid}' as menuid, '${tab.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
+          _sort++
+
+          let _s = 1
+
+          tab.components[0].action.forEach(btn => {
+            if (btn.hidden === 'true') return
+
+            buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
+            _s++
           })
-        } else {
-          item.action && item.action.forEach(btn => {
-            if (btn.hidden === 'true') {
-              delButtons.push(btn.uuid)
-              return
-            }
-            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-            _sort++
-          })
-          item.cols && item.cols.forEach(col => {
+          tab.components[0].cols.forEach(col => {
             if (col.type !== 'action') return
             col.elements.forEach(btn => {
-              if (btn.hidden === 'true') {
-                delButtons.push(btn.uuid)
-                return
-              }
-              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
-              _sort++
+              if (btn.hidden === 'true') return
+
+              buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_s * 10}' as Sort, '${tab.components[0].uuid}' as parentid, 60 as Type`)
+              _s++
             })
           })
+        })
+      } else {
+        if (item.$tables) {
+          tbs.push(...item.$tables)
         }
-      })
-    }
+        item.action.forEach(btn => {
+          if (btn.hidden === 'true') return
 
-    traversal(config.components)
+          buttons.push(`select '${btn.uuid}' as menuid, '${btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
+          _sort++
+        })
+        item.cols.forEach(col => {
+          if (col.type !== 'action') return
+          col.elements.forEach(btn => {
+            if (btn.hidden === 'true') return
+
+            buttons.push(`select '${btn.uuid}' as menuid,  '${btn.label}' as menuname, '${_sort * 10}' as Sort, '${config.uuid}' as parentid, 40 as Type`)
+            _sort++
+          })
+        })
+      }
+    })
 
     return buttons
   }
 
-  submitConfig = () => {
-    let config = fromJS(this.state.config).toJS()
+  checkBase = () => {
+    const { config } = this.state
 
     if (!config.MenuName || !config.MenuNo || !config.fstMenuId || !config.parentId) {
       notification.warning({
@@ -461,6 +487,15 @@
         message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
         duration: 5
       })
+      return false
+    }
+    return true
+  }
+
+  submitConfig = () => {
+    let config = fromJS(this.state.config).toJS()
+
+    if (!this.checkBase()) {
       return
     }
 
@@ -471,6 +506,37 @@
     setTimeout(() => {
       if (config.enabled && this.verifyConfig()) {
         config.enabled = false
+      }
+
+      let tbs = []
+      let btns = this.getMenuMessage(tbs)
+      let arr = []
+      tbs = tbs.filter(tb => {
+        let _tb = tb.toLowerCase()
+
+        if (arr.includes(_tb)) return false
+        arr.push(_tb)
+
+        return true
+      })
+      tbs.sort()
+      if (tbs.length && sessionStorage.getItem('mk_tb_names')) {
+        let names = sessionStorage.getItem('mk_tb_names')
+        tbs = tbs.filter(tb => names.indexOf(',' + tb.toLowerCase() + ',') > -1)
+      }
+      tbs = tbs.map(tb => `'${tb}'`).join(';')
+
+      let key = md5(config.uuid + tbs.toLowerCase())
+      let url = ''
+
+      if (config.tbkey === key) {
+        key = ''
+      } else {
+        let urlparam = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
+        urlparam.type = 'admin'
+        urlparam.MenuType = 'BaseTable'
+        url = window.btoa(window.encodeURIComponent(JSON.stringify(urlparam)))
+        config.tbkey = key
       }
 
       let param = {
@@ -484,10 +550,13 @@
         Template: 'BaseTable',
         MenuName: config.MenuName || '',
         PageParam: JSON.stringify({Template: 'BaseTable', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false'}),
-        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
         open_edition: config.open_edition,
         LText: '',
-        LTexttb: ''
+        LTexttb: '',
+        debug_md5: key,
+        debug_url: url,
+        debug_list: window.btoa(tbs),
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config)))
       }
 
       param.LText = Utils.formatOptions(param.LText)
@@ -500,102 +569,52 @@
         ParentID: config.uuid,
         MenuNo: config.MenuNo,
         Template: 'BaseTable',
-        PageParam: '',
-        LongParam: '',
-        LText: []
+        button_proc_edition: 'Y'
       }
 
-      let delButtons = []
-
-      btnParam.LText = this.getMenuMessage(delButtons)
-      btnParam.LText = btnParam.LText.join(' union all ')
+      btnParam.LText = btns.join(' union all ')
 
       btnParam.LText = Utils.formatOptions(btnParam.LText)
       btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
       btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
 
       new Promise(resolve => {
-        resolve(true)
-      }).then(res => { // 鎸夐挳鍒犻櫎
-        if (delButtons.length === 0) {
-          return {
-            status: true
-          }
-        } else {
-          let _param = {
-            func: 'sPC_MainMenu_Del',
-            MenuID: delButtons.join(',')
-          }
-          return Api.getSystemConfig(_param)
-        }
-      }).then(res => { // 椤甸潰淇濆瓨
-        if (!res) return
-
-        if (res.status) {
-          return Api.getSystemConfig(param)
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
-        if (!res) return
-
-        if (res.status) {
-          let ori = this.state.oriConfig
-          if (config.MenuName !== ori.MenuName || config.MenuNo !== ori.MenuNo || config.parentId !== ori.parentId) {
-            localStorage.setItem('menuUpdate', new Date().getTime())
-          }
-          config.open_edition = res.open_edition || ''
-          this.setState({
-            config,
-            oriConfig: fromJS(config).toJS(),
-          })
-
-          if (btnParam.LText) {
-            return Api.getSystemConfig(btnParam)
-          } else {
-            return {
-              status: true
-            }
-          }
-        } else {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
-      }).then(res => { // 鎸夐挳澶嶅埗
-        if (!res) return
-        if (!res.status) {
-          notification.warning({
-            top: 92,
-            message: res.message,
-            duration: 5
-          })
-          return false
-        }
+        Api.getSystemConfig(param).then(res => {
+          resolve(res)
+        })
       }).then(res => {
-        if (res && res.status) {
-          this.setState({
-            menuloading: false
-          })
+        if (!res || !res.status) return res
+
+        localStorage.setItem('menuUpdate', new Date().getTime() + ',' + config.uuid)
+        config.open_edition = res.open_edition || ''
+
+        this.setState({
+          config,
+          oriConfig: fromJS(config).toJS(),
+        })
+
+        return Api.getSystemConfig(btnParam)
+      }).then(res => {
+        this.setState({
+          menuloading: false
+        })
+
+        if (!res) return
+
+        if (res.status) {
           notification.success({
             top: 92,
             message: '淇濆瓨鎴愬姛',
             duration: 2
           })
+          MKEmitter.emit('completeSave')
         } else {
-          this.setState({
-            menuloading: false
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
           })
         }
-        MKEmitter.emit('completeSave')
       })
     }, 300 + (+sessionStorage.getItem('mkDelay')))
   }
@@ -648,30 +667,36 @@
     const { config } = this.state
     let error = ''
 
-    let check = (components) => {
-      components.forEach(item => {
-        if (error) return
+    config.components.forEach(item => {
+      if (error) return
         
-        if (item.type === 'tabs') {
-          item.subtabs.forEach(tab => {
-            check(tab.components)
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          if (!tab.components[0].errors || tab.components[0].errors.length === 0 || error) return
+          tab.components[0].errors.forEach(err => {
+            if (err.level !== 0 || error) return
+            error = `瀛愯〃銆�${tab.label}銆�${err.detail}`
           })
-          return
-        } else if (item.type === 'group') {
-          check(item.components)
-          return
-        } else if (!item.errors || item.errors.length === 0) {
-          return
-        }
-
+        })
+      } else {
         item.errors.forEach(err => {
           if (err.level !== 0 || error) return
-          error = `缁勪欢銆�${item.name}銆�${err.detail}`
+          error = `涓昏〃 ${err.detail}`
         })
-      })
-    }
+      }
+    })
 
-    check(config.components)
+    if (!error && config.autoMatic && config.autoMatic.enable === 'true') {
+      let pass = false
+      config.components[0].action.forEach(item => {
+        if (item.uuid === config.autoMatic.action && (['pop', 'prompt', 'exec'].includes(item.OpenType) || (item.OpenType === 'funcbutton' && item.funcType === 'print'))) {
+          pass = true
+        }
+      })
+      if (!pass) {
+        error = '鏃犱汉鍊煎畧璁剧疆鏃犳晥锛�'
+      }
+    }
 
     if (show && error) {
       notification.warning({
@@ -707,30 +732,37 @@
   insert = (item) => {
     let config = fromJS(this.state.config).toJS()
 
-    config.components.push(item)
+    let tabs = {
+      uuid: Utils.getuuid(),
+      type: 'tabs',
+      subtype: 'tabletabs',
+      width: 24,
+      setting: {},
+      style: {},
+      subtabs: [
+        { uuid: Utils.getuuid(), label: item.name || '瀛愯〃1', icon: '', components: [item] },
+      ]
+    }
+
+    config.components.push(tabs)
 
     this.setState({config})
     window.GLOB.customMenu = config
-
-    notification.success({
-      top: 92,
-      message: '绮樿创鎴愬姛锛�',
-      duration: 2
-    })
   }
 
   changeSetting = () => {
     this.setState({settingshow: !this.state.settingshow})
+    sessionStorage.setItem('settingshow', '' + !this.state.settingshow)
   }
 
   render () {
-    const { activeKey, comloading, MenuId, config, settingshow, ParentId, menuloading } = this.state
+    const { view, activeKey, comloading, MenuId, config, settingshow, ParentId, menuloading } = this.state
 
     return (
       <ConfigProvider locale={_locale}>
-        <div className="pc-table-view">
-          <Header />
-          <DndProvider backend={HTML5Backend}>
+        <Header />
+        <DndProvider backend={HTML5Backend}>
+        {view !== 'popview' ? <div className="pc-table-view">
             <div className="menu-body">
               <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
                 <div className="draw">
@@ -778,8 +810,12 @@
                   <div style={{paddingLeft: '15px'}}> {config && config.MenuName} </div>
                 } bordered={false} extra={
                   <div>
+                    {config ? <Transfer config={config}/> : null}
+                    {config ? <Unattended config={config} updateConfig={this.updateConfig}/> : null}
                     <Versions MenuId={MenuId} open_edition={config ? config.open_edition : ''}/>
+                    <TableNodes config={config} />
                     <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
+                    <PasteBaseTable type="page" insert={this.insert}/>
                     <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config && config.enabled} onChange={this.onEnabledChange} />
                     <Button type="primary" id="save-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
                     <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
@@ -789,10 +825,10 @@
                 </Card>
               </div>
             </div>
-          </DndProvider>
-          <StyleController />
-          <ModalController />
-        </div>
+          </div> : <PopView btn={this.state.popConfig} save={this.submitPopConfig} cancel={this.closePop}/>}
+        </DndProvider>
+        <StyleController />
+        <ModalController />
       </ConfigProvider>
     )
   }
diff --git a/src/views/tabledesign/index.scss b/src/views/tabledesign/index.scss
index c022ade..2fe5621 100644
--- a/src/views/tabledesign/index.scss
+++ b/src/views/tabledesign/index.scss
@@ -2,6 +2,14 @@
   overflow-x: hidden;
   overflow-y: hidden;
 }
+.mk-source-wrap {
+  .ant-radio-button-wrapper:last-child {
+    display: none;
+  }
+  .ant-radio-button-wrapper:not(:first-child) {
+    border-radius: 0 4px 4px 0;
+  }
+}
 .pc-table-view {
   background: #000;
   min-height: 100vh;
@@ -17,37 +25,6 @@
     }
     .anticon-tool {
       display: none;
-    }
-  }
-  .component-name {
-    position: absolute;
-    z-index: 9;
-    display: none;
-    left: 0;
-    right: 0;
-    top: 0;
-    bottom: 0;
-    background: rgba(255, 255, 255, 0.9);
-    border: 1px solid #1890ff;
-    .center {
-      position: absolute;
-      font-size: 16px;
-      left: 50%;
-      top: 50%;
-      color: #1890ff;
-      transform: translate(-50%, -50%);
-      max-width: 70%;
-      .title {
-        text-align: center;
-      }
-    }
-    .error {
-      text-align: center;
-      color: red;
-      display: block;
-    }
-    .waring {
-      color: orange;
     }
   }
   >.menu-body {
@@ -137,7 +114,7 @@
     }
 
     .menu-setting.hidden {
-      left: -300px;
+      left: -280px;
     }
     .menu-setting.hidden + .menu-view {
       width: 100vw;
@@ -163,7 +140,7 @@
           .ant-card-extra {
             padding: 5px 0;
             button {
-              margin-left: 10px;
+              margin-left: 15px;
             }
             >div >div >button, .style-control-button {
               padding: 0px 7px;
diff --git a/src/views/tabledesign/menuform/index.jsx b/src/views/tabledesign/menuform/index.jsx
index b779db1..5e16759 100644
--- a/src/views/tabledesign/menuform/index.jsx
+++ b/src/views/tabledesign/menuform/index.jsx
@@ -1,7 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Select, notification, Radio, Tooltip, Switch } from 'antd'
-import { QuestionCircleOutlined } from '@ant-design/icons'
+import { Form, Row, Col, Input, Select, notification, Radio, Switch } from 'antd'
 
 import Api from '@/api'
 import options from '@/store/options.js'
@@ -26,7 +25,57 @@
   }
 
   UNSAFE_componentWillMount () {
+    if (sessionStorage.getItem('thdMenuList') && sessionStorage.getItem('fstMenuList')) {
+      this.setMenus()
+    } else {
+      this.getMenus()
+    }
+  }
+
+  setMenus = () => {
     const { MenuId, config } = this.props
+
+    let menulist = sessionStorage.getItem('fstMenuList')
+    let thdMenuList = sessionStorage.getItem('thdMenuList')
+
+    menulist = JSON.parse(menulist)
+    thdMenuList = JSON.parse(thdMenuList)
+
+    let thdMenu = null
+
+    thdMenuList.forEach(trd => {
+      if (MenuId === trd.MenuID) {
+        thdMenu = trd
+      }
+    })
+
+    let smenulist = []
+
+    if (thdMenu) {
+      menulist.forEach(item => {
+        if (item.MenuID === thdMenu.FstId) {
+          smenulist = item.children
+        }
+      })
+    }
+
+    this.props.updateConfig({...config, fstMenuId: thdMenu ? thdMenu.FstId : ''})
+
+    this.setState({
+      fstMenuId: thdMenu ? thdMenu.FstId : '',
+      menulist,
+      smenulist
+    }, () => {
+      this.props.form.setFieldsValue({
+        fstMenuId: thdMenu ? thdMenu.FstId : '',
+        parentId: thdMenu ? thdMenu.ParentId : ''
+      })
+    })
+  }
+
+  getMenus = () => {
+    const { MenuId, config } = this.props
+
     Api.getSystemConfig({func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}).then(result => {
       if (result.status) {
         let thdMenu = null
@@ -64,12 +113,7 @@
                     value: trd.MenuID,
                     label: trd.MenuName,
                     type: 'CommonTable',
-                    // disabled: trd.MenuID === MenuId
                     disabled: false
-                  }
-
-                  if (MenuId === trd.MenuID) {
-                    thdMenu = trdItem
                   }
 
                   if (trd.PageParam) {
@@ -79,6 +123,10 @@
                     } catch (e) {
 
                     }
+                  }
+
+                  if (MenuId === trd.MenuID) {
+                    thdMenu = trdItem
                   }
 
                   thdMenuList.push(trdItem)
@@ -93,11 +141,13 @@
         })
 
         let smenulist = []
-        menulist.forEach(item => {
-          if (thdMenu && (item.MenuID === thdMenu.FstId)) {
-            smenulist = item.children
-          }
-        })
+        if (thdMenu) {
+          menulist.forEach(item => {
+            if (item.MenuID === thdMenu.FstId) {
+              smenulist = item.children
+            }
+          })
+        }
         sessionStorage.setItem('fstMenuList', JSON.stringify(menulist))
         sessionStorage.setItem('thdMenuList', JSON.stringify(thdMenuList))
         this.props.updateConfig({...config, fstMenuId: thdMenu ? thdMenu.FstId : ''})
@@ -150,8 +200,6 @@
       this.props.updateConfig({...config, OpenType: value})
     } else if (key === 'hidden') {
       this.props.updateConfig({...config, hidden: value})
-    } else if (key === 'permission') {
-      this.props.updateConfig({...config, permission: value})
     }
   }
 
@@ -281,23 +329,6 @@
                 <Radio.Group onChange={(e) => {this.selectChange('OpenType', e.target.value)}}>
                   <Radio value="newtab">鏍囩椤�</Radio>
                   <Radio value="newpage">鏂伴〉闈�</Radio>
-                </Radio.Group>
-              )}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label={
-              <Tooltip placement="topLeft" title="璺宠繃鏉冮檺楠岃瘉鏃讹紝椤甸潰涓粍浠跺強鎸夐挳涓嶅湪杩涜鏉冮檺鎺у埗銆�">
-                <QuestionCircleOutlined className="mk-form-tip" />
-                鏉冮檺楠岃瘉
-              </Tooltip>
-            }>
-              {getFieldDecorator('permission', {
-                initialValue: config.permission || 'true'
-              })(
-                <Radio.Group onChange={(e) => {this.selectChange('permission', e.target.value)}}>
-                  <Radio value="true">浣跨敤</Radio>
-                  <Radio value="false">涓嶄娇鐢�</Radio>
                 </Radio.Group>
               )}
             </Form.Item>
diff --git a/src/views/tabledesign/popview/index.jsx b/src/views/tabledesign/popview/index.jsx
new file mode 100644
index 0000000..66e3c21
--- /dev/null
+++ b/src/views/tabledesign/popview/index.jsx
@@ -0,0 +1,234 @@
+import React, { Component } from 'react'
+import { is, fromJS } from 'immutable'
+import PropTypes from 'prop-types'
+import { notification, Modal, Collapse, Card, Switch, Button } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import SourceElement from '@/templates/zshare/dragsource'
+import asyncComponent from '@/utils/asyncComponent'
+import Source from '../source'
+
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+
+const MenuShell = asyncComponent(() => import('@/menu/tableshell'))
+const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
+const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+
+class PopViewDesign extends Component {
+  static propTpyes = {
+    btn: PropTypes.object,
+    save: PropTypes.func,
+    cancel: PropTypes.func
+  }
+
+  state = {
+    menuloading: false,
+    oriConfig: null,
+    config: null,
+    comloading: false,
+  }
+
+  UNSAFE_componentWillMount() {
+    const { btn } = this.props
+    sessionStorage.setItem('editMenuType', 'popview')
+
+    let config = fromJS(btn.config).toJS()
+
+    window.GLOB.urlFields = []               // url鍙橀噺
+    window.GLOB.customMenu = config          // 淇濆瓨鑿滃崟淇℃伅
+
+    this.setState({config: config, oriConfig: fromJS(config).toJS()})
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('completeSave', this.completeSave)
+    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('completeSave', this.completeSave)
+    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
+  }
+
+  closeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!is(fromJS(oriConfig), fromJS(config))) {
+      const _this = this
+
+      confirm({
+        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
+        content: '',
+        onOk() {
+          _this.props.cancel()
+        },
+        onCancel() {}
+      })
+    } else {
+      this.props.cancel()
+    }
+  }
+
+  completeSave = () => {
+    this.setState({
+      oriConfig: fromJS(this.state.config).toJS(),
+      menuloading: false
+    })
+  }
+
+  submitConfig = () => {
+    let config = fromJS(this.state.config).toJS()
+
+    if (config.cacheUseful === 'true' && !config.cacheTime) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勭紦瀛樿缃紒',
+        duration: 5
+      })
+      return
+    }
+
+    if (config.enabled && this.verifyConfig()) {
+      config.enabled = false
+    }
+
+    config.$tables = Array.from(new Set(config.components[0].$tables || []))
+
+    this.setState({
+      config: config,
+      menuloading: true
+    })
+
+    window.GLOB.customMenu = config
+
+    this.props.save(fromJS(config).toJS())
+  }
+
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    if (!config.enabled && this.verifyConfig(true)) {
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: !config.enabled}
+    })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+    
+    if (config.components[0].errors) {
+      config.components[0].errors.forEach(err => {
+        if (err.level !== 0 || error) return
+        error = err.detail
+      })
+    }
+    
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
+  }
+
+  // 鏇存柊閰嶇疆淇℃伅
+  updateConfig = (config) => {
+    this.setState({
+      config: config
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  resetConfig = (config) => {
+    this.setState({
+      config,
+      comloading: true
+    }, () => {
+      this.setState({
+        comloading: false
+      })
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  /**
+   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
+   */
+  updatetable = (config) => {
+    this.setState({ config })
+
+    window.GLOB.customMenu = config
+  }
+
+  render () {
+    const { comloading, config, menuloading } = this.state
+
+    return (
+      <div className="pc-poper-view">
+        <div className="menu-body">
+          <div className="menu-setting">
+            <Collapse accordion defaultActiveKey="basedata" bordered={false}>
+              {/* 鍩烘湰淇℃伅 */}
+              <Panel header="鍩烘湰淇℃伅" key="basedata">
+                {/* 琛ㄥ悕娣诲姞 */}
+                <TableComponent config={config} updatetable={this.updatetable}/>
+              </Panel>
+              {/* 缁勪欢娣诲姞 */}
+              <Panel header="鎼滅储" key="search">
+                {Source.searchItems.map((item, index) => (<SourceElement key={index} content={item}/>))}
+              </Panel>
+              <Panel header="鎸夐挳" key="action">
+                {Source.popactionItems.map((item, index) => (<SourceElement key={index} content={item}/>))}
+              </Panel>
+              <Panel header="鏄剧ず鍒�" key="cols">
+                {Source.columnItems.map((item, index) => (<SourceElement key={index} content={item}/>))}
+              </Panel>
+              <Panel header="椤甸潰鏍峰紡" key="background">
+                <BgController config={config} updateConfig={this.updateConfig} />
+              </Panel>
+            </Collapse>
+          </div>
+          <div className={'menu-view' + (menuloading ? ' saving' : '')}>
+            <Card title={
+              <div> {config.MenuName} </div>
+            } bordered={false} extra={
+              <div>
+                <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
+                <Switch className="big" checkedChildren="鍚�" unCheckedChildren="鍋�" checked={config.enabled} onChange={this.onEnabledChange} />
+                <Button type="primary" id="save-pop-config" onClick={this.submitConfig} loading={menuloading}>淇濆瓨</Button>
+                <Button type="default" onClick={this.closeView}>杩斿洖</Button>
+              </div>
+            } style={{ width: '100%' }}>
+              {!comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
+            </Card>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default PopViewDesign
\ No newline at end of file
diff --git a/src/views/popdesign/index.scss b/src/views/tabledesign/popview/index.scss
similarity index 100%
copy from src/views/popdesign/index.scss
copy to src/views/tabledesign/popview/index.scss
diff --git a/src/views/tabledesign/source.jsx b/src/views/tabledesign/source.jsx
index 8d9b629..eb08829 100644
--- a/src/views/tabledesign/source.jsx
+++ b/src/views/tabledesign/source.jsx
@@ -5,62 +5,52 @@
     {
       type: 'search',
       label: '鏂囨湰',
-      subType: 'text',
-      url: ''
+      subType: 'text'
     },
     {
       type: 'search',
       label: '涓嬫媺閫夋嫨',
-      subType: 'select',
-      url: ''
+      subType: 'select'
     },
     {
       type: 'search',
       label: '涓嬫媺澶氶��',
-      subType: 'multiselect',
-      url: ''
+      subType: 'multiselect'
     },
     {
       type: 'search',
       label: '鑱斿姩鑿滃崟',
-      subType: 'link',
-      url: ''
+      subType: 'link'
     },
     {
       type: 'search',
       label: '閫夐」鍗�',
-      subType: 'checkcard',
-      url: ''
+      subType: 'checkcard'
     },
     {
       type: 'search',
       label: '鏃ユ湡锛堝ぉ锛�',
-      subType: 'date',
-      url: ''
+      subType: 'date'
     },
     {
       type: 'search',
       label: '鏃ユ湡锛堝懆锛�',
-      subType: 'dateweek',
-      url: ''
+      subType: 'dateweek'
     },
     {
       type: 'search',
       label: '鏃ユ湡锛堟湀锛�',
-      subType: 'datemonth',
-      url: ''
+      subType: 'datemonth'
     },
     {
       type: 'search',
       label: '鏃ユ湡锛堝尯闂达級',
-      subType: 'daterange',
-      url: ''
+      subType: 'daterange'
     },
     {
       type: 'search',
       label: '鏃ユ湡锛堢粍鍚堬級',
-      subType: 'group',
-      url: ''
+      subType: 'group'
     }
   ],
   actionItems: [
@@ -90,6 +80,7 @@
       label: '瀵煎叆Excel',
       text: '瀵煎叆Excel',
       value: 'excelIn',
+      class: 'border-dgreen',
       $init: true
     },
     {
@@ -128,48 +119,124 @@
       $init: true
     }
   ],
+  popactionItems: [
+    {
+      type: 'action',
+      label: '寮圭獥锛堣〃鍗曪級',
+      text: '寮圭獥锛堣〃鍗曪級',
+      value: 'pop',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '鎻愮ず妗�',
+      text: '鎻愮ず妗�',
+      value: 'prompt',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '鐩存帴鎵ц',
+      text: '鐩存帴鎵ц',
+      value: 'exec',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '瀵煎叆Excel',
+      text: '瀵煎叆Excel',
+      value: 'excelIn',
+      class: 'border-dgreen',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '瀵煎嚭Excel',
+      text: '瀵煎嚭Excel',
+      value: 'excelOut',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '鏍囩椤�',
+      text: '鏍囩椤�',
+      value: 'tab',
+      $init: true
+    },
+    {
+      type: 'action',
+      label: '鏂伴〉闈�',
+      text: '鏂伴〉闈�',
+      value: 'innerpage',
+      $init: true
+    }
+  ],
   columnItems: [
     {
-      type: 'columns',
+      type: 'col',
       label: '鏂囨湰',
       subType: 'text',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
       label: '鏁板瓧',
       subType: 'number',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
       label: '鍥剧墖',
       subType: 'picture',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
+      label: '瑙嗛',
+      subType: 'video',
+      $init: true
+    },
+    {
+      type: 'col',
       label: '閾炬帴',
       subType: 'link',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
       label: '澶氳鏂囨湰',
       subType: 'textarea',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
+      label: '鑷畾涔夊垪',
+      subType: 'custom',
+      $init: true
+    },
+    {
+      type: 'col',
       label: '鍚堝苟鍒�',
       subType: 'colspan',
-      url: ''
+      $init: true
     },
     {
-      type: 'columns',
+      type: 'col',
+      label: '鍏紡',
+      subType: 'formula',
+      $init: true
+    },
+    {
+      type: 'col',
       label: '搴忓彿',
       subType: 'index',
-      url: ''
+      $init: true
+    },
+    {
+      type: 'col',
+      label: '鎿嶄綔',
+      subType: 'action',
+      $init: true
     }
   ],
   tabItems: [

--
Gitblit v1.8.0