From e9c48bd7356462ba9257540b130a47a65ad1861d Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期四, 26 八月 2021 17:17:11 +0800
Subject: [PATCH] 2021-08-26

---
 src/components/normalform/modalform/index.scss                                    |   67 
 src/components/normalform/modalform/mkInput/index.jsx                             |   71 
 src/templates/zshare/modalform/index.jsx                                          |    4 
 src/menu/components/form/formaction/index.scss                                    |   17 
 src/mob/components/topbar/normal-navbar/index.scss                                |   10 
 src/tabviews/custom/components/chart/antv-scatter/index.scss                      |    4 
 src/menu/components/form/dragtitle/options.jsx                                    |   99 
 src/mob/mobshell/index.jsx                                                        |   15 
 src/menu/components/card/balcony/options.jsx                                      |  220 +
 src/locales/zh-CN/model.js                                                        |    1 
 src/menu/components/chart/antv-scatter/index.jsx                                  |   12 
 src/components/imgScale/index.jsx                                                 |  271 +
 src/tabviews/home/index.jsx                                                       |    2 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx |    2 
 src/tabviews/zshare/mutilform/mkInput/index.jsx                                   |    3 
 src/tabviews/zshare/mutilform/mkNumberInput/index.jsx                             |   13 
 src/pc/components/login/wrapsetting/settingform/index.jsx                         |   38 
 src/templates/zshare/editTable/index.scss                                         |    4 
 src/tabviews/zshare/actionList/changeuserbutton/index.jsx                         |   24 
 src/menu/datasource/verifycard/index.jsx                                          |    8 
 src/components/header/index.scss                                                  |   39 
 src/menu/components/card/cardcellcomponent/index.jsx                              |  188 
 src/menu/components/form/dragtitle/index.scss                                     |   50 
 src/menu/components/form/tab-form/index.jsx                                       |  715 +++
 src/pc/menushell/card.jsx                                                         |    5 
 src/menu/components/carousel/prop-card/index.jsx                                  |   23 
 src/menu/components/card/cardcomponent/index.jsx                                  |  133 
 src/templates/modalconfig/dragelement/index.scss                                  |    3 
 src/menu/components/chart/antv-pie/index.jsx                                      |   16 
 src/menu/components/card/prop-card/index.jsx                                      |  152 
 src/templates/zshare/customscript/index.jsx                                       |    2 
 src/menu/components/tabs/tabcomponents/card.jsx                                   |    8 
 src/menu/datasource/verifycard/utils.jsx                                          |    2 
 src/tabviews/custom/components/chart/antv-bar-line/index.jsx                      |   34 
 package-lock.json                                                                 |   72 
 src/assets/css/viewstyle.scss                                                     |   13 
 src/tabviews/custom/components/chart/antv-dashboard/index.jsx                     |   21 
 src/views/login/loginform.jsx                                                     |  125 
 src/menu/components/share/actioncomponent/dragaction/card.jsx                     |    1 
 src/tabviews/zshare/topSearch/index.jsx                                           |   16 
 src/menu/components/form/tab-form/index.scss                                      |   78 
 src/templates/comtableconfig/index.jsx                                            |    2 
 src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx                  |    2 
 src/menu/components/card/cardsimplecomponent/options.jsx                          |  163 
 src/menu/components/tabs/antv-tabs/dragabletabs.jsx                               |    4 
 src/mob/components/tabs/tabcomponents/index.jsx                                   |  173 
 src/api/index.js                                                                  |   14 
 src/mob/components/formdragelement/index.scss                                     |    2 
 src/tabviews/zshare/topSearch/index.scss                                          |   15 
 src/components/normalform/modalform/mkSelect/index.scss                           |    0 
 src/menu/components/chart/antv-dashboard/index.scss                               |   11 
 src/mob/mobshell/card.jsx                                                         |    7 
 src/mob/components/tabs/antv-tabs/index.scss                                      |  130 
 src/views/pcdesign/index.jsx                                                      |   72 
 src/mob/components/tabs/tabcomponents/card.jsx                                    |  111 
 src/locales/zh-CN/login.js                                                        |    4 
 src/menu/components/card/cardsimplecomponent/index.jsx                            |  106 
 src/menu/picturecontroller/editform/index.jsx                                     |    2 
 src/templates/modalconfig/source.jsx                                              |   24 
 src/mob/components/tabs/tabcomponents/index.scss                                  |   14 
 src/menu/components/form/dragtitle/card.jsx                                       |   40 
 src/pc/modulesource/option.jsx                                                    |    3 
 src/templates/zshare/editTable/index.jsx                                          |   27 
 src/tabviews/formtab/actionList/index.jsx                                         |    4 
 src/menu/components/editor/braft-editor/options.jsx                               |   87 
 src/mob/components/tabs/antv-tabs/index.jsx                                       |  419 ++
 src/tabviews/zshare/actionList/popupbutton/index.jsx                              |  129 
 src/tabviews/custom/components/tabs/antv-tabs/index.jsx                           |    5 
 src/tabviews/zshare/normalTable/index.jsx                                         |   49 
 src/views/menudesign/menuform/index.jsx                                           |   19 
 src/menu/components/chart/antv-bar/index.scss                                     |   13 
 src/menu/components/form/formaction/index.jsx                                     |   10 
 src/menu/components/tree/antd-tree/index.jsx                                      |    2 
 src/tabviews/subtabtable/index.jsx                                                |    9 
 src/tabviews/zshare/actionList/normalbutton/index.jsx                             |   37 
 src/views/menudesign/index.jsx                                                    |   28 
 src/menu/modulesource/option.jsx                                                  |    3 
 src/tabviews/custom/components/card/data-card/index.jsx                           |   66 
 src/templates/sharecomponent/actioncomponent/actionform/index.jsx                 |    2 
 src/menu/stylecontroller/styleInput/index.jsx                                     |    2 
 src/router/index.js                                                               |    5 
 src/menu/pastecontroller/index.jsx                                                |   34 
 src/menu/components/card/data-card/index.jsx                                      |  274 +
 src/mob/components/menubar/normal-menubar/options.jsx                             |   35 
 src/menu/components/chart/antv-dashboard/index.jsx                                |    8 
 src/menu/components/form/normal-form/options.jsx                                  |  106 
 src/views/appmanage/index.jsx                                                     |    4 
 src/menu/components/tabs/antv-tabs/index.jsx                                      |  153 
 src/templates/sharecomponent/searchcomponent/index.scss                           |    2 
 src/mob/components/tabs/antv-tabs/dragabletabs.jsx                                |  129 
 src/components/tabview/index.scss                                                 |    3 
 src/templates/subtableconfig/source.jsx                                           |    3 
 src/menu/components/share/sourcecomponent/inputform/index.jsx                     |    2 
 src/components/header/index.jsx                                                   |   56 
 src/tabviews/custom/components/chart/antv-bar-line/index.scss                     |    4 
 src/components/qrcode/index.jsx                                                   |    7 
 src/components/normalform/index.scss                                              |    0 
 src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx            |    6 
 src/tabviews/calendar/index.jsx                                                   |    9 
 src/views/appmanage/scriptform/index.jsx                                          |    1 
 src/mob/components/topbar/normal-navbar/options.jsx                               |  112 
 src/pc/quotecomponent/index.jsx                                                   |    6 
 src/tabviews/custom/components/carousel/data-card/index.jsx                       |    8 
 src/menu/components/code/sandbox/index.jsx                                        |   19 
 src/tabviews/zshare/mutilform/mkSelect/index.jsx                                  |    2 
 src/tabviews/custom/components/tree/antd-tree/index.scss                          |    2 
 src/menu/sysinterface/settingform/utils.jsx                                       |    2 
 src/templates/sharecomponent/settingcomponent/settingform/utils.jsx               |    4 
 src/menu/components/card/balcony/index.jsx                                        |   40 
 src/tabviews/zshare/actionList/tabbutton/index.jsx                                |   24 
 src/components/normalform/modalform/index.jsx                                     |  275 +
 src/menu/stylecontroller/index.jsx                                                |   78 
 src/templates/zshare/verifycard/index.jsx                                         |   17 
 src/menu/components/share/sourcecomponent/index.jsx                               |    4 
 src/tabviews/zshare/actionList/printbutton/index.jsx                              |  193 
 src/menu/components/card/cardcellcomponent/formconfig.jsx                         |   64 
 src/components/normalform/index.jsx                                               |   93 
 src/menu/components/editor/braft-editor/index.jsx                                 |   20 
 package.json                                                                      |    1 
 src/tabviews/zshare/actionList/popupbutton/index.scss                             |   24 
 src/tabviews/zshare/topSearch/advanceform/index.jsx                               |    7 
 src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx           |    6 
 src/mob/components/tabs/antv-tabs/options.jsx                                     |   63 
 src/tabviews/custom/components/form/normal-form/index.scss                        |   13 
 src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss           |    7 
 src/menu/components/card/cardcellcomponent/dragaction/action.jsx                  |    6 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx                  |   35 
 src/components/normalform/modalform/mkInput/index.scss                            |   27 
 src/templates/modalconfig/index.jsx                                               |   16 
 src/tabviews/zshare/actionList/newpagebutton/index.jsx                            |   22 
 src/mob/modalconfig/index.jsx                                                     |   28 
 src/tabviews/commontable/index.jsx                                                |   13 
 src/locales/en-US/login.js                                                        |    4 
 src/menu/components/form/formaction/formconfig.jsx                                |   24 
 src/menu/components/card/cardsimplecomponent/index.scss                           |    0 
 src/tabviews/custom/components/card/cardcellList/index.jsx                        |   81 
 src/tabviews/custom/components/card/prop-card/index.jsx                           |   16 
 src/menu/components/carousel/data-card/options.jsx                                |  134 
 src/menu/components/card/data-card/options.jsx                                    |  174 
 src/mob/components/menubar/normal-menubar/index.jsx                               |   32 
 src/tabviews/custom/components/table/normal-table/index.jsx                       |   34 
 src/components/normalform/modalform/mkNumberInput/index.jsx                       |   62 
 src/menu/components/chart/antv-pie/index.scss                                     |   11 
 src/menu/components/tabs/antv-tabs/options.jsx                                    |   63 
 src/mob/components/menubar/normal-menubar/menucomponent/options.jsx               |  179 
 src/tabviews/custom/components/chart/antv-pie/index.jsx                           |   22 
 src/menu/components/group/groupcomponents/card.jsx                                |    8 
 src/components/breadview/index.jsx                                                |   61 
 src/tabviews/zshare/mutilform/index.jsx                                           |   14 
 src/menu/components/share/actioncomponent/actionform/index.jsx                    |   20 
 src/components/normalform/modalform/mkRadio/index.scss                            |    0 
 src/tabviews/custom/index.jsx                                                     |  126 
 src/tabviews/zshare/mutilform/mkTextArea/index.jsx                                |    3 
 src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx           |    2 
 src/menu/components/table/normal-table/columns/index.jsx                          |    5 
 src/views/appmanage/submutilform/index.jsx                                        |   19 
 src/menu/popview/index.jsx                                                        |   18 
 src/tabviews/treepage/index.jsx                                                   |    5 
 src/pc/components/navbar/normal-navbar/index.jsx                                  |    7 
 src/tabviews/custom/components/chart/antv-dashboard/index.scss                    |    4 
 src/tabviews/custom/components/chart/antv-scatter/index.jsx                       |   20 
 src/menu/components/card/cardcellcomponent/dragaction/index.scss                  |   17 
 src/tabviews/custom/components/form/tab-form/index.scss                           |   98 
 src/menu/components/group/groupcomponents/index.jsx                               |    3 
 src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx            |   18 
 src/menu/components/form/formaction/actionform/index.jsx                          |    3 
 src/menu/components/card/cardcellcomponent/dragaction/index.jsx                   |    3 
 src/menu/components/form/normal-form/index.jsx                                    |   64 
 src/pc/menushell/index.jsx                                                        |   13 
 src/templates/zshare/verifycard/billcodeform/index.jsx                            |    9 
 src/tabviews/zshare/actionList/exceloutbutton/index.jsx                           |   27 
 src/menu/components/card/cardcellcomponent/dragaction/card.jsx                    |   14 
 src/menu/components/card/balcony/index.scss                                       |   10 
 src/menu/components/chart/antv-bar/index.jsx                                      |   29 
 src/tabviews/subtable/index.jsx                                                   |   13 
 src/views/appmanage/submutilform/index.scss                                       |    8 
 src/menu/components/carousel/data-card/index.jsx                                  |   23 
 src/tabviews/custom/components/card/table-card/index.jsx                          |    8 
 src/components/imgScale/index.scss                                                |  238 +
 src/mob/components/menubar/normal-menubar/menucomponent/index.jsx                 |   95 
 src/menu/components/chart/antv-scatter/index.scss                                 |   11 
 src/pc/components/login/normal-login/loginform.jsx                                |    9 
 src/templates/comtableconfig/source.jsx                                           |    3 
 src/templates/sharecomponent/columncomponent/dragcolumn/card.jsx                  |    1 
 src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx             |    2 
 src/menu/components/search/main-search/index.jsx                                  |   10 
 src/templates/sharecomponent/fieldscomponent/index.scss                           |    3 
 src/mob/components/topbar/normal-navbar/index.jsx                                 |   37 
 src/views/login/index.scss                                                        |   41 
 src/templates/sharecomponent/actioncomponent/dragaction/index.jsx                 |    1 
 src/pc/components/login/normal-login/index.scss                                   |   24 
 src/views/login/index.jsx                                                         |  166 
 src/mob/components/navbar/normal-navbar/index.jsx                                 |    5 
 src/utils/utils-datamanage.js                                                     |   55 
 src/menu/sysinterface/settingform/simplescript/index.jsx                          |    2 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx  |    4 
 src/menu/components/card/table-card/index.jsx                                     |  179 
 src/utils/utils-custom.js                                                         |   31 
 src/views/main/index.jsx                                                          |    6 
 src/mob/modulesource/option.jsx                                                   |    3 
 src/tabviews/custom/components/share/normalTable/index.scss                       |    3 
 src/tabviews/zshare/actionList/excelInbutton/index.jsx                            |   22 
 public/options.json                                                               |    1 
 src/menu/components/tabs/tabcomponents/index.jsx                                  |    3 
 src/views/pcdesign/index.scss                                                     |   21 
 src/templates/formtabconfig/source.jsx                                            |    2 
 src/menu/menushell/card.jsx                                                       |    5 
 src/menu/components/code/sandbox/options.jsx                                      |   62 
 src/templates/modalconfig/dragelement/card.jsx                                    |    6 
 src/views/mobdesign/index.jsx                                                     |   60 
 src/mob/searchconfig/searchdragelement/index.scss                                 |    2 
 src/menu/datasource/verifycard/columnform/index.jsx                               |   31 
 src/components/normalform/modalform/mkSelect/index.jsx                            |  140 
 src/mob/colorsketch/index.jsx                                                     |    2 
 src/menu/components/share/actioncomponent/formconfig.jsx                          |   68 
 src/tabviews/custom/components/group/normal-group/index.jsx                       |   28 
 src/components/normalform/modalform/mkRadio/index.jsx                             |   94 
 src/menu/components/editor/braft-editor/editorcontent/index.scss                  |    3 
 src/views/design/sidemenu/config.jsx                                              |    8 
 src/tabviews/custom/components/carousel/prop-card/index.jsx                       |   16 
 src/templates/comtableconfig/menuform/index.scss                                  |    7 
 src/tabviews/zshare/topSearch/advanceform/index.scss                              |   11 
 src/menu/components/table/normal-table/index.jsx                                  |    3 
 src/menu/components/card/cardcomponent/options.jsx                                |  152 
 src/menu/components/group/normal-group/index.jsx                                  |    2 
 src/tabviews/zshare/normalTable/index.scss                                        |    2 
 src/components/normalform/modalform/mkNumberInput/index.scss                      |   27 
 src/components/tabview/index.jsx                                                  |   56 
 src/components/normalform/modalform/styleInput/index.jsx                          |  144 
 src/tabviews/custom/components/card/balcony/index.scss                            |   11 
 src/menu/datasource/verifycard/customscript/index.jsx                             |    2 
 src/tabviews/custom/components/chart/antv-pie/index.scss                          |    4 
 src/tabviews/zshare/topSearch/mkSelect/index.jsx                                  |    2 
 src/menu/components/form/dragtitle/index.jsx                                      |   11 
 src/tabviews/rolemanage/index.scss                                                |   41 
 src/templates/formtabconfig/dragelement/index.jsx                                 |    1 
 src/templates/sharecomponent/columncomponent/index.jsx                            |    2 
 src/templates/sharecomponent/columncomponent/colspanform/index.jsx                |    4 
 src/locales/en-US/model.js                                                        |    1 
 src/views/mobdesign/index.scss                                                    |   24 
 src/tabviews/custom/components/share/normalTable/index.jsx                        |   52 
 src/tabviews/custom/components/card/data-card/index.scss                          |    2 
 src/menu/components/search/main-search/index.scss                                 |   14 
 src/utils/utils.js                                                                |   25 
 src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx             |    1 
 src/index.js                                                                      |    5 
 src/tabviews/custom/components/form/tab-form/index.jsx                            |  362 +
 src/menu/components/share/pastecomponent/index.jsx                                |    2 
 src/templates/comtableconfig/menuform/index.jsx                                   |   15 
 src/tabviews/custom/components/share/tabtransfer/index.jsx                        |   28 
 src/tabviews/rolemanage/index.jsx                                                 |  677 ++
 src/templates/zshare/formconfig.jsx                                               |   67 
 src/tabviews/custom/components/form/normal-form/index.jsx                         |   71 
 src/mob/components/formdragelement/card.jsx                                       |    2 
 src/pc/components/login/normal-login/index.jsx                                    |   11 
 /dev/null                                                                         |   20 
 src/menu/modalconfig/index.jsx                                                    |   28 
 src/tabviews/custom/components/card/balcony/index.jsx                             |   72 
 src/tabviews/custom/components/card/cardcellList/index.scss                       |   22 
 src/components/normalform/modalform/styleInput/index.scss                         |   11 
 260 files changed, 9,651 insertions(+), 1,817 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 2a72c49..a907381 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -27,9 +27,54 @@
       "integrity": "sha512-LrX0OGZtW+W6iLnTAqnTaoIsRelYeuLZWsrmBJFUXDALQphPsN8cE5DCsmoSlL0QYb94BQxINiuS70Ar/8BNgA=="
     },
     "@ant-design/icons": {
-      "version": "2.1.1",
-      "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
-      "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-4.6.2.tgz",
+      "integrity": "sha512-QsBG2BxBYU/rxr2eb8b2cZ4rPKAPBpzAR+0v6rrZLp/lnyvflLH3tw1vregK+M7aJauGWjIGNdFmUfpAOtw25A==",
+      "requires": {
+        "@ant-design/colors": "^6.0.0",
+        "@ant-design/icons-svg": "^4.0.0",
+        "@babel/runtime": "^7.11.2",
+        "classnames": "^2.2.6",
+        "rc-util": "^5.9.4"
+      },
+      "dependencies": {
+        "@ant-design/colors": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/@ant-design/colors/-/colors-6.0.0.tgz",
+          "integrity": "sha512-qAZRvPzfdWHtfameEGP2Qvuf838NhergR35o+EuVyB5XvSA98xod5r4utvi4TJ3ywmevm290g9nsCG5MryrdWQ==",
+          "requires": {
+            "@ctrl/tinycolor": "^3.4.0"
+          }
+        },
+        "@babel/runtime": {
+          "version": "7.14.8",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.14.8.tgz",
+          "integrity": "sha512-twj3L8Og5SaCRCErB4x4ajbvBIVV77CGeFglHpeg5WC5FF8TZzBWXtTJ4MqaD9QszLYTtr+IsaAL2rEUevb+eg==",
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
+        },
+        "rc-util": {
+          "version": "5.13.2",
+          "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.13.2.tgz",
+          "integrity": "sha512-eYc71XXGlp96RMzg01Mhq/T3BL6OOVTDSS0urFEuvpi+e7slhJRhaHGCKy2hqJm18m9ff7VoRoptplKu60dYog==",
+          "requires": {
+            "@babel/runtime": "^7.12.5",
+            "react-is": "^16.12.0",
+            "shallowequal": "^1.1.0"
+          }
+        },
+        "react-is": {
+          "version": "16.13.1",
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+          "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
+        },
+        "regenerator-runtime": {
+          "version": "0.13.9",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz",
+          "integrity": "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
+        }
+      }
     },
     "@ant-design/icons-react": {
       "version": "2.0.1",
@@ -39,6 +84,11 @@
         "@ant-design/colors": "^3.1.0",
         "babel-runtime": "^6.26.0"
       }
+    },
+    "@ant-design/icons-svg": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@ant-design/icons-svg/-/icons-svg-4.1.0.tgz",
+      "integrity": "sha512-Fi03PfuUqRs76aI3UWYpP864lkrfPo0hluwGqh7NJdLhvH4iRDc3jbJqZIvRDLHKbXrvAfPPV3+zjUccfFvWOQ=="
     },
     "@antv/adjust": {
       "version": "0.2.3",
@@ -1540,6 +1590,11 @@
       "version": "9.0.1",
       "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-9.0.1.tgz",
       "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA=="
+    },
+    "@ctrl/tinycolor": {
+      "version": "3.4.0",
+      "resolved": "https://registry.npmjs.org/@ctrl/tinycolor/-/tinycolor-3.4.0.tgz",
+      "integrity": "sha512-JZButFdZ1+/xAfpguQHoabIXkcqRRKpMrWKBkpEZZyxfY9C1DpADFB8PEqGSTeFr135SaTRfKqGKx5xSCLI7ZQ=="
     },
     "@fast-csv/format": {
       "version": "4.3.5",
@@ -3125,6 +3180,11 @@
         "warning": "~4.0.3"
       },
       "dependencies": {
+        "@ant-design/icons": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/@ant-design/icons/-/icons-2.1.1.tgz",
+          "integrity": "sha512-jCH+k2Vjlno4YWl6g535nHR09PwCEmTBKAG6VqF+rhkrSPRLfgpU2maagwbZPLjaHuU5Jd1DFQ2KJpQuI6uG8w=="
+        },
         "rc-animate": {
           "version": "2.11.1",
           "resolved": "https://registry.npmjs.org/rc-animate/-/rc-animate-2.11.1.tgz",
@@ -4733,9 +4793,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001191",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001191.tgz",
-      "integrity": "sha512-xJJqzyd+7GCJXkcoBiQ1GuxEiOBCLQ0aVW9HMekifZsAVGdj5eJ4mFB9fEhSHipq9IOk/QXFJUiIr9lZT+EsGw=="
+      "version": "1.0.30001251",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001251.tgz",
+      "integrity": "sha512-HOe1r+9VkU4TFmnU70z+r7OLmtR+/chB1rdcJUeQlAinjEeb0cKL20tlAtOagNZhbrtLnCvV19B4FmF1rgzl6A=="
     },
     "capture-exit": {
       "version": "2.0.0",
diff --git a/package.json b/package.json
index aaef24f..b31ef50 100644
--- a/package.json
+++ b/package.json
@@ -3,6 +3,7 @@
   "version": "0.1.0",
   "private": true,
   "dependencies": {
+    "@ant-design/icons": "^4.6.2",
     "@antv/data-set": "^0.11.4",
     "@antv/g2": "^4.1.14",
     "@antv/util": "^2.0.13",
diff --git a/public/options.json b/public/options.json
index e213888..bcb2413 100644
--- a/public/options.json
+++ b/public/options.json
@@ -10,6 +10,7 @@
   "defaultLang": "zh-CN",
   "WXAppID": "",
   "debugger": false,
+  "licenseKey": "E1A8FE",
   "host": "http://bms-test.kresstools.cn",
   "service": "oc/"
 }
\ No newline at end of file
diff --git a/src/api/index.js b/src/api/index.js
index c5f65d9..b020f90 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -14,7 +14,7 @@
   1585192949946f3et2ts8tn82krmumdf,15855615451212m12ip23vpcm79kloro,1587005717541lov40vg61q7l1rbveon,1590458676585agbbr63t6ihighg2i1g,1602315375262ikd33ii0nii34pt861o,1582771068837vsv54a089lgp45migbg,
   1582777675954ifu05upurs465omoth7,158294809668898cklbv6c5bou8e1fpu,1584676379094iktph45fb8imhg96bql,1584695125339vo5g7iqgfn01qmrd6s2,1584699661372vhmpp9dn9foo0eob722,15848421131551gg04ie8sitsd3f7467,
   1589782279158ngr675kk3oksin35sul,1589788042787ffdt9hle4s45k9r1nvs,15900310928174dro07ihfckghpb5h13,1594095599055qicg2eb642v5qglhnuo,1599613340050c8nu6rbst9d4emnnbsq,1577972969199lei1g0qkvlh4tkc908m,
-  1578479100252lfbp29v1kafk4s4q4ig,1577971621421tg4v0i1ur8873k7e0ob,1577929944419lgc5h3hepum765e2k7u,1588493493409k9guqp067d31lu7blsv`
+  1578479100252lfbp29v1kafk4s4q4ig,1577971621421tg4v0i1ur8873k7e0ob,1577929944419lgc5h3hepum765e2k7u,1588493493409k9guqp067d31lu7blsv,15827879285193g85m3i2uprektpgmpf`
 
 if (window.openDatabase) {
   CacheUtils.openWebSql(options.sysType)
@@ -169,7 +169,7 @@
   /**
    * @description 娓稿鐧诲綍
    */
-  getTouristMsg () {
+  getTouristMsg (appid, openid, memberid, scanId) {
     let _SessionUid = localStorage.getItem('SessionUid')
 
     if (!_SessionUid) { // 鎵嬪姩娓呴櫎SessionUid鏃讹紝瀹炴椂鐢熸垚
@@ -188,6 +188,14 @@
     param.secretkey = md5(param.LText + 'mingke' + param.timestamp)
 
     param.appkey = window.GLOB.appkey || ''
+
+    if (appid) {
+      param.binding_type = 'mk'
+      param.thd_party_member_id = memberid
+      param.thd_party_openid = openid
+      param.thd_party_appid = appid
+      param.id = scanId
+    }
 
     let url = '/webapi/dologon/s_visitor_login'
     if (window.GLOB.mainSystemApi) {
@@ -487,7 +495,7 @@
       appkey: window.GLOB.appkey
     }
 
-    let url = window.GLOB.mainSystemApi
+    let url = window.GLOB.mainSystemApi || '/webapi/dostars'
     param = this.encryptParam(param)
 
     return axios({
diff --git a/src/assets/css/viewstyle.scss b/src/assets/css/viewstyle.scss
index e1f8bcc..44d158d 100644
--- a/src/assets/css/viewstyle.scss
+++ b/src/assets/css/viewstyle.scss
@@ -50,6 +50,19 @@
     }
   }
 
+  .menu-board {
+    .menu-wrap {
+      .title {
+        color: $color6;
+      }
+      .menu-detail {
+        div:hover {
+          color: $color5;
+        }
+      }
+    }
+  }
+
   #root > .mk-main-view {
     > .header-container {
       background: $bg1;
diff --git a/src/components/breadview/index.jsx b/src/components/breadview/index.jsx
index 8d25025..ff506c3 100644
--- a/src/components/breadview/index.jsx
+++ b/src/components/breadview/index.jsx
@@ -1,16 +1,18 @@
 import React, {Component} from 'react'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { BackTop, Breadcrumb, Icon} from 'antd'
+import { BackTop, Breadcrumb, Icon, notification} from 'antd'
 import moment from 'moment'
 import 'moment/locale/zh-cn'
 
 import asyncComponent from '@/utils/asyncLoadComponent'
 import NotFount from '@/components/404'
+import options from '@/store/options.js'
 import mzhCN from '@/locales/zh-CN/main.js'
 import menUS from '@/locales/en-US/main.js'
 import MKEmitter from '@/utils/events.js'
-
+import { initActionPermission } from '@/store/action'
+import Api from '@/api'
 import './index.scss'
 
 const Home = asyncComponent(() => import('@/tabviews/home'))
@@ -34,14 +36,59 @@
   state = {
     tabview: null, // 鏍囩
     dict: sessionStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
-    hasNavBar: window.GLOB.navBar !== 'linkage'
+    hasNavBar: window.GLOB.navBar === 'linkage_navigation'
   }
 
   refreshTabview = () => {
     const { tabview } = this.state
     window.GLOB.CacheMap = new Map()
 
-    MKEmitter.emit('reloadMenuView', tabview.MenuID)
+    if (options.sysType === 'local' && window.GLOB.systemType !== 'production') {
+      let roledefer = new Promise(resolve => {
+        Api.getSystemConfig({
+          func: 's_Get_TrdMenu_Role',
+          edition_type: 'A',
+          pro_sys: ''
+        }).then(result => {
+          if (!result) return
+          if (!result.status) {
+            notification.error({
+              top: 92,
+              message: result.message,
+              duration: 10
+            })
+          } else {
+            let _permAction = {loaded: true} // 鎸夐挳鏉冮檺
+    
+            if (result.UserRoles_Menu) {
+              result.UserRoles_Menu.forEach(menu => {
+                if (!menu.MenuID) return
+                _permAction[menu.MenuID] = true
+              })
+            }
+  
+            this.props.initActionPermission(_permAction)
+          }
+
+          resolve()
+        })
+      })
+  
+      // 鑾峰彇涓昏彍鍗曞弬鏁�
+      let menudefer = new Promise(resolve => {
+        Api.getAppVersion().then(() => {
+          resolve()
+        }, () => {
+          resolve()
+        })
+      })
+      
+      Promise.all([roledefer, menudefer]).then(() => {
+        MKEmitter.emit('reloadMenuView', tabview.MenuID)
+      })
+    } else {
+      MKEmitter.emit('reloadMenuView', tabview.MenuID)
+    }
   }
 
   selectcomponent = (view) => {
@@ -154,8 +201,10 @@
   }
 }
 
-const mapDispatchToProps = () => {
-  return {}
+const mapDispatchToProps = (dispatch) => {
+  return {
+    initActionPermission: (permAction) => dispatch(initActionPermission(permAction)),
+  }
 }
 
 export default connect(mapStateToProps, mapDispatchToProps)(BreadView)
\ No newline at end of file
diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx
index 0fab405..9ba0be0 100644
--- a/src/components/header/index.jsx
+++ b/src/components/header/index.jsx
@@ -49,7 +49,8 @@
     oriVersion: '',
     newVersion: '',
     debug: sessionStorage.getItem('debug') === 'true',
-    navBar: ['linkage_navigation', 'linkage'].includes(window.GLOB.navBar) ? 'topmenu' : ''
+    navBar: ['linkage_navigation', 'linkage', 'menu_board', 'menu_board_navigation'].includes(window.GLOB.navBar) ? 'topmenu' : '',
+    menuType: window.GLOB.navBar
   }
 
   handleCollapse = () => {
@@ -308,8 +309,9 @@
                 ParentNames: [fst.MenuName, snd.MenuName],
                 MenuNo: trd.MenuNo,
                 EasyCode: trd.EasyCode,
-                type: 'CommonTable',            // 榛樿鍊间负甯哥敤琛�
-                OpenType: 'newtab'              // 鎵撳紑鏂瑰紡
+                type: 'CommonTable',
+                OpenType: 'newtab',
+                hidden: 'false'
               }
   
               if (trd.LinkUrl && iframes.includes(trd.LinkUrl.split('?')[0])) {
@@ -324,6 +326,7 @@
 
                 trdItem.type = trdItem.PageParam.Template || trdItem.type
                 trdItem.OpenType = trdItem.PageParam.OpenType || trdItem.OpenType
+                trdItem.hidden = trdItem.PageParam.hidden || trdItem.hidden
 
                 if (trdItem.type === 'NewPage') {
                   trdItem.src = trdItem.PageParam.url || ''
@@ -356,6 +359,8 @@
               return trdItem
             })
           }
+
+          sndItem.children = sndItem.children.filter(item => item.hidden !== 'true')
 
           return sndItem
         })
@@ -668,7 +673,7 @@
       } else if (menu.OpenType === 'blank') {
         menu.selected = true
         this.props.modifyTabview([menu])
-      } else if (this.state.navBar === 'topmenu') {
+      } else if (this.state.navBar === 'topmenu' && this.state.menuType !== 'menu_board_navigation') {
         menu.selected = true
         this.props.modifyTabview([menu])
       } else {
@@ -697,7 +702,7 @@
 
   render () {
     const { mainMenu, collapse } = this.props
-    const { thdMenuList, searchkey, oriVersion, newVersion, debug, menulist, navBar } = this.state
+    const { thdMenuList, searchkey, oriVersion, newVersion, debug, menulist, navBar, menuType } = this.state
 
     const menu = (
       <Menu className="header-dropdown">
@@ -737,8 +742,7 @@
             })}
           </ul> : null
         }
-        {/* 姝e父鑿滃崟 */}
-        {navBar === 'topmenu' && menulist ?
+        {navBar === 'topmenu' && menuType !== 'menu_board' && menuType !== 'menu_board_navigation' && menulist ?
           <ul className="header-menu vertical-menu">{
             menulist.map(item => {
               if (item.children && item.children.length > 0) {
@@ -781,6 +785,44 @@
             })}
           </ul> : null
         }
+        {navBar === 'topmenu' && (menuType === 'menu_board' || menuType === 'menu_board_navigation') && menulist ?
+          <ul className="header-menu vertical-menu">{
+            menulist.map(item => {
+              if (item.children && item.children.length > 0) {
+                return (
+                  <Dropdown key={item.MenuID} placement="bottomCenter" overlayClassName="vertical-dropdown-menu" overlay={
+                    <div className="menu-board">
+                      {item.children.map(cell => {
+                        return (
+                          <div className="menu-wrap" key={cell.MenuID}>
+                            <div className="title" onClick={e => e.stopPropagation()}>{cell.MenuName}</div>
+                            <div className="menu-detail">
+                              {cell.children && cell.children.map(m => (
+                                <div key={m.MenuID} title={m.MenuName} onClick={() => {this.changeVerMenu(m)}}>
+                                  {m.MenuName}
+                                </div>
+                              ))}
+                            </div>
+                          </div>
+                        )
+                      })}
+                    </div>
+                  }>
+                    <li>
+                      <span>{item.MenuName}</span>
+                    </li>
+                  </Dropdown>
+                )
+              } else {
+                return (
+                  <li key={item.MenuID} onClick={() => {this.changeVerMenu(item, 'first')}}>
+                    <span>{item.MenuName}</span>
+                  </li>
+                )
+              }
+            })}
+          </ul> : null
+        }
         {/* 澶村儚銆佺敤鎴峰悕 */}
         <Dropdown className="header-setting" overlay={menu}>
           <div>
diff --git a/src/components/header/index.scss b/src/components/header/index.scss
index 77b3ade..7c4e021 100644
--- a/src/components/header/index.scss
+++ b/src/components/header/index.scss
@@ -178,3 +178,42 @@
     padding-right: 30px;
   }
 }
+.menu-board {
+  background: #ffffff;
+  padding: 20px 50px;
+  border-radius: 4px;
+  box-shadow: 0 0 3px #959595;
+  position: relative;
+  left: 11.8%;
+  .menu-wrap {
+    .title {
+      color: #1890ff;
+      font-size: 15px;
+      font-weight: 600;
+    }
+    .menu-detail {
+      max-width: 60vw;
+      padding: 5px 0 10px 15px;
+      div {
+        float: left;
+        margin-bottom: 5px;
+        width: 120px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        cursor: pointer;
+      }
+      div:hover {
+        color: #40a9ff;
+      }
+      div:not(:last-child) {
+        margin-right: 20px;
+      }
+    }
+    .menu-detail::after {
+      content: ' ';
+      display: block;
+      clear: both;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/imgScale/index.jsx b/src/components/imgScale/index.jsx
new file mode 100644
index 0000000..9d82112
--- /dev/null
+++ b/src/components/imgScale/index.jsx
@@ -0,0 +1,271 @@
+import React, {Component} from 'react'
+import {
+  RotateLeftOutlined,
+  RotateRightOutlined,
+  CloseOutlined,
+  ZoomOutOutlined,
+  ZoomInOutlined,
+  LeftOutlined,
+  RightOutlined
+} from '@ant-design/icons'
+
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+class ImgScale extends Component {
+  state = {
+    className: 'close',
+    url: '',
+    list: [],
+    scale: 1,
+    rotate: 0,
+    index: 0
+  }
+
+  imgNode = null
+  wrapNode = null
+  visible = false
+  originPositionRef = null
+  position = {x: 0, y: 0}
+
+  componentDidMount () {
+    MKEmitter.addListener('mkImageScale', this.mkImageScale)
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkImageScale', this.mkImageScale)
+  }
+
+  mkImageScale = (src, list = []) => {
+    const { url } = this.state
+
+    if (url || !src) return
+
+    let index = 0
+    let _list = []
+
+    if (list.length > 1) {
+      index = list.indexOf(src)
+      _list = list
+    }
+
+    this.setState({url: src, className: 'opening', list: _list, scale: 1, rotate: 0, index, position: {x: 0, y: 0}})
+    setTimeout(() => {
+      this.setState({className: 'open'})
+
+      window.addEventListener('mouseup', this.onMouseUp)
+      window.addEventListener('mousemove', this.onMouseMove)
+      window.addEventListener('wheel', this.onWheelMove, {passive: false})
+      this.imgNode && this.imgNode.addEventListener('mousedown', this.onMouseDown)
+      this.wrapNode.style.transform = 'translate3d(0px, 0px, 0px)'
+
+      this.position = {x: 0, y: 0}
+      this.originPositionRef = {
+        originX: 0,
+        originY: 0,
+        deltaX: 0,
+        deltaY: 0
+      }
+    }, 300)
+  }
+
+  onMouseDown = (e) => {
+    e.preventDefault()
+    e.stopPropagation()
+
+    this.visible = true
+
+    this.originPositionRef.deltaX = e.pageX
+    this.originPositionRef.deltaY = e.pageY
+    this.originPositionRef.originX = this.position.x
+    this.originPositionRef.originY = this.position.y
+  }
+
+  onMouseUp = (e) => {
+    const { scale, rotate } = this.state
+    this.visible = false
+
+    if (!this.imgNode) return
+
+    let isRotate = rotate % 180 !== 0
+
+    let nodeWidth = this.imgNode.offsetWidth * scale
+    let nodeHeight = this.imgNode.offsetHeight * scale
+    let winWidth = window.innerWidth
+    let winHeight = window.innerHeight
+    let width = isRotate ? nodeHeight : nodeWidth
+    let height = isRotate ? nodeWidth : nodeHeight
+
+    let x = this.position.x
+    let y = this.position.y
+
+    if (width <= winWidth && height <= winHeight) {
+      x = 0
+      y = 0
+    } else {
+      x = this.fixPoint(x, width, winWidth)
+      y = this.fixPoint(y, height, winHeight)
+    }
+
+    this.position.x = x
+    this.position.y = y
+
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  fixPoint(start, width, clientWidth) {
+    let startAddWidth = start + width
+    let offsetStart = (width - clientWidth) / 2
+  
+    if (width > clientWidth) {
+      if (start > 0) {
+        return offsetStart
+      }
+  
+      if (start < 0 && startAddWidth < clientWidth) {
+        return -offsetStart
+      }
+    } else if (start < 0 || startAddWidth > clientWidth) {
+      return start < 0 ? offsetStart : -offsetStart
+    }
+  
+    return start
+  }
+
+  onMouseMove = (e) => {
+    if (!this.visible) return
+
+    e.preventDefault()
+    e.stopPropagation()
+
+    let x = e.pageX - this.originPositionRef.deltaX
+    let y = e.pageY - this.originPositionRef.deltaY
+
+    this.position.x = this.originPositionRef.originX + x
+    this.position.y = this.originPositionRef.originY + y
+
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  onWheelMove = (e) => {
+    e.preventDefault()
+    let delta = -e.wheelDelta || e.deltaY
+
+    if (delta > 0) {
+      this.zoomOut()
+    } else if(delta < 0) {
+      this.zoomIn()
+    }
+
+    this.position.x = 0
+    this.position.y = 0
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  close = () => {
+    this.setState({className: 'closeing'})
+
+    this.visible = false
+    
+    this.imgNode && this.imgNode.removeEventListener('mousedown', this.onMouseDown)
+    window.removeEventListener('mouseup', this.onMouseUp)
+    window.removeEventListener('mousemove', this.onMouseMove)
+    window.removeEventListener('wheel', this.onWheelMove)
+
+    setTimeout(() => {
+      this.wrapNode.style.transform = 'translate3d(0px, 0px, 0px)'
+      this.setState({className: 'close', url: ''})
+    }, 300)
+  }
+
+  zoomIn = () => {
+    this.setState({scale: this.state.scale + 1})
+
+    this.position.x = 0
+    this.position.y = 0
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  zoomOut = () => {
+    const { scale } = this.state
+
+    if (scale === 1) return
+
+    this.setState({scale: scale - 1})
+
+    this.position.x = 0
+    this.position.y = 0
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  rotateRight = () => {
+    this.setState({rotate: this.state.rotate + 90})
+  }
+
+  rotateLeft = () => {
+    this.setState({rotate: this.state.rotate - 90})
+  }
+
+  prev = () => {
+    const { list, index } = this.state
+    
+    this.setState({url: list[index - 1], index: index - 1, scale: 1, rotate: 0})
+    this.position.x = 0
+    this.position.y = 0
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  next = () => {
+    const { list, index } = this.state
+    
+    this.setState({url: list[index + 1], index: index + 1, scale: 1, rotate: 0})
+    this.position.x = 0
+    this.position.y = 0
+    this.wrapNode.style.transform = `translate3d(${this.position.x}px, ${this.position.y}px, 0px)`
+  }
+
+  render() {
+    const { index, url, scale, rotate, className, list } = this.state
+
+    return (
+      <div className={'mk-preview ' + className}>
+        <div className="mk-image-preview-mask"></div>
+        <div className="mk-image-preview-wrap">
+          <div className="mk-image-preview">
+            <div className="mk-image-preview-content">
+              <div className="mk-image-preview-body">
+                <ul className="mk-image-preview-operations">
+                  <li className="mk-image-preview-operations-operation" onClick={this.close}>
+                    <CloseOutlined />
+                  </li>
+                  <li className="mk-image-preview-operations-operation" onClick={this.zoomIn}>
+                    <ZoomInOutlined />
+                  </li>
+                  <li className={'mk-image-preview-operations-operation ' + (scale === 1 ? 'mk-image-preview-operations-operation-disabled' : '')} onClick={this.zoomOut}>
+                    <ZoomOutOutlined />
+                  </li>
+                  <li className="mk-image-preview-operations-operation" onClick={this.rotateRight}>
+                    <RotateRightOutlined />
+                  </li>
+                  <li className="mk-image-preview-operations-operation" onClick={this.rotateLeft}>
+                    <RotateLeftOutlined />
+                  </li>
+                </ul>
+                <div ref={e => (this.wrapNode = e)}  className="mk-image-preview-img-wrapper">
+                  {url ? <img ref={e => (this.imgNode = e)} className="mk-image-preview-img" alt="" src={url} style={{transform: `scale3d(${scale}, ${scale}, 1) rotate(${rotate}deg)`}}/> : null}
+                </div>
+                {index ? <LeftOutlined className="mk-image-preview-switch-left" onClick={this.prev}/> : null}
+                {list.length > index + 1 ? <RightOutlined className="mk-image-preview-switch-right" onClick={this.next}/> : null}
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default ImgScale
\ No newline at end of file
diff --git a/src/components/imgScale/index.scss b/src/components/imgScale/index.scss
new file mode 100644
index 0000000..8d8c762
--- /dev/null
+++ b/src/components/imgScale/index.scss
@@ -0,0 +1,238 @@
+
+.mk-image-preview {
+  pointer-events: none;
+  height: 100%;
+  text-align: center;
+}
+.mk-image-preview.zoom-enter,
+.mk-image-preview.zoom-appear {
+  -webkit-transform: none;
+          transform: none;
+  opacity: 0;
+  -webkit-animation-duration: 0.3s;
+          animation-duration: 0.3s;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+}
+.mk-image-preview-mask {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  z-index: 1100;
+  height: 100%;
+  background-color: rgba(0, 0, 0, 0.65);
+}
+.mk-image-preview-mask-hidden {
+  display: none;
+}
+.mk-image-preview-wrap {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  outline: 0;
+  -webkit-overflow-scrolling: touch;
+}
+.mk-image-preview-body {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: hidden;
+}
+.mk-image-preview-img {
+  max-width: 100%;
+  max-height: 100%;
+  vertical-align: middle;
+  -webkit-transform: scale3d(1, 1, 1);
+          transform: scale3d(1, 1, 1);
+  cursor: -webkit-grab;
+  cursor: grab;
+  -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s, -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none;
+  pointer-events: auto;
+}
+.mk-image-preview-img-wrapper {
+  position: absolute;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  -webkit-transition: -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+  transition: transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s, -webkit-transform 0.3s cubic-bezier(0.215, 0.61, 0.355, 1) 0s;
+}
+.mk-image-preview-img-wrapper::before {
+  display: inline-block;
+  width: 1px;
+  height: 50%;
+  margin-right: -1px;
+  content: '';
+}
+.mk-image-preview-moving .mk-image-preview-img {
+  cursor: -webkit-grabbing;
+  cursor: grabbing;
+}
+.mk-image-preview-moving .mk-image-preview-img-wrapper {
+  -webkit-transition-duration: 0s;
+          transition-duration: 0s;
+}
+.mk-image-preview-wrap {
+  z-index: 1180;
+}
+.mk-image-preview-operations {
+  -webkit-box-sizing: border-box;
+          box-sizing: border-box;
+  margin: 0;
+  padding: 0;
+  color: rgba(0, 0, 0, 0.85);
+  font-size: 14px;
+  font-variant: tabular-nums;
+  line-height: 1.5715;
+  -webkit-font-feature-settings: 'tnum';
+          font-feature-settings: 'tnum';
+  position: absolute;
+  top: 0;
+  right: 0;
+  z-index: 1;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-orient: horizontal;
+  -webkit-box-direction: reverse;
+      -ms-flex-direction: row-reverse;
+          flex-direction: row-reverse;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  width: 100%;
+  color: rgba(255, 255, 255, 0.85);
+  list-style: none;
+  background: rgba(0, 0, 0, 0.5);
+  pointer-events: auto;
+}
+.mk-image-preview-operations-operation {
+  margin-left: 12px;
+  padding: 10px 12px;
+  cursor: pointer;
+  font-size: 18px;
+}
+.mk-image-preview-operations-operation-disabled {
+  color: rgba(255, 255, 255, 0.25);
+  pointer-events: none;
+}
+.mk-image-preview-operations-operation:last-of-type {
+  margin-left: 0;
+}
+.mk-image-preview-switch-left,
+.mk-image-preview-switch-right {
+  font-size: 20px;
+  position: absolute;
+  top: 50%;
+  right: 10px;
+  z-index: 1;
+  display: -webkit-box;
+  display: -ms-flexbox;
+  display: flex;
+  -webkit-box-align: center;
+      -ms-flex-align: center;
+          align-items: center;
+  -webkit-box-pack: center;
+      -ms-flex-pack: center;
+          justify-content: center;
+  width: 44px;
+  height: 44px;
+  margin-top: -22px;
+  color: rgba(255, 255, 255, 0.85);
+  background: rgba(0, 0, 0, 0.1);
+  border-radius: 50%;
+  cursor: pointer;
+  pointer-events: auto;
+}
+
+.mk-image-preview-switch-left {
+  left: 10px;
+}
+.mk-image-preview-switch-right {
+  right: 10px;
+}
+
+.mk-drawer.mk-drawer-open .mk-drawer-mask {
+  height: 100%;
+  opacity: 1;
+  transition: none;
+  -webkit-animation: antdDrawerFadeIn 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+          animation: antdDrawerFadeIn 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+  pointer-events: auto;
+}
+.mk-preview {
+  z-index: 1100;
+  opacity: 0;
+}
+.mk-preview.close {
+  display: none;
+}
+.mk-preview.open {
+  opacity: 1;
+}
+
+.mk-preview.opening {
+  transition: none;
+  -webkit-animation: antdDrawerFadeIn 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+          animation: antdDrawerFadeIn 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+  pointer-events: auto;
+}
+.mk-preview.closeing {
+  opacity: 1;
+  transition: none;
+  -webkit-animation: antdDrawerFadeOut 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+          animation: antdDrawerFadeOut 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
+  pointer-events: auto;
+}
+
+@-webkit-keyframes antdDrawerFadeIn {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+@keyframes antdDrawerFadeIn {
+  0% {
+    opacity: 0;
+  }
+  100% {
+    opacity: 1;
+  }
+}
+@-webkit-keyframes antdDrawerFadeOut {
+  0% {
+    opacity: 1;
+  }
+  100% {
+    opacity: 0;
+  }
+}
+@keyframes antdDrawerFadeOut {
+  0% {
+    opacity: 1;
+  }
+  100% {
+    opacity: 0;
+  }
+}
diff --git a/src/components/normalform/index.jsx b/src/components/normalform/index.jsx
new file mode 100644
index 0000000..de675fb
--- /dev/null
+++ b/src/components/normalform/index.jsx
@@ -0,0 +1,93 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import ModalForm from './modalform'
+import './index.scss'
+
+class NormalFormComponent extends Component {
+  static propTpyes = {
+    width: PropTypes.any,
+    check: PropTypes.any,
+    title: PropTypes.string,
+    getForms: PropTypes.func,
+    update: PropTypes.func
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    formlist: []
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) || !is(fromJS(this.props.children), fromJS(nextProps.children))
+  }
+
+  trigger = () => {
+    this.setState({
+      visible: true,
+      formlist: this.props.getForms()
+    })
+  }
+
+  submit = () => {
+    const { check } = this.props
+
+    this.Ref.handleConfirm().then(res => {
+      if (check) {
+        this.props.update(res, () => {
+          this.setState({
+            visible: false,
+            formlist: []
+          })
+        })
+      } else {
+        this.setState({
+          visible: false,
+          formlist: []
+        })
+        this.props.update(res)
+      }
+    })
+  }
+
+  cancel = () => {
+    this.setState({ visible: false })
+
+    this.props.cancel && this.props.cancel()
+  }
+
+  render () {
+    const { title, width, children, double } = this.props
+    const { visible, dict, formlist } = this.state
+
+    return (
+      <>
+        {!double ? <span onClick={this.trigger}>{children}</span> : <span onDoubleClick={this.trigger}>{children}</span>}
+        <Modal
+          wrapClassName="popview-modal"
+          title={title}
+          visible={visible}
+          width={width}
+          maskClosable={false}
+          okText={dict['model.confirm']}
+          onOk={this.submit}
+          onCancel={this.cancel}
+          destroyOnClose
+        >
+          <ModalForm
+            formlist={formlist}
+            inputSubmit={this.submit}
+            wrappedComponentRef={(inst) => this.Ref = inst}
+          />
+        </Modal>
+      </>
+    )
+  }
+}
+
+export default NormalFormComponent
\ No newline at end of file
diff --git a/src/menu/components/tabs/tablabelform/index.scss b/src/components/normalform/index.scss
similarity index 100%
rename from src/menu/components/tabs/tablabelform/index.scss
rename to src/components/normalform/index.scss
diff --git a/src/components/normalform/modalform/index.jsx b/src/components/normalform/modalform/index.jsx
new file mode 100644
index 0000000..e0927dc
--- /dev/null
+++ b/src/components/normalform/modalform/index.jsx
@@ -0,0 +1,275 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { fromJS } from 'immutable'
+import { Form, Row, Col, Tooltip, Icon, Cascader, Input } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import MKEInput from './mkInput'
+import MKNumberInput from './mkNumberInput'
+import MKSelect from './mkSelect'
+import './index.scss'
+
+const { TextArea } = Input
+
+const MKRadio = asyncComponent(() => import('./mkRadio'))
+const StyleInput = asyncComponent(() => import('./styleInput'))
+const MKFileUpload = asyncComponent(() => import('@/tabviews/zshare/fileupload'))
+const MKColor = asyncComponent(() => import('@/tabviews/zshare/mutilform/mkColor'))
+const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+
+class ModalForm extends Component {
+  static propTpyes = {
+    formlist: PropTypes.array,   // 琛ㄥ崟鍒楄〃
+    inputSubmit: PropTypes.func  // input鍥炶溅鎻愪氦
+  }
+
+  state = {
+    formlist: [],    // 琛ㄥ崟椤�
+  }
+
+  record = {}
+
+  componentDidMount () {
+    let record = {}
+    let controlFields = {}
+    let fieldMap = new Map()
+
+    let formlist = this.props.formlist.filter(item => {
+      if (item.controlFields) { // 澶氬眰琛ㄥ崟鎺у埗
+        controlFields[item.field] = item.controlFields
+      }
+      
+      item.hidden = false
+
+      if (item.forbid) {
+        item.hidden = true
+      }
+      if (item.options) {
+        item.oriOptions = fromJS(item.options).toJS()
+      }
+
+      if (item.type === 'text') {
+        let _rules = [{
+          required: item.required,
+          message: item.label + '涓嶅彲涓虹┖!'
+        }]
+        
+        item.rules = _rules
+      } else if (item.type === 'number') {
+        item.rules = [{
+          required: item.required,
+          message: item.label + '涓嶅彲涓虹┖!'
+        }, {
+          validator: (rule, value, callback) => this.handleConfirmPassword(rule, value, callback, item)
+        }]
+      } else if (item.type === 'textarea') {
+        let _rules = [
+          {
+            required: item.required,
+            message: item.label + '涓嶅彲涓虹┖!'
+          }
+        ]
+        item.rules = _rules
+      } else {
+        item.rules = [
+          {
+            required: item.required,
+            message: '璇烽�夋嫨' + item.label + '!'
+          }
+        ]
+      }
+
+      record[item.field] = item.initval
+
+      fieldMap.set(item.field, item)
+
+      return true
+    })
+
+    Object.keys(controlFields).forEach(key => {
+      if (!fieldMap.has(key)) return
+
+      let supItem = fieldMap.get(key)
+      let fields = []
+      
+      controlFields[key].forEach(item => {
+        if (!fieldMap.has(item.field)) return
+
+        let cell = fieldMap.get(item.field)
+
+        if (cell.hidden) return
+
+        if (supItem.hidden || !item.values.includes(supItem.initval)) {
+          cell.hidden = true
+          fieldMap.set(item.field, cell)
+        }
+
+        fields.push(item)
+      })
+
+      supItem.controlFields = fields
+      
+      fieldMap.set(key, supItem)
+    })
+
+    formlist = formlist.map(cell => {
+      let item = fieldMap.get(cell.field)
+
+      if (item.linkField) {
+        let supInitVal = fieldMap.get(item.linkField).initval || ''
+        
+        item.options = item.oriOptions.filter(option => option.ParentID === supInitVal)
+      }
+
+      return item
+    })
+
+    this.record = record
+
+    this.setState({ formlist })
+  }
+
+  handleConfirmPassword = (rule, value, callback, item) => {
+    let val = parseFloat(value)
+
+    if (!isNaN(val)) {
+      if (typeof(item.min) === 'number' && val < item.min) {
+        callback(item.label + '鏈�灏忓�间负 ' + item.min)
+      } else if (typeof(item.max) === 'number' && val > item.max) {
+        callback(item.label + '鏈�澶у�间负 ' + item.max)
+      }
+    }
+    callback()
+  }
+
+  recordChange = (values, item) => {
+    this.record = {...this.record, ...values}
+
+    if (item && item.controlFields) {
+      let map = new Map()
+      this.state.formlist.forEach(cell => {
+        if (!cell.field) return
+        map.set(cell.field, cell)
+      })
+
+      let reset = (current) => {
+        let val = this.record[current.field]
+
+        current.controlFields.forEach(cell => {
+          let m = map.get(cell.field)
+          m.hidden = current.hidden || !cell.values.includes(val)
+
+          if (m.hidden) {
+            m.initval = this.record[m.field]
+          }
+
+          map.set(cell.field, m)
+
+          if (m.controlFields) {
+            reset(m)
+          }
+        })
+      }
+
+      reset(item)
+
+      this.setState({
+        formlist: this.state.formlist.map(cell => {
+          if (cell.field) {
+            return map.get(cell.field)
+          }
+          return cell
+        })
+      })
+    }
+  }
+
+  getFields() {
+    const { getFieldDecorator } = this.props.form
+    const { formlist } = this.state
+
+    const fields = []
+
+    formlist.forEach((item, index) => {
+      if (item.hidden || item.forbid) return
+
+      let content = null
+      let label = item.tooltip ? <Tooltip placement="topLeft" title={item.tooltip}><Icon type="question-circle" />{item.label}</Tooltip> : item.label
+    
+      if (item.type === 'text') {
+        content = (<MKEInput config={item} onChange={(val, defer) => !defer && this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
+      } else if (item.type === 'number') {
+        content = (<MKNumberInput config={item} onChange={(val, defer) => !defer && this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
+      } else if (item.type === 'select' || item.type === 'multiselect') {
+        content = (<MKSelect config={item} onChange={(val, other) => this.recordChange({[item.field]: val, ...other}, item)} />)
+      } else if (item.type === 'color') {
+        content = (<MKColor config={item} onChange={(val) => this.recordChange({[item.field]: val})}/>)
+      } else if (item.type === 'styleInput') {
+        content = (<StyleInput config={item} onChange={(val) => this.recordChange({[item.field]: val})}/>)
+      } else if (item.type === 'radio') {
+        content = (<MKRadio config={item} onChange={(val, other) => this.recordChange({[item.field]: val, ...other}, item)}/>)
+      } else if (item.type === 'fileupload') {
+        content = (<MKFileUpload config={item} onChange={(val) => this.recordChange({[item.field]: val})} />)
+      } else if (item.type === 'cascader') {
+        content = (<Cascader options={item.options} expandTrigger="hover" placeholder="" />)
+      } else if (item.type === 'textarea') {
+        content = (<TextArea rows={item.rows || 2} placeholder=""/>)
+      } else if (item.type === 'mkicon') {
+        content = (<MkIcon allowClear={item.allowClear}/>)
+      } else if (item.type === 'source') {
+        content = (<SourceComponent type="" placement="right"/>)
+      }
+
+      if (!content) return
+
+      fields.push(
+        <Col span={item.span || 12} key={index}>
+          <Form.Item label={label}>
+            {getFieldDecorator(item.field, {
+              initialValue: item.initval,
+              rules: item.rules
+            })(content)}
+          </Form.Item>
+        </Col>
+      )
+    })
+    
+    return fields
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (err) {
+          reject(err)
+          return
+        }
+
+        resolve(values)
+      })
+    })
+  }
+
+  render() {
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="normal-form-field">
+        <Row gutter={24}>{this.getFields()}</Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(ModalForm)
\ No newline at end of file
diff --git a/src/components/normalform/modalform/index.scss b/src/components/normalform/modalform/index.scss
new file mode 100644
index 0000000..23b5f23
--- /dev/null
+++ b/src/components/normalform/modalform/index.scss
@@ -0,0 +1,67 @@
+.normal-form-field {
+  position: relative;
+  padding: 0px 24px 20px;
+  >.ant-row >.ant-col {
+    display: inline-block;
+    float: none;
+    vertical-align: top;
+    padding-left: 1.2%!important;
+    padding-right: 1.2%!important;
+  }
+  .ant-form-item-label .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-checkbox-group {
+    line-height: unset;
+    .ant-checkbox-wrapper {
+      margin-right: 8px;
+    }
+    .ant-checkbox-wrapper + .ant-checkbox-wrapper {
+      margin-left: 0;
+    }
+  }
+  .ant-form-item {
+    display: flex;
+  }
+
+  .ant-form-item-control-wrapper {
+    flex: 1;
+  }
+  .ant-form-item-label {
+    overflow: hidden;
+    display: inline-block;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    width: 33.3%;
+  }
+
+  .ant-input-number {
+    width: 100%;
+  }
+  .ant-form-explain {
+    overflow:hidden;
+    text-overflow:ellipsis;
+    white-space:nowrap;
+  }
+  .color-sketch-block {
+    margin-top: 7px;
+    overflow: hidden;
+    .color-sketch-block-box {
+      min-width: 100px;
+      .color-sketch-block-inner {
+        box-shadow: 0 0 0 1px rgba(0, 0, 0, .1) inset;
+      }
+    }
+  }
+
+  .ant-input-number-input {
+    color: rgba(0, 0, 0, 0.65)!important;
+    cursor: default!important;
+  }
+  .ant-col-12 + .ant-col-24 {
+    .ant-form-item-label {
+      width: 16%;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/normalform/modalform/mkInput/index.jsx b/src/components/normalform/modalform/mkInput/index.jsx
new file mode 100644
index 0000000..2df60f7
--- /dev/null
+++ b/src/components/normalform/modalform/mkInput/index.jsx
@@ -0,0 +1,71 @@
+import React, { Component } from 'react'
+import { is, fromJS } from 'immutable'
+import { Input } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+
+import './index.scss'
+
+/**
+ * @description 鑷畾涔夋枃鏈緭鍏�
+ */
+class MKEInput extends Component {
+  constructor(props) {
+    super(props)
+    
+    const config = props.config
+    
+    this.state = {
+      value: config.initval
+    }
+  }
+  
+  inputRef = React.createRef()
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    const { config } = this.props
+    MKEmitter.addListener('mkFC', this.mkFormHandle)
+
+    if (config.focus) {
+      this.inputRef.current.select()
+    }
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkFC', this.mkFormHandle)
+  }
+
+  mkFormHandle = (type, field, value) => {
+    if (field !== this.props.config.field) return
+
+    if (type === 'focus') {
+      this.inputRef.current.select()
+    } else if (type === 'input') {
+      this.setState({value})
+      this.props.onChange(value, true)
+    }
+  }
+
+  handleChange = (e) => {
+    let val = e.target.value
+
+    this.setState({value: val})
+    this.props.onChange(val)
+  }
+
+  render() {
+    const { config } = this.props
+    const { value } = this.state
+
+    return <Input ref={this.inputRef} placeholder={config.placeholder || ''} value={value} autoComplete="off" onChange={this.handleChange} onPressEnter={this.props.onSubmit} />
+  }
+}
+
+export default MKEInput
\ No newline at end of file
diff --git a/src/components/normalform/modalform/mkInput/index.scss b/src/components/normalform/modalform/mkInput/index.scss
new file mode 100644
index 0000000..93dcc24
--- /dev/null
+++ b/src/components/normalform/modalform/mkInput/index.scss
@@ -0,0 +1,27 @@
+.am-list-item.am-input-item {
+  .am-input-control {
+    height: 100%;
+    input {
+      height: 100%;
+    }
+  }
+  .am-input-label {
+    width: 28%;
+    max-width: 120px;
+    text-overflow: ellipsis;
+  }
+  .am-input-extra {
+    max-height: 40px;
+    .anticon-scan {
+      font-size: 22px;
+      padding: 8px 5px;
+    }
+  }
+}
+.am-input-item.right {
+  .am-input-control {
+    input {
+      text-align: right;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/normalform/modalform/mkNumberInput/index.jsx b/src/components/normalform/modalform/mkNumberInput/index.jsx
new file mode 100644
index 0000000..ae5e040
--- /dev/null
+++ b/src/components/normalform/modalform/mkNumberInput/index.jsx
@@ -0,0 +1,62 @@
+import React, { Component } from 'react'
+import { is, fromJS } from 'immutable'
+import { InputNumber } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+
+import './index.scss'
+
+class MKNumberInput extends Component {
+  constructor(props) {
+    super(props)
+
+    const config = props.config
+
+    this.state = {
+      value: config.initval,
+      precision: config.precision
+    }
+  }
+
+  inputNumberRef = React.createRef()
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('mkFC', this.mkFormHandle)
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkFC', this.mkFormHandle)
+  }
+
+  mkFormHandle = (type, field, value) => {
+    if (field !== this.props.config.field) return
+    
+    if (type === 'focus') {
+      this.inputNumberRef.current.focus()
+    } else if (type === 'input') {
+      this.setState({value})
+      this.props.onChange(value, true)
+    }
+  }
+
+  handleChange = (val) => {
+    this.setState({value: val})
+    this.props.onChange(val)
+  }
+
+  render() {
+    const { onSubmit } = this.props
+    const { value, precision } = this.state
+
+    return (<InputNumber ref={this.inputNumberRef} value={value} precision={precision} onChange={this.handleChange} onPressEnter={onSubmit} />)
+  }
+}
+
+export default MKNumberInput
\ No newline at end of file
diff --git a/src/components/normalform/modalform/mkNumberInput/index.scss b/src/components/normalform/modalform/mkNumberInput/index.scss
new file mode 100644
index 0000000..93dcc24
--- /dev/null
+++ b/src/components/normalform/modalform/mkNumberInput/index.scss
@@ -0,0 +1,27 @@
+.am-list-item.am-input-item {
+  .am-input-control {
+    height: 100%;
+    input {
+      height: 100%;
+    }
+  }
+  .am-input-label {
+    width: 28%;
+    max-width: 120px;
+    text-overflow: ellipsis;
+  }
+  .am-input-extra {
+    max-height: 40px;
+    .anticon-scan {
+      font-size: 22px;
+      padding: 8px 5px;
+    }
+  }
+}
+.am-input-item.right {
+  .am-input-control {
+    input {
+      text-align: right;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/components/normalform/modalform/mkRadio/index.jsx b/src/components/normalform/modalform/mkRadio/index.jsx
new file mode 100644
index 0000000..9b2ec9e
--- /dev/null
+++ b/src/components/normalform/modalform/mkRadio/index.jsx
@@ -0,0 +1,94 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Radio } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+class MKRadio extends Component {
+  static propTpyes = {
+    config: PropTypes.object,
+    onChange: PropTypes.func
+  }
+
+  state = {
+    value: this.props.config.initval,
+    config: fromJS(this.props.config).toJS(),
+    options: fromJS(this.props.config.options).toJS(),
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('mkFP', this.mkFormHandle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkFP', this.mkFormHandle)
+  }
+
+  mkFormHandle = (field, parentId) => {
+    const { config } = this.state
+
+    if (field !== config.field) return
+
+    let options = config.oriOptions ? config.oriOptions.filter(option => option.ParentID === parentId) : []
+    let val = options[0] ? options[0].value : ''
+
+    this.setState({
+      options,
+      value: val
+    })
+
+    this.props.onChange(val)
+
+    config.linkFields && config.linkFields.forEach((m, i) => {
+      setTimeout(() => {
+        MKEmitter.emit('mkFP', m, val)
+      }, (i + 1) * 70)
+    })
+  }
+
+  onChange = (e) => {
+    const { config } = this.state
+    let value = e.target.value
+    let other = {}
+
+    if (config.subFields) {
+      let option = this.state.options.filter(m => m.value === value)[0]
+      option && config.subFields.forEach((n, i) => {
+        other[n] = option[n] || ''
+        setTimeout(() => {
+          MKEmitter.emit('mkFC', 'input', n, option[n] || '')
+        }, i * 5)
+      })
+    }
+
+    config.linkFields && config.linkFields.forEach((m, i) => {
+      setTimeout(() => {
+        MKEmitter.emit('mkFP', m, value)
+      }, (i + 1) * 100)
+    })
+
+    this.setState({value})
+    this.props.onChange(value, other)
+  }
+
+  render() {
+    const { value, options } = this.state
+
+    return (
+      <Radio.Group style={{whiteSpace: 'nowrap'}} value={value} onChange={this.onChange}>
+        {options.map(option => <Radio key={option.value} value={option.value}>{option.label}</Radio>)}
+      </Radio.Group>
+    )
+  }
+}
+
+export default MKRadio
\ No newline at end of file
diff --git a/src/menu/components/card/cardcomponent/pastecomponent/index.scss b/src/components/normalform/modalform/mkRadio/index.scss
similarity index 100%
copy from src/menu/components/card/cardcomponent/pastecomponent/index.scss
copy to src/components/normalform/modalform/mkRadio/index.scss
diff --git a/src/components/normalform/modalform/mkSelect/index.jsx b/src/components/normalform/modalform/mkSelect/index.jsx
new file mode 100644
index 0000000..6fe6b33
--- /dev/null
+++ b/src/components/normalform/modalform/mkSelect/index.jsx
@@ -0,0 +1,140 @@
+import React, {Component} from 'react'
+import { is, fromJS } from 'immutable'
+import { Select } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+class MKSelect extends Component {
+  constructor(props) {
+    super(props)
+    
+    const config = props.config
+    let value = config.initval
+
+    if (config.type === 'multiselect') {
+      value = value || []
+    }
+
+    this.state = {
+      config: fromJS(config).toJS(),
+      options: fromJS(config.options).toJS(),
+      value,
+    }
+  }
+
+  componentDidMount () {
+    const { config } = this.state
+
+    if (config.type !== 'multiselect') {
+      MKEmitter.addListener('mkFP', this.mkFormHandle)
+    }
+    MKEmitter.addListener('mkFC', this.mkFormControl)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkFP', this.mkFormHandle)
+    MKEmitter.removeListener('mkFC', this.mkFormControl)
+  }
+
+  mkFormControl = (type, field, value) => {
+    if (field !== this.props.config.field) return
+    
+    if (type === 'input') {
+      this.setState({value})
+      this.props.onChange(value, {})
+    }
+  }
+
+  mkFormHandle = (field, parentId) => {
+    if (field !== this.state.config.field) return
+
+    const { config } = this.state
+
+    let options = config.oriOptions ? config.oriOptions.filter(option => option.ParentID === parentId) : []
+    let val = options[0] ? options[0].value : ''
+
+    this.setState({
+      options,
+      value: val
+    })
+
+    this.props.onChange(val)
+
+    config.linkFields && config.linkFields.forEach((m, i) => {
+      setTimeout(() => {
+        MKEmitter.emit('mkFP', m, val)
+      }, (i + 1) * 70)
+    })
+  }
+
+  selectChange = (val) => {
+    const { config } = this.state
+    let other = {}
+
+    if (config.subFields) {
+      let option = this.state.options.filter(m => m.value === val)[0]
+      option && config.subFields.forEach((n, i) => {
+        other[n] = option[n] || ''
+        setTimeout(() => {
+          MKEmitter.emit('mkFC', 'input', n, option[n] || '')
+        }, i * 5)
+      })
+    }
+
+    config.linkFields && config.linkFields.forEach((m, i) => {
+      setTimeout(() => {
+        MKEmitter.emit('mkFP', m, val)
+      }, (i + 1) * 100)
+    })
+
+    this.setState({value: val})
+    this.props.onChange(val, other)
+  }
+
+  mutilselectChange = (val) => {
+    this.props.onChange(val)
+  }
+
+  render() {
+    const { value, config, options } = this.state
+
+    if (config.type !== 'multiselect') {
+      return (
+        <Select
+          showSearch
+          allowClear
+          value={value}
+          filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+          onSelect={this.selectChange}
+          onChange={(val) => val === undefined && this.selectChange('')}
+        >
+          {options.map(option =>
+            <Select.Option key={option.value || option.field} value={option.value || option.field}>{option.label || option.text}</Select.Option>
+          )}
+        </Select>
+      )
+    } else {
+      return (<Select
+        showSearch
+        mode="multiple"
+        defaultValue={value}
+        filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+        onChange={this.mutilselectChange}
+      >
+        {options.map(option =>
+          <Select.Option key={option.value} value={option.value}>{option.label || option.text}</Select.Option>
+        )}
+      </Select>)
+    }
+  }
+}
+
+export default MKSelect
\ No newline at end of file
diff --git a/src/menu/components/card/table-card/cardcomponent/index.scss b/src/components/normalform/modalform/mkSelect/index.scss
similarity index 100%
rename from src/menu/components/card/table-card/cardcomponent/index.scss
rename to src/components/normalform/modalform/mkSelect/index.scss
diff --git a/src/components/normalform/modalform/styleInput/index.jsx b/src/components/normalform/modalform/styleInput/index.jsx
new file mode 100644
index 0000000..3925b2a
--- /dev/null
+++ b/src/components/normalform/modalform/styleInput/index.jsx
@@ -0,0 +1,144 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Select, Input } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const { Option } = Select
+
+class StyleInput extends Component {
+  static propTpyes = {
+    config: PropTypes.object,
+    onChange: PropTypes.func,
+  }
+
+  state = {
+    value: '',
+    unit: '',
+    options: []
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let val = config.initval || ''
+    let options = config.options || ['px', 'vh', 'vw', '%']
+    let unit = options[0]
+
+    if (val) {
+      if (val.indexOf('px') > -1) {
+        unit = 'px'
+      } else if (val.indexOf('%') > -1) {
+        unit = '%'
+      } else if (val.indexOf('vw') > -1) {
+        unit = 'vw'
+      } else if (val.indexOf('vh') > -1) {
+        unit = 'vh'
+      }
+    }
+
+    let _val = parseFloat(val)
+
+    if (isNaN(_val)) {
+      _val = ''
+    }
+
+    this.setState({value: _val, options: options, unit})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('mkFC', this.mkFormHandle)
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('mkFC', this.mkFormHandle)
+  }
+
+  mkFormHandle = (type, field, value) => {
+    if (field !== this.props.config.field) return
+    
+    if (type === 'input') {
+      let val = value
+      let unit = this.state.unit
+
+      if (val) {
+        if (val.indexOf('px') > -1) {
+          unit = 'px'
+        } else if (val.indexOf('%') > -1) {
+          unit = '%'
+        } else if (val.indexOf('vw') > -1) {
+          unit = 'vw'
+        } else if (val.indexOf('vh') > -1) {
+          unit = 'vh'
+        }
+      }
+
+      let _val = parseFloat(val)
+    
+      if (isNaN(_val)) {
+        _val = ''
+      }
+      this.setState({value: _val, unit})
+
+      this.props.onChange(value, true)
+    }
+  }
+
+  changeValue = (e) => {
+    const { unit } = this.state
+    let val = e.target.value
+
+    if (/\d+\.$/.test(val)) {
+      this.setState({
+        value: val
+      })
+      return
+    }
+    let _val = parseFloat(val)
+    
+    if (isNaN(_val)) {
+      _val = ''
+    }
+
+    this.setState({
+      value: _val,
+    })
+
+    this.props.onChange(_val !== '' ? `${_val}${unit}` : '')
+  }
+
+  changeUnit = (val) => {
+    const { value } = this.state
+
+    this.setState({unit: val})
+
+    this.props.onChange(value !== '' ? `${value}${val}` : '')
+  }
+
+  render () {
+    const { value, options, unit } = this.state
+
+    return (
+      <div className="style-input-wrap">
+        <Input value={value} addonAfter={
+          options.length > 1 ?
+          <Select value={unit} onChange={this.changeUnit}>
+            {options.map(item => <Option key={item} value={item}>{item}</Option>)}
+          </Select> :
+          <div className="single-unit">{unit}</div>
+        } onChange={this.changeValue}/>
+      </div>
+    )
+  }
+}
+
+export default StyleInput
\ No newline at end of file
diff --git a/src/components/normalform/modalform/styleInput/index.scss b/src/components/normalform/modalform/styleInput/index.scss
new file mode 100644
index 0000000..b058a37
--- /dev/null
+++ b/src/components/normalform/modalform/styleInput/index.scss
@@ -0,0 +1,11 @@
+.style-input-wrap {
+  line-height: 32px;
+  .ant-select {
+    width: 60px!important;
+  }
+  .single-unit {
+    width: 38px;
+    text-align: left;
+    color: rgba(255, 255, 255, 0.65);
+  }
+}
diff --git a/src/components/qrcode/index.jsx b/src/components/qrcode/index.jsx
index da630b7..122a9b1 100644
--- a/src/components/qrcode/index.jsx
+++ b/src/components/qrcode/index.jsx
@@ -28,6 +28,7 @@
   render() {
     const { value, card } = this.props
     let color = card.color
+    let size = card.qrWidth || 50
     
     if (/rgb/ig.test(color)) {
       color = this.hexify(color)
@@ -37,12 +38,12 @@
       <div className="qrcode-box">
         <QrCode
           value={value}
-          size={card.qrWidth || 50}
+          size={size}
           fgColor={color}
           imageSettings={card.url ? {
             src: card.url,
-            height: (card.qrWidth || 50) / 4,
-            width: (card.qrWidth || 50) / 4,
+            height: size / 5,
+            width: size / 5,
             excavate: true
           } : null}/>
       </div>
diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index a9ebd63..cab23a1 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -2,18 +2,18 @@
 import PropTypes from 'prop-types'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import {Tabs, Icon, BackTop} from 'antd'
+import {Tabs, Icon, BackTop, notification} from 'antd'
 import moment from 'moment'
 import 'moment/locale/zh-cn'
 
-import { modifyTabview, toggleIsiframe } from '@/store/action'
+import { modifyTabview, toggleIsiframe, initActionPermission } from '@/store/action'
 import asyncComponent from '@/utils/asyncLoadComponent'
 import NotFount from '@/components/404'
-// import options from '@/store/options.js'
+import options from '@/store/options.js'
 import mzhCN from '@/locales/zh-CN/main.js'
 import menUS from '@/locales/en-US/main.js'
 import MKEmitter from '@/utils/events.js'
-
+import Api from '@/api'
 import './index.scss'
 
 const Home = asyncComponent(() => import('@/tabviews/home'))
@@ -75,7 +75,52 @@
     e.stopPropagation()
     window.GLOB.CacheMap = new Map()
 
-    MKEmitter.emit('reloadMenuView', menu.MenuID)
+    if (options.sysType === 'local' && window.GLOB.systemType !== 'production') {
+      let roledefer = new Promise(resolve => {
+        Api.getSystemConfig({
+          func: 's_Get_TrdMenu_Role',
+          edition_type: 'A',
+          pro_sys: ''
+        }).then(result => {
+          if (!result) return
+          if (!result.status) {
+            notification.error({
+              top: 92,
+              message: result.message,
+              duration: 10
+            })
+          } else {
+            let _permAction = {loaded: true} // 鎸夐挳鏉冮檺
+    
+            if (result.UserRoles_Menu) {
+              result.UserRoles_Menu.forEach(menu => {
+                if (!menu.MenuID) return
+                _permAction[menu.MenuID] = true
+              })
+            }
+  
+            this.props.initActionPermission(_permAction)
+          }
+
+          resolve()
+        })
+      })
+  
+      // 鑾峰彇涓昏彍鍗曞弬鏁�
+      let menudefer = new Promise(resolve => {
+        Api.getAppVersion().then(() => {
+          resolve()
+        }, () => {
+          resolve()
+        })
+      })
+      
+      Promise.all([roledefer, menudefer]).then(() => {
+        MKEmitter.emit('reloadMenuView', menu.MenuID)
+      })
+    } else {
+      MKEmitter.emit('reloadMenuView', menu.MenuID)
+    }
   }
 
   changeTab = (e, menu) => {
@@ -269,6 +314,7 @@
 const mapDispatchToProps = (dispatch) => {
   return {
     modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews)),
+    initActionPermission: (permAction) => dispatch(initActionPermission(permAction)),
     toggleIsiframe: (isiframe) => dispatch(toggleIsiframe(isiframe))
   }
 }
diff --git a/src/components/tabview/index.scss b/src/components/tabview/index.scss
index fe43335..5ea2e5a 100644
--- a/src/components/tabview/index.scss
+++ b/src/components/tabview/index.scss
@@ -115,6 +115,9 @@
     font-style: italic;
   }
 }
+.header-container + .mk-tabview-wrap {
+  max-width: 100%;
+}
 .mk-tabview-wrap.collapsed {
   max-width: calc(100% - 80px);
 }
diff --git a/src/index.js b/src/index.js
index 5c7b909..d9609e1 100644
--- a/src/index.js
+++ b/src/index.js
@@ -55,6 +55,8 @@
 sessionStorage.setItem('localDataM', localStorage.getItem('localDataM') || '')
 sessionStorage.setItem('debug', localStorage.getItem('debug') || '')
 sessionStorage.setItem('role_id', localStorage.getItem('role_id') || '')
+sessionStorage.setItem('departmentcode', localStorage.getItem('departmentcode') || '')
+sessionStorage.setItem('organization', localStorage.getItem('organization') || '')
 sessionStorage.setItem('localRole_id', localStorage.getItem('localRole_id') || '')
 
 if (sessionStorage.getItem('loginError')) {
@@ -82,6 +84,7 @@
     let GLOB = {}
     GLOB.appId = config.appId || ''
     GLOB.lineColor = config.lineColor || ''
+    GLOB.licenseKey = config.licenseKey || ''
 
     if (config.externalDatabase !== false && config.externalDatabase !== 'false' && config.externalDatabase !== undefined) {
       GLOB.externalDatabase = config.externalDatabase ? `[${config.externalDatabase}]..` : ''
@@ -131,6 +134,8 @@
 
     if (localStorage.getItem(_href + 'lang')) {
       sessionStorage.setItem('lang', localStorage.getItem(_href + 'lang'))
+    } else {
+      sessionStorage.setItem('lang', config.defaultLang !== 'en-US' ? 'zh-CN' : 'en-US')
     }
 
     let _systemMsg = localStorage.getItem(_href + 'system')
diff --git a/src/locales/en-US/login.js b/src/locales/en-US/login.js
index 513d262..f2df2c1 100644
--- a/src/locales/en-US/login.js
+++ b/src/locales/en-US/login.js
@@ -1,7 +1,7 @@
 export default {
   'login.auth.tip': 'The system is not authorized, please contact the administrator.',
-  'login.auth.ok': 'OK',
-  'login.auth.cancel': 'Cancel',
+  'login.ok': 'OK',
+  'login.cancel': 'Cancel',
   'login.username': 'Username',
   'login.username.empty': 'Please input your username!',
   'login.phone': 'Mobile phone no',
diff --git a/src/locales/en-US/model.js b/src/locales/en-US/model.js
index 2d2ef4b..cdff5ec 100644
--- a/src/locales/en-US/model.js
+++ b/src/locales/en-US/model.js
@@ -91,7 +91,6 @@
   'model.form.afterSuccess': 'Success',
   'model.form.afterError': 'Failure',
   'header.form.messageTip': 'Message',
-  'header.form.errorTime': 'Residence time',
   'header.form.refresh': 'Refresh',
   'header.form.refresh.never': 'Don\'t refresh',
   'header.form.refresh.grid': 'Refresh the table',
diff --git a/src/locales/zh-CN/login.js b/src/locales/zh-CN/login.js
index 70de125..321aa63 100644
--- a/src/locales/zh-CN/login.js
+++ b/src/locales/zh-CN/login.js
@@ -1,7 +1,7 @@
 export default {
   'login.auth.tip': '绯荤粺鏈巿鏉冿紝璇疯仈绯荤鐞嗗憳銆�',
-  'login.auth.ok': '纭畾',
-  'login.auth.cancel': '鍙栨秷',
+  'login.ok': '纭畾',
+  'login.cancel': '鍙栨秷',
   'login.username': '鐢ㄦ埛鍚�',
   'login.username.empty': '璇疯緭鍏ョ敤鎴峰悕!',
   'login.phone': '鎵嬫満鍙�',
diff --git a/src/locales/zh-CN/model.js b/src/locales/zh-CN/model.js
index 5a3a4cd..d2667aa 100644
--- a/src/locales/zh-CN/model.js
+++ b/src/locales/zh-CN/model.js
@@ -91,7 +91,6 @@
   'model.form.afterSuccess': '鎴愬姛鍚�',
   'model.form.afterError': '澶辫触鍚�',
   'header.form.messageTip': '淇℃伅鎻愮ず',
-  'header.form.errorTime': '鍋滅暀鏃堕棿',
   'header.form.refresh': '鍒锋柊',
   'header.form.refresh.never': '涓嶅埛鏂�',
   'header.form.refresh.grid': '鍒锋柊琛ㄦ牸',
diff --git a/src/menu/components/card/balcony/index.jsx b/src/menu/components/card/balcony/index.jsx
index 0fc02d5..fc5ae1b 100644
--- a/src/menu/components/card/balcony/index.jsx
+++ b/src/menu/components/card/balcony/index.jsx
@@ -1,19 +1,20 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Icon, Popover } from 'antd'
+import { Icon, Popover, Checkbox } from 'antd'
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
@@ -128,7 +129,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -163,24 +164,9 @@
   addButton = () => {
     const { card } = this.state
 
-    let newcard = {}
+    let newcard = {eleType: 'button', label: 'button', verify: null, show: 'link', sqlType: '', Ot: 'requiredSgl', OpenType: 'prompt', icon: '', class: 'primary', intertype: 'system', execSuccess: 'grid', execError: 'never', popClose: 'never'}
     newcard.uuid = Utils.getuuid()
     newcard.focus = true
-    
-    newcard.eleType = 'button'
-    newcard.label = 'button'
-    newcard.sqlType = ''
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'prompt'
-    newcard.icon = ''
-    newcard.class = 'primary'
-    newcard.intertype = 'system'
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.popClose = 'never'
-    newcard.errorTime = 10
-    newcard.verify = null
-    newcard.show = 'link'
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
     MKEmitter.emit('cardAddElement', [card.uuid, card.uuid], newcard)
@@ -204,6 +190,17 @@
     resolve({status: true})
   }
 
+  getWrapForms = () => {
+    const { wrap } = this.state.card
+
+    return getWrapForm(wrap)
+  }
+
+  updateWrap = (res) => {
+    delete res.quick
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   render() {
     const { card } = this.state
 
@@ -215,7 +212,9 @@
           <div className="mk-popover-control">
             <Icon className="plus" title="娣诲姞鍏冪礌" onClick={this.addElement} type="plus" />
             <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="鍗$墖璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="balcony" card={card}/>
             <PasteComponent options={['action', 'customCardElement']} updateConfig={this.pasteComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
@@ -228,6 +227,7 @@
         } trigger="hover">
           <Icon type="tool" />
         </Popover>
+        {card.wrap.checkAll === 'show' ? <div className="check-all"><Checkbox>鍏ㄩ��</Checkbox></div> : null}
         <CardCellComponent cards={card} cardCell={card} elements={card.elements} updateElement={this.updateCard}/>
       </div>
     )
diff --git a/src/menu/components/card/balcony/index.scss b/src/menu/components/card/balcony/index.scss
index 1e071bd..8359299 100644
--- a/src/menu/components/card/balcony/index.scss
+++ b/src/menu/components/card/balcony/index.scss
@@ -6,7 +6,17 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 30px;
+  display: flex;
   
+  .check-all {
+    width: 70px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  .model-menu-card-cell-list {
+    flex: 1;
+  }
   .card-control {
     position: absolute;
     top: 0px;
diff --git a/src/menu/components/card/balcony/options.jsx b/src/menu/components/card/balcony/options.jsx
new file mode 100644
index 0000000..06e8c95
--- /dev/null
+++ b/src/menu/components/card/balcony/options.jsx
@@ -0,0 +1,220 @@
+import { fromJS } from 'immutable'
+import MenuUtils from '@/utils/utils-custom.js'
+
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap) {
+  let modules = MenuUtils.getLinkModules(fromJS(window.GLOB.customMenu).toJS().components) || []
+  let supmodules = MenuUtils.getSupModules(fromJS(window.GLOB.customMenu).toJS().components, '') || []
+  let roleList = sessionStorage.getItem('sysRoles')
+  let appType = sessionStorage.getItem('appType')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const balconyWrapForm = [
+    {
+      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: '闈欐��'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'linkType',
+      label: '鍙楁帶绫诲瀷',
+      initval: wrap.linkType || 'static',
+      tooltip: '缁勪欢涓庡叾浠栫粍浠朵箣闂寸殑鎺у埗绫诲瀷锛岀嫭绔嬭〃绀轰笌鍏朵粬娌℃湁鍏宠仈銆�',
+      required: false,
+      options: [
+        {value: 'static', label: '鐙珛'},
+        {value: 'sync', label: '鍚屾'},
+        {value: 'sup', label: '涓婄骇'},
+      ],
+      controlFields: [
+        {field: 'supModule', values: ['sup']},
+        {field: 'supControl', values: ['sup']},
+        {field: 'syncModule', values: ['sync']},
+        {field: 'checkAll', values: ['sync']},
+      ]
+    },
+    {
+      type: 'cascader',
+      field: 'supModule',
+      label: '涓婄骇缁勪欢',
+      initval: wrap.supModule || '',
+      // tooltip: '褰撲笂绾х粍浠朵笉瀛樺湪鎴栨病鏈夋潈闄愭椂锛屽綋鍓嶇粍浠朵笉鏄剧ず銆�',
+      required: true,
+      options: supmodules
+    },
+    {
+      type: 'radio',
+      field: 'supControl',
+      label: '鏄剧ず鎺у埗',
+      initval: wrap.supControl || 'show',
+      tooltip: '褰撳墠缁勪欢鍦ㄤ富琛ㄩ�変腑琛屾椂鏄剧ず锛岃繕鏄缁堟樉绀恒��',
+      required: false,
+      options: [
+        {value: 'hidden', label: '閫夎'},
+        {value: 'show', label: '濮嬬粓'},
+      ]
+    },
+    {
+      type: 'cascader',
+      field: 'syncModule',
+      label: '鍚屾缁勪欢',
+      initval: wrap.syncModule || '',
+      tooltip: '褰撳悓姝ョ粍浠朵笉瀛樺湪鎴栨病鏈夋潈闄愭椂锛屽綋鍓嶇粍浠朵笉鏄剧ず銆�',
+      required: true,
+      options: modules
+    },
+    {
+      type: 'radio',
+      field: 'checkAll',
+      label: '鍏ㄩ��',
+      initval: wrap.checkAll || 'hidden',
+      tooltip: '褰撳悓姝ョ粍浠跺彲澶氶�夋椂锛岃缃叏閫夋湁鏁堛��',
+      required: false,
+      options: [
+        {value: 'hidden', label: '闅愯棌'},
+        {value: 'show', label: '鏄剧ず'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'position',
+      label: '浣嶇疆',
+      initval: wrap.position || 'relative',
+      tooltip: '浣跨敤鍥哄畾瀹氫綅鏃讹紝璇峰湪娴嬭瘯鐜涓煡鐪嬪畾浣嶆晥鏋溿��',
+      required: false,
+      options: [
+        {value: 'relative', label: '鐩稿瀹氫綅'},
+        {value: 'fixed', label: '鍥哄畾瀹氫綅'},
+      ],
+      controlFields: [
+        {field: 'quick', values: ['fixed']},
+        {field: 'top', values: ['fixed']},
+        {field: 'right', values: ['fixed']},
+        {field: 'bottom', values: ['fixed']},
+        {field: 'left', values: ['fixed']},
+        {field: 'realwidth', values: ['fixed']},
+        {field: 'transform', values: ['fixed']},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'quick',
+      label: '蹇嵎閫夋嫨',
+      initval: '',
+      required: false,
+      subFields: ['top', 'left', 'right', 'bottom', 'transform'],
+      options: [
+        {value: 'top', label: '涓�', top: '0px', left: '0px', right: '0px', bottom: '', transform: ''},
+        {value: 'top-left', label: '宸︿笂', top: '0px', left: '0px', right: '', bottom: '', transform: ''},
+        {value: 'top-right', label: '鍙充笂', top: '0px', left: '', right: '0px', bottom: '', transform: ''},
+        {value: 'left-middle', label: '宸︿腑', top: '50%', left: '0px', right: '', bottom: '', transform: 'translateY(-50%)'},
+        {value: 'right-middle', label: '鍙充腑', top: '50%', left: '', right: '0px', bottom: '', transform: 'translateY(-50%)'},
+        {value: 'bottom-left', label: '宸︿笅', top: '', left: '0px', right: '', bottom: '0px', transform: ''},
+        {value: 'bottom-right', label: '鍙充笅', top: '', left: '', right: '0px', bottom: '0px', transform: ''},
+        {value: 'bottom', label: '涓�', top: '', left: '0px', right: '0px', bottom: '0px', transform: ''},
+        {value: 'middle', label: '涓棿', top: '50%', left: '50%', right: '', bottom: '', transform: 'translate(-50%, -50%)'}
+      ]
+    },
+    {
+      type: 'styleInput',
+      field: 'top',
+      label: '璺濅笂',
+      initval: wrap.top || '',
+      required: false
+    },
+    {
+      type: 'styleInput',
+      field: 'right',
+      label: '璺濆彸',
+      initval: wrap.right || '',
+      required: false
+    },
+    {
+      type: 'styleInput',
+      field: 'bottom',
+      label: '璺濅笅',
+      initval: wrap.bottom || '',
+      required: false
+    },
+    {
+      type: 'styleInput',
+      field: 'left',
+      label: '璺濆乏',
+      initval: wrap.left || '',
+      required: false
+    },
+    {
+      type: 'styleInput',
+      field: 'realwidth',
+      label: '瀹為檯瀹藉害',
+      initval: wrap.realwidth || '',
+      required: false
+    },
+    {
+      type: 'select',
+      field: 'transform',
+      label: '鍙樻崲',
+      initval: wrap.transform || '',
+      required: false,
+      options: [
+        {value: 'translateY(-50%)', label: '涓婄Щ50%'},
+        {value: 'translateY(50%)', label: '涓嬬Щ50%'},
+        {value: 'translateX(-50%)', label: '宸︾Щ50%'},
+        {value: 'translateX(50%)', label: '鍙崇Щ50%'},
+        {value: 'translate(-50%, -50%)', label: '宸︿笂绉�50%'},
+        {value: 'translate(-50%, 50%)', label: '宸︿笅绉�50%'},
+        {value: 'translate(50%, -50%)', label: '鍙充笂绉�50%'},
+        {value: 'translate(50%, 50%)', label: '鍙充笅绉�50%'},
+      ]
+    },
+    {
+      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/card/balcony/wrapsetting/index.jsx b/src/menu/components/card/balcony/wrapsetting/index.jsx
deleted file mode 100644
index a8332db..0000000
--- a/src/menu/components/card/balcony/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class BalconyWrapSetting extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="鍗$墖璁剧疆"
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default BalconyWrapSetting
\ No newline at end of file
diff --git a/src/menu/components/card/balcony/wrapsetting/index.scss b/src/menu/components/card/balcony/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/card/balcony/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/balcony/wrapsetting/settingform/index.jsx b/src/menu/components/card/balcony/wrapsetting/settingform/index.jsx
deleted file mode 100644
index d0124d5..0000000
--- a/src/menu/components/card/balcony/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,415 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { fromJS } from 'immutable'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select, Cascader } from 'antd'
-
-import MenuUtils from '@/utils/utils-custom.js'
-import StyleInput from '@/menu/components/share/styleInput'
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,
-    config: PropTypes.object,
-    wrap: PropTypes.object,
-    inputSubmit: PropTypes.func
-  }
-
-  state = {
-    roleList: [],
-    modules: [],
-    supmodules: [],
-    appType: sessionStorage.getItem('appType'),
-    linkType: this.props.wrap.linkType,
-    position: this.props.wrap.position,
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    let menu = fromJS(window.GLOB.customMenu).toJS()
-
-    let modules = MenuUtils.getLinkModules(menu.components)
-    if (!modules) {
-      modules = []
-    }
-
-    let _menu = fromJS(window.GLOB.customMenu).toJS()
-
-    let supmodules = MenuUtils.getSupModules(_menu.components, '')
-    if (!supmodules) {
-      supmodules = []
-    }
-
-    this.setState({roleList, modules, supmodules})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  chose = (val) => {
-    let values = {}
-    if (val === 'top') {
-      values = {
-        top: '0px',
-        left: '50%',
-        right: '',
-        bottom: '',
-        transform: 'translateX(-50%)'
-      }
-    } else if (val === 'top-left') {
-      values = {
-        top: '0px',
-        left: '0px',
-        right: '',
-        bottom: '',
-        transform: ''
-      }
-    } else if (val === 'top-right') {
-      values = {
-        top: '0px',
-        left: '',
-        right: '0px',
-        bottom: '',
-        transform: ''
-      }
-    } else if (val === 'left-middle') {
-      values = {
-        top: '50%',
-        left: '0px',
-        right: '',
-        bottom: '',
-        transform: 'translateY(-50%)'
-      }
-    } else if (val === 'right-middle') {
-      values = {
-        top: '50%',
-        left: '',
-        right: '0px',
-        bottom: '',
-        transform: 'translateY(-50%)'
-      }
-    } else if (val === 'bottom-left') {
-      values = {
-        top: '',
-        left: '0px',
-        right: '',
-        bottom: '0px',
-        transform: ''
-      }
-    } else if (val === 'bottom-right') {
-      values = {
-        top: '',
-        left: '',
-        right: '0px',
-        bottom: '0px',
-        transform: ''
-      }
-    } else if (val === 'bottom') {
-      values = {
-        top: '',
-        left: '50%',
-        right: '',
-        bottom: '0px',
-        transform: 'translateX(-50%)'
-      }
-    } else if (val === 'middle') {
-      values = {
-        top: '50%',
-        left: '50%',
-        right: '',
-        bottom: '',
-        transform: 'translate(-50%, -50%)'
-      }
-    }
-    this.props.form.setFieldsValue(values)
-  }
-
-  render() {
-    const { wrap } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList, modules, supmodules, linkType, position } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
-                  <Icon type="question-circle" />
-                  鏁版嵁鏉ユ簮
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: wrap.datatype
-                })(
-                  <Radio.Group>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="缁勪欢涓庡叾浠栫粍浠朵箣闂寸殑鎺у埗绫诲瀷锛岀嫭绔嬭〃绀轰笌鍏朵粬娌℃湁鍏宠仈銆�">
-                  <Icon type="question-circle" />
-                  鍙楁帶绫诲瀷
-                </Tooltip>
-              }>
-                {getFieldDecorator('linkType', {
-                  initialValue: wrap.linkType || 'static'
-                })(
-                  <Radio.Group onChange={(e) => this.setState({linkType: e.target.value})}>
-                    <Radio value="static">鐙珛</Radio>
-                    <Radio value="sync">鍚屾</Radio>
-                    <Radio value="sup">涓婄骇</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {linkType === 'sup' ? <Col span={12}>
-              <Form.Item label="涓婄骇缁勪欢">
-                {getFieldDecorator('supModule', {
-                  initialValue: wrap.supModule,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '涓婄骇缁勪欢!'
-                    }
-                  ]
-                })(
-                  <Cascader options={supmodules} expandTrigger="hover" placeholder="" />
-                )}
-              </Form.Item>
-            </Col> : null}
-            {linkType === 'sup' ? <Col span={12}>
-              <Form.Item label="鏄剧ず鎺у埗">
-                {getFieldDecorator('supControl', {
-                  initialValue: wrap.supControl || 'show'
-                })(
-                  <Radio.Group>
-                    <Radio key="hidden" value="hidden"> 閫夎鏄剧ず </Radio>
-                    <Radio key="show" value="show"> 濮嬬粓鏄剧ず </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {linkType === 'sync' ? <Col span={12}>
-              <Form.Item label="鍚屾缁勪欢">
-                {getFieldDecorator('syncModule', {
-                  initialValue: wrap.syncModule,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鍚屾缁勪欢!'
-                    }
-                  ]
-                })(
-                  <Cascader options={modules} expandTrigger="hover" placeholder="" />
-                )}
-              </Form.Item>
-            </Col> : null}
-            {linkType === 'sync' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="褰撳悓姝ョ粍浠跺彲澶氶�夋椂锛岃缃叏閫夋湁鏁堛��">
-                  <Icon type="question-circle" />
-                  鍏ㄩ��
-                </Tooltip>
-              }>
-                {getFieldDecorator('checkAll', {
-                  initialValue: wrap.checkAll || 'hidden'
-                })(
-                  <Radio.Group>
-                    <Radio key="hidden" value="hidden"> 闅愯棌 </Radio>
-                    <Radio key="show" value="show"> 鏄剧ず </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="浣跨敤鍥哄畾瀹氫綅鏃讹紝璇峰湪娴嬭瘯鐜涓煡鐪嬪畾浣嶆晥鏋溿��">
-                  <Icon type="question-circle" />
-                  浣嶇疆
-                </Tooltip>
-              }>
-                {getFieldDecorator('position', {
-                  initialValue: wrap.position || 'relative'
-                })(
-                  <Radio.Group onChange={(e) => this.setState({position: e.target.value})}>
-                    <Radio value="relative">鐩稿瀹氫綅</Radio>
-                    <Radio value="fixed">鍥哄畾瀹氫綅</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="蹇嵎閫夋嫨">
-                <Select onSelect={this.chose}>
-                  <Select.Option key='1' value={'top'}>涓�</Select.Option>
-                  <Select.Option key='2' value={'top-left'}>宸︿笂</Select.Option>
-                  <Select.Option key='3' value={'top-right'}>鍙充笂</Select.Option>
-                  <Select.Option key='4' value={'left-middle'}>宸︿腑</Select.Option>
-                  <Select.Option key='5' value={'right-middle'}>鍙充腑</Select.Option>
-                  <Select.Option key='6' value={'bottom-left'}>宸︿笅</Select.Option>
-                  <Select.Option key='7' value={'bottom-right'}>鍙充笅</Select.Option>
-                  <Select.Option key='8' value={'bottom'}>涓�</Select.Option>
-                  <Select.Option key='9' value={'middle'}>涓棿</Select.Option>
-                </Select>
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="璺濅笂">
-                {getFieldDecorator('top', {
-                  initialValue: wrap.top || ''
-                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="璺濆彸">
-                {getFieldDecorator('right', {
-                  initialValue: wrap.right || ''
-                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="璺濅笅">
-                {getFieldDecorator('bottom', {
-                  initialValue: wrap.bottom || ''
-                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="璺濆乏">
-                {getFieldDecorator('left', {
-                  initialValue: wrap.left || ''
-                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="瀹為檯瀹藉害">
-                {getFieldDecorator('realwidth', {
-                  initialValue: wrap.realwidth || ''
-                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
-              </Form.Item>
-            </Col> : null}
-            {position === 'fixed' ? <Col span={12}>
-              <Form.Item label="鍙樻崲">
-                {getFieldDecorator('transform', {
-                  initialValue: wrap.transform || ''
-                })(
-                  <Select>
-                    <Select.Option key='1' value={''}>鏃�</Select.Option>
-                    <Select.Option key='2' value={'translateY(-50%)'}>涓婄Щ50%</Select.Option>
-                    <Select.Option key='3' value={'translateY(50%)'}>涓嬬Щ50%</Select.Option>
-                    <Select.Option key='4' value={'translateX(-50%)'}>宸︾Щ50%</Select.Option>
-                    <Select.Option key='5' value={'translateX(50%)'}>鍙崇Щ50%</Select.Option>
-                    <Select.Option key='6' value={'translate(-50%, -50%)'}>宸︿笂绉�50%</Select.Option>
-                    <Select.Option key='7' value={'translate(-50%, 50%)'}>宸︿笅绉�50%</Select.Option>
-                    <Select.Option key='8' value={'translate(50%, -50%)'}>鍙充笂绉�50%</Select.Option>
-                    <Select.Option key='9' value={'translate(50%, 50%)'}>鍙充笅绉�50%</Select.Option>
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/card/balcony/wrapsetting/settingform/index.scss b/src/menu/components/card/balcony/wrapsetting/settingform/index.scss
deleted file mode 100644
index 9644e12..0000000
--- a/src/menu/components/card/balcony/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,36 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .css {
-    padding-top: 10px;
-    .css-class {
-      position: absolute;
-      right: 13px;
-      top: -15px;
-      z-index: 1;
-      button {
-        height: 25px;
-      }
-    }
-    .ant-form-item {
-      margin-bottom: 0;
-    }
-    .ant-form-item-label {
-      width: 16%;
-    }
-    .ant-form-item-control-wrapper {
-      width: 84%;
-      .code-mirror-wrap .code-mirror-area .CodeMirror {
-        height: 100px;
-        min-height: 100px;
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
index 4ce1876..60cf952 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -41,14 +41,14 @@
   if (card.show === 'icon') {
     btnElement = (<Button style={_style} type="link"><Icon type={card.icon}/></Button>)
   } else if (card.show === 'link') {
-    btnElement = (<Button style={_style} type="link">{card.icon ? <Icon type={card.icon}/> : null}{card.label}</Button>)
+    btnElement = (<Button style={_style} type="link">{card.label}{card.icon ? <Icon type={card.icon}/> : null}</Button>)
   } else {
-    btnElement = (<Button style={_style}> {card.label}{card.icon ? <Icon type={card.icon}/> : null} </Button>)
+    btnElement = (<Button style={_style}> {card.icon ? <Icon type={card.icon}/> : null}{card.label} </Button>)
   }
 
   return (
     <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-      <div className="mk-popover-control">
+      <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
         <Icon className="edit" title="缂栬緫" type="edit" onClick={() => editCard(id)} />
         <Icon className="copy" title="澶嶅埗" type="copy" onClick={() => copyCard(id)} />
         <Icon className="close" title="鍒犻櫎" type="close" onClick={() => delCard(id)} />
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
index 9ee5976..ce5e4d4 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -24,7 +24,7 @@
   '10:1': '10%', '3:4': '133.33%', '2:3': '150%', '9:16': '177.78%'
 }
 
-const Card = ({ id, parent, fields, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, updateMarks, doubleClickCard }) => {
+const Card = ({ id, parent, fields, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, updateMarks }) => {
   const originalIndex = findCard(id).index
   const [{ isDragging }, drag] = useDrag({
     item: { type: 'action', id, originalIndex },
@@ -141,6 +141,12 @@
           {`${card.prefix || ''}${moment().format(card.dateFormat)}${card.postfix || ''}`}
         </div>
       )
+    } else if (card.eleType === 'formula') {
+      return (
+        <div className="ant-mk-date">
+          {`${card.prefix || ''}${card.formula}${card.postfix || ''}`}
+        </div>
+      )
     }
   }
 
@@ -153,16 +159,16 @@
 
   return (
     <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-      <div className="mk-popover-control">
+      <div className="mk-popover-control" onDoubleClick={(e) => e.stopPropagation()}>
         <Icon className="edit" title="缂栬緫" type="edit" onClick={() => editCard(id)} />
         <Icon className="copy" title="澶嶅埗" type="copy" onClick={() => copyCard(id)} />
         <Icon className="close" title="鍒犻櫎" type="close" onClick={() => delCard(id)} />
         <Icon className="style" title="璋冩暣鏍峰紡" onClick={() => changeStyle(id)} type="font-colors" />
-        {['text', 'number', 'slider', 'sequence'].includes(card.eleType) ? <MarkColumn columns={fields} type={card.eleType} marks={card.marks} onSubmit={(vals) => updateMarks({...card, marks: vals})} /> : null }
+        {['text', 'number', 'slider', 'sequence', 'formula'].includes(card.eleType) ? <MarkColumn 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 style={_style} onClick={clickComponent} onDoubleClick={() => doubleClickCard(id)} id={card.uuid}>
+        <div style={_style} onClick={clickComponent} id={card.uuid}>
           {getContent()}
         </div>
       </div>
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/index.jsx b/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
index 96eea80..64bb8d9 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -85,7 +85,7 @@
   const doubleClickCard = id => {
     const { card } = findCard(id)
 
-    if (card.eleType !== 'button' && card.eleType !== 'text' && card.eleType !== 'picture') {
+    if (card.eleType !== 'button') {
       return
     }
     
@@ -139,7 +139,6 @@
               editCard={editCard}
               updateMarks={updateMarks}
               changeStyle={changeStyle}
-              doubleClickCard={doubleClickCard}
               delCard={delCard}
               findCard={findCard}
             />
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/index.scss b/src/menu/components/card/cardcellcomponent/dragaction/index.scss
index 2b2ec2c..b851536 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/index.scss
+++ b/src/menu/components/card/cardcellcomponent/dragaction/index.scss
@@ -100,6 +100,23 @@
     font-weight: inherit;
     font-style: inherit;
   }
+  .ant-mk-check {
+    white-space: nowrap;
+    overflow: hidden;
+    word-break: break-word;
+    text-overflow: ellipsis;
+    font-weight: inherit;
+    font-style: inherit;
+    .ant-checkbox-wrapper {
+      font-weight: inherit;
+      font-size: inherit;
+      font-style: inherit;
+      color: inherit;
+      span {
+        font-weight: inherit;
+      }
+    }
+  }
   .ant-mk-picture {
     background-size: cover;
     background-position: center center;
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index 38de87f..1a5f1d4 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -10,6 +10,7 @@
 const { TextArea } = Input
 const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
 const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
 
 const cardTypeOptions = {
   sequence: ['eleType', 'width'],
@@ -23,6 +24,7 @@
   barcode: ['eleType', 'datatype', 'width', 'barHeight', 'displayValue', 'interval'],
   qrcode: ['eleType', 'datatype', 'width', 'qrWidth', 'color', 'url'],
   currentDate: ['eleType', 'width', 'dateFormat', 'prefix', 'postfix'],
+  formula: ['eleType', 'width', 'height', 'prefix', 'postfix', 'formula'],
 }
 
 class MainSearch extends Component {
@@ -55,12 +57,12 @@
         if (item.key === 'field' || item.key === 'linkurl') {
           item.options = []
           config.columns.forEach(col => {
-            if (!/^Nvarchar/ig.test(col.datatype) && (card.eleType === 'number' || card.eleType === 'slider')) {
+            if (/^(Int|Decimal)/ig.test(col.datatype) && (card.eleType === 'number' || card.eleType === 'slider')) {
               item.options.push({
                 value: col.field,
                 text: col.label
               })
-            } else if (/^Nvarchar/ig.test(col.datatype) && card.eleType !== 'number' && card.eleType !== 'slider') {
+            } else if (/^(Nvarchar|date)/ig.test(col.datatype) && card.eleType !== 'number' && card.eleType !== 'slider') {
               item.options.push({
                 value: col.field,
                 text: col.label
@@ -108,6 +110,8 @@
         } else if (link === 'linkpage') {
           _options.push('linkmenu', 'joint', 'open')
         }
+      } else if (eleType === 'picture' && !link) {
+        _options.push('scale')
       }
     } else if (eleType === 'icon') {
       if (datatype === 'dynamic') {
@@ -139,12 +143,12 @@
         if (item.key === 'field') {
           item.options = []
           config.columns.forEach(col => {
-            if (!/^Nvarchar/ig.test(col.datatype) && (value === 'number' || value === 'slider')) {
+            if (/^(Int|Decimal)/ig.test(col.datatype) && (value === 'number' || value === 'slider')) {
               item.options.push({
                 value: col.field,
                 text: col.label
               })
-            } else if (/^Nvarchar/ig.test(col.datatype) && value !== 'number' && value !== 'slider') {
+            } else if (/^(Nvarchar|date)/ig.test(col.datatype) && value !== 'number' && value !== 'slider') {
               item.options.push({
                 value: col.field,
                 text: col.label
@@ -298,7 +302,7 @@
                     message: formRule.input.message
                   }
                 ]
-              })(<TextArea rows={2} disabled={item.readonly} />)}
+              })(<TextArea rows={2} disabled={item.readonly} placeholder={item.placeholder || ''} />)}
             </Form.Item>
           </Col>
         )
@@ -353,6 +357,27 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'icon') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [{
+                  required: !!item.required,
+                  message: this.props.dict['form.required.select'] + item.label + '!'
+                }]
+              })(
+                <MkIcon />
+              )}
+            </Form.Item>
+          </Col>
+        )
       } else if (item.type === 'radio') {
         fields.push(
           <Col span={12} key={index}>
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index ac0423d..f77d270 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -20,11 +20,13 @@
     { value: 'barcode', text: '鏉″舰鐮�'},
     { value: 'qrcode', text: '浜岀淮鐮�'},
     { value: 'currentDate', text: '褰撳墠鏃堕棿'},
+    { value: 'formula', text: '鍏紡'},
   ]
 
   if (type === 'table' || (type === 'card' && subtype === 'datacard')) {
     _options.push({value: 'sequence', text: '搴忓彿'})
   }
+
   let appMenus = []
   const isApp = sessionStorage.getItem('appType') === 'pc'
 
@@ -33,7 +35,6 @@
     if (appMenus) {
       try {
         appMenus = JSON.parse(appMenus)
-        appMenus = appMenus.map(item => ({value: item.MenuID, text: item.MenuName}))
       } catch {
         appMenus = []
       }
@@ -63,45 +64,11 @@
       options: _options
     },
     {
-      type: 'select',
+      type: 'icon',
       key: 'icon',
       label: '鍥炬爣',
       initVal: card.icon,
-      required: true,
-      options: [
-        { value: 'question-circle', text: 'question-circle'},
-        { value: 'alert', text: 'alert'},
-        { value: 'cloud', text: 'cloud'},
-        { value: 'eye', text: 'eye'},
-        { value: 'eye-invisible', text: 'eye-invisible'},
-        { value: 'android', text: 'android'},
-        { value: 'apple', text: 'apple'},
-        { value: 'windows', text: 'windows'},
-        { value: 'ie', text: 'ie'},
-        { value: 'chrome', text: 'chrome'},
-        { value: 'github', text: 'github'},
-        { value: 'aliwangwang', text: 'aliwangwang'},
-        { value: 'dingding', text: 'dingding'},
-        { value: 'wechat', text: 'wechat'},
-        { value: 'alipay', text: 'alipay'},
-        { value: 'weibo-square', text: 'weibo-square'},
-        { value: 'weibo-circle', text: 'weibo-circle'},
-        { value: 'taobao-circle', text: 'taobao-circle'},
-        { value: 'weibo', text: 'weibo'},
-        { value: 'twitter', text: 'twitter'},
-        { value: 'youtube', text: 'youtube'},
-        { value: 'alipay-circle', text: 'alipay-circle'},
-        { value: 'taobao', text: 'taobao'},
-        { value: 'skype', text: 'skype'},
-        { value: 'qq', text: 'qq'},
-        { value: 'gitlab', text: 'gitlab'},
-        { value: 'zhihu', text: 'zhihu'},
-        { value: 'slack', text: 'slack'},
-        { value: 'sketch', text: 'sketch'},
-        { value: 'yahoo', text: 'yahoo'},
-        { value: 'reddit', text: 'reddit'},
-        { value: 'dribbble', text: 'dribbble'},
-      ]
+      required: true
     },
     {
       type: 'radio',
@@ -354,7 +321,7 @@
       ]
     },
     {
-      type: 'select',
+      type: 'radio',
       key: 'link',
       label: '閾炬帴',
       initVal: card.link || '',
@@ -362,9 +329,19 @@
       forbid: !isApp,
       options: [
         { value: '', text: '鏃�' },
-        // { value: 'page', text: '鑿滃崟' },
         { value: 'linkpage', text: '鍏宠仈鑿滃崟' },
         { value: 'custom', text: '閾炬帴' }
+      ]
+    },
+    {
+      type: 'radio',
+      key: 'scale',
+      label: '鍥剧墖鏀惧ぇ',
+      initVal: card.scale || 'false',
+      required: false,
+      options: [
+        { value: 'false', text: '涓嶅彲浠�' },
+        { value: 'true', text: '鍙互' }
       ]
     },
     {
@@ -419,6 +396,15 @@
       required: true,
       options: []
     },
+    {
+      type: 'textarea',
+      key: 'formula',
+      label: '鍏紡',
+      initVal: card.formula || '',
+      tooltip: '鎵ц鏃朵細浣跨敤鏌ヨ鍒扮殑鏁版嵁鏇挎崲鐩稿簲鐨勫瓧娈碉紝灞曠ず鑾峰緱鐨勭粨鏋溿��',
+      placeholder: '渚嬪锛欯price@ * @number@',
+      required: true
+    },
   ]
 
   return forms
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index e46935d..8dce9dd 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -161,6 +161,12 @@
 
     if (comIds.length !== 3 || comIds[0] !== cards.uuid || comIds[1] !== cardCell.uuid || !card) return
 
+    if (card.eleType === 'button') {
+      if ((style.paddingLeft || style.paddingRight) && !style.width) {
+        style.width = 'auto'
+      }
+    }
+
     let _card = this.resetCardStyle(card, style)
 
     let _elements = elements.map(cell => {
@@ -178,7 +184,7 @@
   resetCardStyle = (card, style) => {
     let _card = fromJS(card).toJS()
     
-    if (_card.eleType === 'text' || _card.eleType === 'number') {
+    if (['text', 'number', 'formula'].includes(_card.eleType)) {
       _card.style = style
 
       let fontSize = 14
@@ -297,11 +303,15 @@
    * @description 鍙栨秷淇濆瓨锛屽鏋滃厓绱犱负鏂版坊鍏冪礌锛屽垯浠庡簭鍒椾腑鍒犻櫎
    */
   editModalCancel = () => {
-    const { card, elements } = this.state
+    const { card, elements, appType } = 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
     }
@@ -311,6 +321,8 @@
       elements: _elements,
       visible: false,
       actvisible: false
+    }, () => {
+      this.props.updateElement(_elements)
     })
   }
 
@@ -328,7 +340,7 @@
           if (res.eleType === 'splitline' && cell.eleType !== 'splitline') {
             res.style.paddingTop = '5px'
             res.style.paddingBottom = '5px'
-          } else if (res.eleType === 'text' || res.eleType === 'number') {
+          } else if (['text', 'number', 'formula'].includes(res.eleType)) {
             let fontSize = 14
             let lineHeight = 1.5
             let line = res.height || null
@@ -595,94 +607,96 @@
           handleSubConfig={this.handleSubConfig}
           deleteMenu={this.deleteElement}
         />
-        {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
-        <Modal
-          title={'缂栬緫鍏冪礌'}
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          onCancel={this.editModalCancel}
-          onOk={this.handleSubmit}
-          destroyOnClose
-        >
-          <ElementForm
-            dict={dict}
-            card={card}
-            formlist={this.state.formlist}
-            inputSubmit={this.handleSubmit}
-            config={cards}
-            wrappedComponentRef={(inst) => this.elementFormRef = inst}
-          />
-        </Modal>
-        {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
-        <Modal
-          title={dict['model.action'] + '-' + (card && card.copyType === 'action' ? dict['model.copy'] : dict['model.edit'])}
-          visible={actvisible}
-          width={800}
-          maskClosable={false}
-          onCancel={this.editModalCancel}
-          footer={[
-            <Button key="cancel" onClick={this.editModalCancel}>{dict['model.cancel']}</Button>,
-            <Button key="confirm" type="primary" onClick={this.handleActionSubmit}>{dict['model.confirm']}</Button>
-          ]}
-          destroyOnClose
-        >
-          <ActionForm
-            dict={dict}
-            type={cards.type === 'balcony' ? '' : 'card'}
-            card={card}
-            formlist={this.state.formlist}
-            inputSubmit={this.handleActionSubmit}
-            setting={cards.setting}
-            wrappedComponentRef={(inst) => this.actionFormRef = inst}
-          />
-        </Modal>
-        {/* 鎸夐挳浣跨敤绯荤粺瀛樺偍杩囩▼鏃讹紝楠岃瘉淇℃伅妯℃�佹 */}
-        <Modal
-          wrapClassName="model-table-action-verify-modal"
-          title={'楠岃瘉淇℃伅'}
-          visible={profVisible}
-          width={'75vw'}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ profVisible: false }) }}
-          destroyOnClose
-        >
-          {card && !card.execMode && card.OpenType !== 'excelIn' && card.OpenType !== 'excelOut' ?
-            <VerifyCard
-              card={card}
+        <div onDoubleClick={(e) => e.stopPropagation()}>
+          {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
+          <Modal
+            title={'缂栬緫鍏冪礌'}
+            visible={visible}
+            width={800}
+            maskClosable={false}
+            onCancel={this.editModalCancel}
+            onOk={this.handleSubmit}
+            destroyOnClose
+          >
+            <ElementForm
               dict={dict}
+              card={card}
+              formlist={this.state.formlist}
+              inputSubmit={this.handleSubmit}
               config={cards}
-              columns={cards.columns}
-              wrappedComponentRef={(inst) => this.verifyRef = inst}
-            /> : null
-          }
-          {card && card.execMode ?
-            <VerifyPrint
-              card={card}
+              wrappedComponentRef={(inst) => this.elementFormRef = inst}
+            />
+          </Modal>
+          {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
+          <Modal
+            title={dict['model.action'] + '-' + (card && card.copyType === 'action' ? dict['model.copy'] : dict['model.edit'])}
+            visible={actvisible}
+            width={800}
+            maskClosable={false}
+            onCancel={this.editModalCancel}
+            footer={[
+              <Button key="cancel" onClick={this.editModalCancel}>{dict['model.cancel']}</Button>,
+              <Button key="confirm" type="primary" onClick={this.handleActionSubmit}>{dict['model.confirm']}</Button>
+            ]}
+            destroyOnClose
+          >
+            <ActionForm
               dict={dict}
-              columns={cards.columns}
-              wrappedComponentRef={(inst) => this.verifyRef = inst}
-            /> : null
-          }
-          {card && card.OpenType === 'excelIn' ?
-            <VerifyExcelIn
+              type={cards.type === 'balcony' ? '' : 'card'}
               card={card}
-              dict={dict}
-              columns={cards.columns}
-              wrappedComponentRef={(inst) => this.verifyRef = inst}
-            /> : null
-          }
-          {card && card.OpenType === 'excelOut' ?
-            <VerifyExcelOut
-              card={card}
-              dict={dict}
-              config={cards}
-              wrappedComponentRef={(inst) => this.verifyRef = inst}
-            /> : null
-          }
-        </Modal>
+              formlist={this.state.formlist}
+              inputSubmit={this.handleActionSubmit}
+              setting={cards.setting}
+              wrappedComponentRef={(inst) => this.actionFormRef = inst}
+            />
+          </Modal>
+          {/* 鎸夐挳浣跨敤绯荤粺瀛樺偍杩囩▼鏃讹紝楠岃瘉淇℃伅妯℃�佹 */}
+          <Modal
+            wrapClassName="model-table-action-verify-modal"
+            title={'楠岃瘉淇℃伅'}
+            visible={profVisible}
+            width={'75vw'}
+            maskClosable={false}
+            okText={dict['model.submit']}
+            onOk={this.verifySubmit}
+            onCancel={() => { this.setState({ profVisible: false }) }}
+            destroyOnClose
+          >
+            {card && !card.execMode && card.OpenType !== 'excelIn' && card.OpenType !== 'excelOut' ?
+              <VerifyCard
+                card={card}
+                dict={dict}
+                config={cards}
+                columns={cards.columns}
+                wrappedComponentRef={(inst) => this.verifyRef = inst}
+              /> : null
+            }
+            {card && card.execMode ?
+              <VerifyPrint
+                card={card}
+                dict={dict}
+                columns={cards.columns}
+                wrappedComponentRef={(inst) => this.verifyRef = inst}
+              /> : null
+            }
+            {card && card.OpenType === 'excelIn' ?
+              <VerifyExcelIn
+                card={card}
+                dict={dict}
+                columns={cards.columns}
+                wrappedComponentRef={(inst) => this.verifyRef = inst}
+              /> : null
+            }
+            {card && card.OpenType === 'excelOut' ?
+              <VerifyExcelOut
+                card={card}
+                dict={dict}
+                config={cards}
+                wrappedComponentRef={(inst) => this.verifyRef = inst}
+              /> : null
+            }
+          </Modal>
+        </div>
       </div>
     )
   }
diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index af376d8..0709e4f 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -1,21 +1,20 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Modal, Popover, Icon, Switch, Col } from 'antd'
+import { Popover, Icon, Switch, Col } from 'antd'
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
 import { resetStyle } from '@/utils/utils-custom.js'
+import getSettingForm from './options'
 import Utils from '@/utils/utils.js'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
-const PasteComponent = asyncIconComponent(() => import('./pastecomponent'))
+const PasteController = asyncIconComponent(() => import('@/components/paste'))
 
 class CardBoxComponent extends Component {
   static propTpyes = {
@@ -28,12 +27,9 @@
   }
 
   state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
     formlist: null,        // 璁剧疆琛ㄥ崟淇℃伅
     elements: null,        // 缂栬緫缁�
-    visible: false,        // 妯℃�佹鎺у埗
-    settingVisible: false,
     side: 'front'
   }
 
@@ -89,7 +85,7 @@
     this.props.updateElement(_card)
   }
 
-  updateCard = (elements, type) => {
+  updateCard = (elements) => {
     const { card, side } = this.state
 
     let _card = {}
@@ -100,16 +96,9 @@
       _card = {...card, elements: elements}
     }
 
-    if (type === 'paste') {
-      this.setState({
-        card: _card,
-        elements: fromJS(elements).toJS()
-      })
-    } else {
-      this.setState({
-        card: _card
-      })
-    }
+    this.setState({
+      card: _card
+    })
 
     this.props.updateElement(_card)
   }
@@ -155,24 +144,9 @@
     const { cards } = this.props
     const { card } = this.state
 
-    let newcard = {}
+    let newcard = {eleType: 'button', label: 'button', verify: null, show: 'link', sqlType: '', Ot: 'requiredSgl', OpenType: 'prompt', icon: '', class: 'primary', intertype: 'system', execSuccess: 'grid', execError: 'never', popClose: 'never'}
     newcard.uuid = Utils.getuuid()
     newcard.focus = true
-    
-    newcard.eleType = 'button'
-    newcard.label = 'button'
-    newcard.sqlType = ''
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'prompt'
-    newcard.icon = ''
-    newcard.class = 'primary'
-    newcard.intertype = 'system'
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.popClose = 'never'
-    newcard.errorTime = 10
-    newcard.verify = null
-    newcard.show = 'link'
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
     MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
@@ -194,24 +168,50 @@
     MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, _style)
   }
 
-  settingSubmit = () => {
+  getSettingForms = () => {
+    const { cards } = this.props
+    const { setting } = this.state.card
+
+    return getSettingForm(setting, cards.subtype === 'propcard')
+  }
+
+  updateSetting = (res) => {
     const { card, side } = this.state
 
-    this.settingRef.handleConfirm().then(res => {
-      this.setState({
-        settingVisible: false,
-        card: {...card, setting: res}
-      })
-
-      if (side === 'back' && res.type === 'simple') {
-        this.setState({
-          side: 'front',
-          elements: fromJS(card.elements).toJS()
-        })
-      }
-
-      this.props.updateElement({...card, setting: res})
+    this.setState({
+      card: {...card, setting: res}
     })
+
+    if (side === 'back' && res.type === 'simple') {
+      this.setState({
+        side: 'front',
+        elements: fromJS(card.elements).toJS()
+      })
+    }
+
+    this.props.updateElement({...card, setting: res})
+  }
+
+  paste = (element, resolve) => {
+    const { cards } = this.props
+    const { card } = this.state
+
+    let _uuid = Utils.getuuid()
+    
+    if (element.copyType === 'action' && element.OpenType === 'popview') { // 寮圭獥鏍囩澶嶅埗
+      let _cell = fromJS(element).toJS()
+      _cell.$originUuid = element.uuid
+      _cell.uuid = _uuid
+      MKEmitter.emit('copyButtons', [_cell])
+    }
+
+    element.uuid = _uuid
+    element.focus = true
+
+    resolve({status: true})
+
+    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
+    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], element)
   }
 
   clickComponent = (e) => {
@@ -231,13 +231,9 @@
 
   render() {
     const { cards, offset } = this.props
-    const { card, elements, side, settingVisible, dict } = this.state
+    const { card, elements, side } = this.state
 
     let _style = {...card.style}
-
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
 
     if (side === 'back') {
       _style = {
@@ -255,14 +251,16 @@
       <Col span={card.setting.width || 6} offset={offset || 0}>
         <div className="card-item" style={_style} onClick={this.clickComponent} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
           <CardCellComponent cards={cards} cardCell={card} side={side} elements={elements} updateElement={this.updateCard}/>
-          <div className="card-control">
+          <div className="card-control" onDoubleClick={(e) => e.stopPropagation()}>
             <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
               <div className="mk-popover-control">
                 <Icon className="plus" title="娣诲姞鍏冪礌" onClick={this.addElement} type="plus" />
                 <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
-                <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.setState({settingVisible: true})} />
+                <NormalForm title="鍗$墖璁剧疆" width={800} update={this.updateSetting} getForms={this.getSettingForms}>
+                  <Icon type="edit" className="edit" title="缂栬緫"/>
+                </NormalForm>
                 <CopyComponent type="cardcell" card={card}/>
-                <PasteComponent elements={elements} options={['action', 'customCardElement']} updateConfig={(list) => this.updateCard(list, 'paste')} />
+                <PasteController options={['action', 'customCardElement']} updateConfig={this.paste} />
                 <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
                 <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                   <div className="mk-popover-control">
@@ -280,25 +278,6 @@
             </Popover>
           </div>
         </div>
-        <Modal
-          wrapClassName="popview-modal"
-          title={'鍗$墖璁剧疆'}
-          visible={settingVisible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.settingSubmit}
-          onCancel={() => { this.setState({ settingVisible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            cards={cards}
-            setting={card.setting}
-            inputSubmit={this.settingSubmit}
-            wrappedComponentRef={(inst) => this.settingRef = inst}
-          />
-        </Modal>
       </Col>
     )
   }
diff --git a/src/menu/components/card/cardcomponent/options.jsx b/src/menu/components/card/cardcomponent/options.jsx
new file mode 100644
index 0000000..431e2be
--- /dev/null
+++ b/src/menu/components/card/cardcomponent/options.jsx
@@ -0,0 +1,152 @@
+/**
+ * @description Setting琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (setting, hasPrimaryKey) {
+  let appType = sessionStorage.getItem('appType')
+
+  let menulist = []
+  let appmenulist = []
+
+  if (appType) {
+    appmenulist = sessionStorage.getItem('appMenus')
+    if (appmenulist) {
+      try {
+        appmenulist = JSON.parse(appmenulist)
+      } catch {
+        appmenulist = []
+      }
+    } else {
+      appmenulist = []
+    }
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  }
+
+  const cardSettingForm = [
+    {
+      type: 'number',
+      field: 'width',
+      label: '鍗$墖瀹藉害',
+      initval: setting.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'radio',
+      field: 'type',
+      label: '鍗$墖绫诲瀷',
+      initval: setting.type || 'simple',
+      tooltip: '閫夋嫨澶嶅紡鍗℃椂锛屽彲閰嶇疆榧犳爣鎮诞鏃剁殑鏄剧ず淇℃伅銆�',
+      required: false,
+      options: [
+        {value: 'simple', label: '鍗曞崱'},
+        {value: 'multi', label: '澶嶅紡鍗�'},
+      ],
+      controlFields: [
+        {field: 'transform', values: ['multi']},
+      ],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'select',
+      field: 'transform',
+      label: '杩囨浮鏁堟灉',
+      initval: setting.transform || 'up',
+      tooltip: '澶嶅紡鍗$墖榧犳爣鎮诞淇℃伅鐨勫姩鐢绘晥鏋溿��',
+      required: false,
+      options: [
+        {value: 'up', label: '鍚戜笂婊戝姩'},
+        {value: 'down', label: '鍚戜笅婊戝姩'},
+        {value: 'left', label: '鍚戝乏婊戝姩'},
+        {value: 'right', label: '鍚戝彸婊戝姩'},
+        {value: 'scale', label: '缂╂斁'},
+        {value: 'opacity', label: '閫忔槑搴�'},
+        {value: 'rotateX', label: '绾靛悜灞曞紑'},
+        {value: 'rotateY', label: '妯悜灞曞紑'},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'primaryId',
+      label: '涓婚敭鍊�',
+      initval: setting.primaryId || '',
+      tooltip: '璁剧疆涓�涓睘鎬у崱闈欐�両D锛屽悜鍏朵粬缁勪欢浼犻�掔殑鎸囧畾闈欐�両D鍊�',
+      required: false,
+      forbid: !hasPrimaryKey
+    },
+    {
+      type: 'radio',
+      field: 'click',
+      label: '鐐瑰嚮浜嬩欢',
+      initval: setting.click || '',
+      tooltip: '褰撻�夋嫨瑙﹀彂鎸夐挳鏃讹紝鍙湁褰撳崱鐗囦腑鍙瓨鍦ㄤ竴涓寜閽椂鏈夋晥銆�',
+      required: false,
+      options: [
+        {value: '', label: '鏃�'},
+        {value: 'menu', label: '鑿滃崟'},
+        {value: 'link', label: '閾炬帴'},
+        {value: 'button', label: '鎸夐挳'},
+      ],
+      controlFields: [
+        {field: 'menu', values: ['menu']},
+        {field: 'linkurl', values: ['link']},
+        {field: 'open', values: ['menu', 'link']},
+        {field: 'joint', values: ['menu', 'link']},
+      ]
+    },
+    {
+      type: appType ? 'select' : 'cascader',
+      field: 'menu',
+      label: '鍏宠仈鑿滃崟',
+      initval: setting.menu || (appType ? '' : []),
+      required: true,
+      options: appType ? appmenulist : menulist,
+    },
+    {
+      type: 'textarea',
+      field: 'linkurl',
+      label: '閾炬帴',
+      initval: setting.linkurl || '',
+      required: true,
+      options: [],
+      span: 24
+    },
+    {
+      type: 'radio',
+      field: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initval: setting.open || 'blank',
+      required: false,
+      options: [
+        {value: 'blank', label: '鏂扮獥鍙�'},
+        {value: 'self', label: '褰撳墠绐楀彛'},
+      ],
+      forbid: appType !== 'pc'
+    },
+    {
+      type: 'radio',
+      field: 'joint',
+      label: '鍙傛暟鎷兼帴',
+      initval: setting.joint || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+    },
+  ]
+
+  return cardSettingForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/card/cardcomponent/pastecomponent/index.jsx b/src/menu/components/card/cardcomponent/pastecomponent/index.jsx
deleted file mode 100644
index bc98b34..0000000
--- a/src/menu/components/card/cardcomponent/pastecomponent/index.jsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { fromJS } from 'immutable'
-import { Icon, Modal, notification } from 'antd'
-
-import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import asyncComponent from '@/utils/asyncComponent'
-import './index.scss'
-
-const PasteForm = asyncComponent(() => import('@/templates/zshare/pasteform'))
-
-class PasteController extends Component {
-  static propTpyes = {
-    config: PropTypes.object,        // 缁勪欢閰嶇疆
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    visible: false
-  }
-
-  handleMenuClick = () => {
-    this.setState({visible: true})
-  }
-
-  pasteSubmit = () => {
-    const { options, elements } = this.props
-    this.pasteFormRef.handleConfirm().then(res => {
-      if (!options.includes(res.copyType)) {
-        notification.warning({ top: 92, message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�', duration: 5 })
-        return
-      }
-
-      let _uuid = Utils.getuuid()
-      if (res.copyType === 'action' && res.OpenType === 'popview') {
-        let _cell = fromJS(res).toJS()
-        _cell.$originUuid = res.uuid
-        _cell.uuid = _uuid
-        MKEmitter.emit('copyButtons', [_cell])
-      }
-      res.uuid = _uuid
-
-      this.props.updateConfig([...elements, res])
-      this.setState({visible: false})
-
-      notification.success({
-        top: 92,
-        message: '绮樿创鎴愬姛锛�',
-        duration: 2
-      })
-    })
-  }
-
-  render() {
-    const { visible } = this.state
-
-    return (
-      <div style={{display: 'inline-block'}}>
-        <Icon type="snippets" 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 PasteController
\ No newline at end of file
diff --git a/src/menu/components/card/cardcomponent/settingform/index.jsx b/src/menu/components/card/cardcomponent/settingform/index.jsx
deleted file mode 100644
index 6424977..0000000
--- a/src/menu/components/card/cardcomponent/settingform/index.jsx
+++ /dev/null
@@ -1,257 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Tooltip, Icon, Input, InputNumber, Select, Cascader } from 'antd'
-
-import './index.scss'
-
-const { TextArea } = Input
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    cards: PropTypes.object,     // 鍗$墖闆�
-    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    type: this.props.setting.type || 'simple',
-    click: this.props.setting.click || '',
-    appType: sessionStorage.getItem('appType'),
-    menulist: []
-  }
-
-  UNSAFE_componentWillMount() {
-    const { appType } = this.state
-    let menulist = null
-
-    if (appType) {
-      menulist = sessionStorage.getItem('appMenus')
-    } else {
-      menulist = sessionStorage.getItem('fstMenuList')
-    }
-
-    if (menulist) {
-      try {
-        menulist = JSON.parse(menulist)
-      } catch {
-        menulist = []
-      }
-    } else {
-      menulist = []
-    }
-    this.setState({menulist})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { setting, cards } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { menulist, click, appType } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-card-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  鍗$墖瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: setting.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
-              </Form.Item>
-            </Col>
-            {appType !== 'mob' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨澶嶅紡鍗℃椂锛屽彲閰嶇疆榧犳爣鎮诞鏃剁殑鏄剧ず淇℃伅銆�">
-                  <Icon type="question-circle" />
-                  鍗$墖绫诲瀷
-                </Tooltip>
-              }>
-                {getFieldDecorator('type', {
-                  initialValue: setting.type || 'simple'
-                })(
-                  <Radio.Group onChange={(e) => this.setState({ type: e.target.value })}>
-                    <Radio value="simple">鍗曞崱</Radio>
-                    <Radio value="multi">澶嶅紡鍗�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {this.state.type === 'multi' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="澶嶅紡鍗$墖榧犳爣鎮诞淇℃伅鐨勫姩鐢绘晥鏋溿��">
-                  <Icon type="question-circle" />
-                  杩囨浮鏁堟灉
-                </Tooltip>
-              }>
-                {getFieldDecorator('transform', {
-                  initialValue: setting.transform || 'up'
-                })(
-                  <Select>
-                    <Select.Option value="up">鍚戜笂婊戝姩</Select.Option>
-                    <Select.Option value="down">鍚戜笅婊戝姩</Select.Option>
-                    <Select.Option value="left">鍚戝乏婊戝姩</Select.Option>
-                    <Select.Option value="right">鍚戝彸婊戝姩</Select.Option>
-                    <Select.Option value="scale">缂╂斁</Select.Option>
-                    <Select.Option value="opacity">閫忔槑搴�</Select.Option>
-                    <Select.Option value="rotateX">绾靛悜灞曞紑</Select.Option>
-                    <Select.Option value="rotateY">妯悜灞曞紑</Select.Option>
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {cards.subtype === 'propcard' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鍗$墖鐐瑰嚮鏃讹紝鍚戝叾浠栫粍浠朵紶閫掔殑BID鍊笺��">
-                  <Icon type="question-circle" />
-                  涓婚敭鍊�
-                </Tooltip>
-              }>
-                {getFieldDecorator('primaryId', {
-                  initialValue: setting.primaryId || ''
-                })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="褰撻�夋嫨瑙﹀彂鎸夐挳鏃讹紝鍙湁褰撳崱鐗囦腑鍙瓨鍦ㄤ竴涓寜閽椂鏈夋晥銆�">
-                  <Icon type="question-circle" />
-                  鐐瑰嚮浜嬩欢
-                </Tooltip>
-              }>
-                {getFieldDecorator('click', {
-                  initialValue: click
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({click: e.target.value})}>
-                    <Radio value="">鏃�</Radio>
-                    <Radio value="menu">鑿滃崟</Radio>
-                    <Radio value="link">閾炬帴</Radio>
-                    <Radio value="button">鎸夐挳</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {!appType && click === 'menu' ? <Col span={12}>
-              <Form.Item label="鑿滃崟">
-                {getFieldDecorator('menu', {
-                  initialValue: setting.menu || [],
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鑿滃崟!'
-                    }
-                  ]
-                })(
-                  <Cascader options={menulist} placeholder=""/>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {appType && click === 'menu' ? <Col span={12}>
-              <Form.Item label="鍏宠仈鑿滃崟">
-                {getFieldDecorator('menu', {
-                  initialValue: setting.menu || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鍏宠仈鑿滃崟!'
-                    }
-                  ]
-                })(
-                  <Select
-                    showSearch
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {menulist.map(option =>
-                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {click === 'link' ? <Col span={24} className="textarea">
-              <Form.Item label="閾炬帴">
-                {getFieldDecorator('linkurl', {
-                  initialValue: setting.linkurl || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '閾炬帴!'
-                    }
-                  ]
-                })( <TextArea rows={2}/> )}
-              </Form.Item>
-            </Col> : null}
-            {appType === 'pc' && click !== '' && click !== 'button' ? <Col span={12}>
-              <Form.Item label="鎵撳紑鏂瑰紡">
-                {getFieldDecorator('open', {
-                  initialValue: setting.open || 'blank'
-                })(
-                  <Radio.Group>
-                    <Radio value="blank">鏂扮獥鍙�</Radio>
-                    <Radio value="self">褰撳墠绐楀彛</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {click !== '' && click !== 'button' ? <Col span={12}>
-              <Form.Item label="鍙傛暟鎷兼帴">
-                {getFieldDecorator('joint', {
-                  initialValue: setting.joint || 'true'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄�</Radio>
-                    <Radio value="false">鍚�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/card/cardcomponent/settingform/index.scss b/src/menu/components/card/cardcomponent/settingform/index.scss
deleted file mode 100644
index 36d76eb..0000000
--- a/src/menu/components/card/cardcomponent/settingform/index.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.model-menu-card-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .textarea {
-    .ant-form-item-label {
-      width: 16%;
-    }
-    .ant-form-item-control-wrapper {
-      width: 84%;
-    }
-  }
-  .ant-radio-wrapper {
-    margin-right: 3px;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/table-card/cardcomponent/index.jsx b/src/menu/components/card/cardsimplecomponent/index.jsx
similarity index 65%
rename from src/menu/components/card/table-card/cardcomponent/index.jsx
rename to src/menu/components/card/cardsimplecomponent/index.jsx
index a46143b..cdda53a 100644
--- a/src/menu/components/card/table-card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardsimplecomponent/index.jsx
@@ -1,36 +1,32 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Modal, Popover, Icon } from 'antd'
+import { Popover, Icon } from 'antd'
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
 import { resetStyle } from '@/utils/utils-custom.js'
+import { getTableSetting, getCarouselSetting } from './options'
 import Utils from '@/utils/utils.js'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
-const CardCellComponent = asyncComponent(() => import('../../cardcellcomponent'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 
 class CardBoxComponent extends Component {
   static propTpyes = {
     cards: PropTypes.object,         // 鍗$墖琛岄厤缃俊鎭�
     card: PropTypes.object,          // 鍗$墖閰嶇疆淇℃伅
+    move: PropTypes.func,            // 鍗$墖绉诲姩
     deleteElement: PropTypes.func,   // 鍗$墖鍒犻櫎
     updateElement: PropTypes.func    // 鑿滃崟閰嶇疆鏇存柊
   }
 
   state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
+    card: null,            // 鍗$墖淇℃伅
     formlist: null,        // 璁剧疆琛ㄥ崟淇℃伅
-    elements: null,        // 缂栬緫缁�
-    visible: false,        // 妯℃�佹鎺у埗
-    settingVisible: false,
   }
 
   /**
@@ -40,8 +36,7 @@
     const { card } = this.props
 
     this.setState({
-      card: fromJS(card).toJS(),
-      elements: fromJS(card.elements).toJS(),
+      card: fromJS(card).toJS()
     })
   }
 
@@ -113,24 +108,10 @@
     const { cards } = this.props
     const { card } = this.state
 
-    let newcard = {}
+    let newcard = {eleType: 'button', label: 'button', verify: null, show: 'link', sqlType: '', Ot: 'requiredSgl', OpenType: 'prompt', icon: '', class: 'primary', intertype: 'system', execSuccess: 'grid', execError: 'never', popClose: 'never'}
     newcard.uuid = Utils.getuuid()
     newcard.focus = true
     
-    newcard.eleType = 'button'
-    newcard.label = 'button'
-    newcard.sqlType = ''
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'prompt'
-    newcard.icon = ''
-    newcard.class = 'primary'
-    newcard.intertype = 'system'
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.popClose = 'never'
-    newcard.errorTime = 10
-    newcard.verify = null
-    newcard.show = 'link'
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
     MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
@@ -141,76 +122,77 @@
     const { card } = this.state
 
     let _style = null
-    let options = ['height', 'background', 'border', 'padding', 'margin']
+    let options = ['height', 'background', 'border', 'padding', 'margin', 'shadow']
     _style = card.style ? fromJS(card.style).toJS() : {}
+
+    if (cards.type === 'carousel') {
+      options = ['background', 'border', 'padding', 'margin', 'shadow']
+    }
 
     MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, _style)
   }
 
-  settingSubmit = () => {
+  getSettingForms = () => {
+    const { cards } = this.props
+    const { setting } = this.state.card
+
+    if (cards.type !== 'carousel') {
+      return getTableSetting(setting, cards.columns)
+    } else {
+      return getCarouselSetting(setting, cards.subtype === 'propcard')
+    }
+  }
+
+  updateSetting = (res) => {
     const { card } = this.state
 
-    this.settingRef.handleConfirm().then(res => {
-      this.setState({
-        settingVisible: false,
-        card: {...card, setting: res}
-      })
+    let _card = {...card, setting: res}
 
-      this.props.updateElement({...card, setting: res})
-    })
+    this.setState({ card: _card })
+    this.props.updateElement(_card)
   }
 
   render() {
     const { cards } = this.props
-    const { card, elements, settingVisible, dict } = this.state
-    let _style = resetStyle(card.style)
+    const { card } = this.state
+
+    let _style = {...card.style}
+
+    if (cards.type === 'carousel') {
+      _style.height = cards.style.height
+    }
+    
+    _style = resetStyle(_style)
 
     return (
       <div className="ant-col ant-col-24">
         <div className="card-item" style={_style}>
-          <CardCellComponent cards={cards} cardCell={card} elements={elements} updateElement={this.updateCard}/>
+          <CardCellComponent cards={cards} cardCell={card} elements={card.elements} updateElement={this.updateCard}/>
           <div className="card-control">
             <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
               <div className="mk-popover-control">
                 <Icon className="plus" title="娣诲姞鍏冪礌" onClick={this.addElement} type="plus" />
                 <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
-                <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.setState({settingVisible: true})} />
+                <NormalForm title="鍗$墖璁剧疆" width={700} update={this.updateSetting} getForms={this.getSettingForms}>
+                  <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+                </NormalForm>
                 <CopyComponent type="cardcell" card={card}/>
                 <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
-                <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+                {cards.subtype !== 'datacard' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                   <div className="mk-popover-control">
                     <Icon className="plus" title="鍓嶇Щ" type="arrow-left" onClick={() => this.props.move(card, 'left')} />
                     <Icon className="close" title="鍚庣Щ" type="arrow-right" onClick={() => this.props.move(card, 'right')} />
                   </div>
                 } trigger="hover" getPopupContainer={() => document.getElementById(card.uuid + 'swap')}>
                   <Icon type="swap" id={card.uuid + 'swap'}/>
-                </Popover>
-                <Icon className="close" title="鍒犻櫎鍗$墖" type="delete" onClick={() => this.props.deleteElement(card)} />
+                </Popover> : null}
+                {cards.subtype !== 'datacard' ? <Icon className="close" title="鍒犻櫎鍗$墖" type="delete" onClick={() => this.props.deleteElement(card)} /> : null}
               </div>
             } trigger="hover">
               <Icon type="tool" />
             </Popover>
           </div>
         </div>
-        <Modal
-          wrapClassName="popview-modal"
-          title={'琛岃缃�'}
-          visible={settingVisible}
-          width={700}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.settingSubmit}
-          onCancel={() => { this.setState({ settingVisible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            cards={cards}
-            setting={card.setting}
-            inputSubmit={this.settingSubmit}
-            wrappedComponentRef={(inst) => this.settingRef = inst}
-          />
-        </Modal>
       </div>
     )
   }
diff --git a/src/menu/components/card/cardcomponent/pastecomponent/index.scss b/src/menu/components/card/cardsimplecomponent/index.scss
similarity index 100%
rename from src/menu/components/card/cardcomponent/pastecomponent/index.scss
rename to src/menu/components/card/cardsimplecomponent/index.scss
diff --git a/src/menu/components/card/cardsimplecomponent/options.jsx b/src/menu/components/card/cardsimplecomponent/options.jsx
new file mode 100644
index 0000000..bb414a4
--- /dev/null
+++ b/src/menu/components/card/cardsimplecomponent/options.jsx
@@ -0,0 +1,163 @@
+/**
+ * @description tablecard setting琛ㄥ崟閰嶇疆淇℃伅
+ */
+export function getTableSetting (setting, columns) {
+  let _columns = columns.map(item => ({value: item.field, label: item.label}))
+  _columns.push({value: '$Index', label: '搴忓彿锛堝墠绔級'})
+
+  const settingForm = [
+    {
+      type: 'radio',
+      field: 'condition',
+      label: '鏄剧ず鏉′欢',
+      initval: setting.condition || 'false',
+      tooltip: '褰撻�夋嫨鈥滄湁鈥濇椂锛屽彧鏈夌鍚堟潯浠剁殑鏁版嵁鎵嶄細灞曠ず銆�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏈�'},
+        {value: 'false', label: '鏃�'},
+      ],
+      controlFields: [
+        {field: 'controlField', values: ['true']},
+        {field: 'controlType', values: ['true']},
+        {field: 'controlValue', values: ['true']},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'controlField',
+      label: '鎺у埗瀛楁',
+      initval: setting.controlField || '',
+      required: true,
+      options: _columns
+    },
+    {
+      type: 'radio',
+      field: 'controlType',
+      label: '瀵规瘮鏂瑰紡',
+      initval: setting.controlType || '=',
+      required: true,
+      options: [
+        {value: '=', label: '='},
+        {value: '!=', label: '!='},
+        {value: '>', label: '>'},
+        {value: '<', label: '<'},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'controlValue',
+      label: '瀵规瘮鍊�',
+      initval: setting.controlValue || '',
+      required: false
+    }
+  ]
+
+  return settingForm
+}
+
+/**
+ * @description carousel setting琛ㄥ崟閰嶇疆淇℃伅
+ */
+export function getCarouselSetting (setting, hasPrimaryKey) {
+  let appType = sessionStorage.getItem('appType')
+
+  let menulist = []
+  let appmenulist = []
+
+  if (appType) {
+    appmenulist = sessionStorage.getItem('appMenus')
+    if (appmenulist) {
+      try {
+        appmenulist = JSON.parse(appmenulist)
+      } catch {
+        appmenulist = []
+      }
+    } else {
+      appmenulist = []
+    }
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  }
+
+  const cardSettingForm = [
+    {
+      type: 'text',
+      field: 'primaryId',
+      label: '涓婚敭鍊�',
+      initval: setting.primaryId || '',
+      tooltip: '璁剧疆涓�涓睘鎬у崱闈欐�両D锛屽悜鍏朵粬缁勪欢浼犻�掔殑鎸囧畾闈欐�両D鍊�',
+      required: false,
+      forbid: !hasPrimaryKey
+    },
+    {
+      type: 'radio',
+      field: 'click',
+      label: '鐐瑰嚮浜嬩欢',
+      initval: setting.click || '',
+      required: false,
+      options: [
+        {value: '', label: '鏃�'},
+        {value: 'menu', label: '鑿滃崟'},
+        {value: 'link', label: '閾炬帴'},
+      ],
+      controlFields: [
+        {field: 'menu', values: ['menu']},
+        {field: 'linkurl', values: ['link']},
+        {field: 'open', values: ['menu', 'link']},
+        {field: 'joint', values: ['menu', 'link']},
+      ]
+    },
+    {
+      type: appType ? 'select' : 'cascader',
+      field: 'menu',
+      label: '鍏宠仈鑿滃崟',
+      initval: setting.menu || (appType ? '' : []),
+      required: true,
+      options: appType ? appmenulist : menulist,
+    },
+    {
+      type: 'textarea',
+      field: 'linkurl',
+      label: '閾炬帴',
+      initval: setting.linkurl || '',
+      required: true,
+      options: [],
+      span: 24
+    },
+    {
+      type: 'radio',
+      field: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initval: setting.open || 'blank',
+      required: false,
+      options: [
+        {value: 'blank', label: '鏂扮獥鍙�'},
+        {value: 'self', label: '褰撳墠绐楀彛'},
+      ],
+      forbid: appType !== 'pc'
+    },
+    {
+      type: 'radio',
+      field: 'joint',
+      label: '鍙傛暟鎷兼帴',
+      initval: setting.joint || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+    },
+  ]
+
+  return cardSettingForm
+}
\ No newline at end of file
diff --git a/src/menu/components/card/data-card/index.jsx b/src/menu/components/card/data-card/index.jsx
index 5c85b95..4e09ba6 100644
--- a/src/menu/components/card/data-card/index.jsx
+++ b/src/menu/components/card/data-card/index.jsx
@@ -8,18 +8,19 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CardComponent = asyncComponent(() => import('../cardcomponent'))
 const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
-const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent'))
 
@@ -236,7 +237,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -253,48 +254,60 @@
     this.props.updateConfig(_card)
   }
 
-  addSearch = () => {
+  addSearch = (copy) => {
     const { card } = this.state
 
     let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
 
-    newcard.label = 'label'
-    newcard.type = 'select'
-    newcard.resourceType = '0'
-    newcard.options = []
-    newcard.setAll = 'false'
-    newcard.orderType = 'asc'
-    newcard.display = 'dropdown'
-    newcard.match = '='
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+  
+      newcard.label = 'label'
+      newcard.type = 'select'
+      newcard.resourceType = '0'
+      newcard.options = []
+      newcard.setAll = 'false'
+      newcard.orderType = 'asc'
+      newcard.display = 'dropdown'
+      newcard.match = '='
+    }
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鎼滅储
     MKEmitter.emit('addSearch', card.uuid, newcard)
   }
 
-  addButton = () => {
+  addButton = (copy) => {
     const { card } = this.state
 
     let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
-    
-    newcard.label = 'label'
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'pop'
-    newcard.icon = ''
-    newcard.class = 'green'
-    newcard.intertype = card.setting.interType || 'system'
-    newcard.innerFunc = card.setting.innerFunc || ''
-    newcard.sysInterface = card.setting.sysInterface || ''
-    newcard.outerFunc = card.setting.outerFunc || ''
-    newcard.interface = card.setting.interface || ''
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.verify = null
-    newcard.show = 'button'
-    newcard.style = {marginRight: '15px'}
+
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+      
+      newcard.label = 'label'
+      newcard.Ot = 'requiredSgl'
+      newcard.OpenType = 'pop'
+      newcard.icon = ''
+      newcard.class = 'green'
+      newcard.intertype = card.setting.interType || 'system'
+      newcard.innerFunc = card.setting.innerFunc || ''
+      newcard.sysInterface = card.setting.sysInterface || ''
+      newcard.outerFunc = card.setting.outerFunc || ''
+      newcard.interface = card.setting.interface || ''
+      newcard.execSuccess = 'grid'
+      newcard.execError = 'never'
+      newcard.verify = null
+      newcard.show = 'button'
+      newcard.style = {marginRight: '15px'}
+    }
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鎸夐挳
     MKEmitter.emit('addButton', card.uuid, newcard)
@@ -383,26 +396,32 @@
     }
   }
 
-  addCard = () => {
+  addCard = (copy) => {
     let card = fromJS(this.state.card).toJS()
-    let height = card.subcards[0].style.height
-    if (height === 'auto') {
-      height = '100px'
-    }
+    let newcard = {}
 
-    let newcard = {
-      uuid: Utils.getuuid(),
-      $cardType: 'extendCard',
-      setting: { width: 6, type: 'simple', click: 'button'},
-      style: {
-        height,
-        borderWidth: '1px', borderColor: '#e8e8e8',
-        paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
-        marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
-      },
-      backStyle: {},
-      elements: [],
-      backElements: []
+    if (copy) { // 绮樿创
+      newcard = copy
+    } else {
+      let height = card.subcards[0].style.height
+      if (height === 'auto') {
+        height = '100px'
+      }
+  
+      newcard = {
+        uuid: Utils.getuuid(),
+        $cardType: 'extendCard',
+        setting: { width: 6, type: 'simple', click: 'button'},
+        style: {
+          height,
+          borderWidth: '1px', borderColor: '#e8e8e8',
+          paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
+        },
+        backStyle: {},
+        elements: [],
+        backElements: []
+      }
     }
 
     card.subcards.push(newcard)
@@ -429,6 +448,147 @@
 
     this.setState({card})
     this.props.updateConfig(card)
+  }
+
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.subtype)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
+  pasteComponent = (res, resolve) => {
+    const { card, appType } = this.state
+
+    let type = res.copyType
+    delete res.copyType
+
+    if (type === 'cardcell') {
+      res.uuid = Utils.getuuid()
+      res.setting = res.setting || {}
+      res.$cardType = 'extendCard'
+      res.setting.width = res.setting.width || 6
+
+      let copyBtns = []
+      let mobtypes = ['pop', 'prompt', 'exec', 'innerpage']
+
+      let elements = []
+      res.elements && res.elements.forEach(cell => {
+        if (cell.datatype === 'dynamic') {
+          cell.datatype = 'static'
+        }
+
+        if (cell.eleType !== 'button') {
+          cell.uuid = Utils.getuuid()
+          elements.push(cell)
+        } 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
+          elements.push(cell)
+        }
+      })
+
+      res.elements = elements
+
+      let backElements = []
+
+      if (appType !== 'mob') {
+        res.backElements && res.backElements.forEach(cell => {
+          if (cell.datatype === 'dynamic') {
+            cell.datatype = 'static'
+          }
+
+          if (cell.eleType !== 'button') {
+            cell.uuid = Utils.getuuid()
+            backElements.push(cell)
+          } 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
+            backElements.push(cell)
+          }
+        })
+      }
+
+      res.backElements = backElements
+
+      if (copyBtns.length > 0) {
+        MKEmitter.emit('copyButtons', copyBtns)
+      }
+
+      resolve({status: true})
+
+      this.addCard(res)
+    } else if (type === 'search' || type === 'form') {
+      if (appType === 'mob') {
+        resolve({status: false, message: '绉诲姩绔暟鎹崱涓嶆敮鎸佹坊鍔犳悳绱㈡潯浠躲��'})
+      } else {
+        res.uuid = Utils.getuuid()
+        let keys = card.search.map(item => item.field.toLowerCase())
+  
+        if (type === 'form') {
+          if (['number', 'switch', 'textarea', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
+            res.type = 'text'
+          } else if (res.type === 'radio') {
+            res.type = 'select'
+          } else if (res.type === 'checkbox') {
+            res.type = 'multiselect'
+          } else if (res.type === 'datetime') {
+            res.type = 'date'
+          }
+        }
+  
+        if (res.field && keys.includes(res.field.toLowerCase())) {
+          resolve({status: false, message: '鎼滅储瀛楁宸插瓨鍦紒'})
+          return
+        }
+
+        resolve({status: true})
+
+        this.addSearch(res)
+      }
+    } else if (type === 'action') {
+      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
+        this.addButton(res)
+      }
+    }
   }
 
   clickComponent = (e) => {
@@ -459,12 +619,14 @@
         <NormalHeader defaultshow="hidden" config={card} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            {appType !== 'mob' ? <Icon className="plus" title="娣诲姞鍗$墖" onClick={this.addCard} type="plus" /> : null}
-            {appType !== 'mob' ? <Icon className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch} type="plus-circle" /> : null}
-            <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <Icon className="plus" title="娣诲姞鍗$墖" onClick={() => this.addCard()} type="plus" />
+            {appType !== 'mob' ? <Icon className="plus" title="娣诲姞鎼滅储" onClick={() => this.addSearch()} type="plus-circle" /> : null}
+            <Icon className="plus" title="娣诲姞鎸夐挳" onClick={() => this.addButton()} type="plus-square" />
+            <NormalForm title="鏁版嵁鍗¤缃�" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="datacard" card={card}/>
-            <PasteComponent config={card} options={['action', 'search', 'form', 'cardcell']} updateConfig={this.updateComponent} />
+            <PasteComponent options={['action', 'search', 'form', 'cardcell']} updateConfig={this.pasteComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
             <UserComponent config={card}/>
diff --git a/src/menu/components/card/data-card/options.jsx b/src/menu/components/card/data-card/options.jsx
new file mode 100644
index 0000000..c02de20
--- /dev/null
+++ b/src/menu/components/card/data-card/options.jsx
@@ -0,0 +1,174 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap, subtype, columns) {
+  let appType = sessionStorage.getItem('appType')
+  let MenuType = ''
+
+  if (window.GLOB.customMenu.parentId === 'BillPrintTemp') {
+    MenuType = 'billPrint'
+  }
+
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const cardWrapForm = [
+    {
+      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 || 'dynamic',
+      tooltip: '閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��',
+      required: false,
+      options: [
+        {value: 'dynamic', label: '鍔ㄦ��'},
+        {value: 'static', label: '闈欐��'},
+      ],
+      forbid: subtype !== 'propcard'
+    },
+    {
+      type: 'radio',
+      field: 'pagestyle',
+      label: '鍒嗛〉椋庢牸',
+      initval: wrap.pagestyle || 'page',
+      tooltip: '鏁版嵁婧愰�夋嫨鍒嗛〉鏃舵湁鏁堛��',
+      required: false,
+      options: [
+        {value: 'page', label: '椤电爜'},
+        {value: 'switch', label: '宸﹀彸鍒囨崲', forbid: appType === 'mob'},
+        {value: 'slide', label: '婊戝姩鍔犺浇', forbid: appType !== 'mob'},
+      ],
+      forbid: !(subtype === 'datacard' || (subtype === 'tablecard' && appType === 'mob'))
+    },
+    {
+      type: 'radio',
+      field: 'cardType',
+      label: '鏁版嵁閫夋嫨',
+      initval: wrap.cardType || '',
+      required: false,
+      options: [
+        {value: '', label: '涓嶅彲閫�'},
+        {value: 'radio', label: '鍗曢��'},
+        {value: 'checkbox', label: '澶氶��', forbid: subtype === 'propcard'},
+      ],
+      controlFields: [
+        {field: 'checkAll', values: ['checkbox']}
+      ],
+      forbid: subtype === 'tablecard'
+    },
+    {
+      type: 'radio',
+      field: 'checkAll',
+      label: '鍏ㄩ��',
+      initval: wrap.checkAll || 'hidden',
+      required: false,
+      options: [
+        {value: 'hidden', label: '闅愯棌'},
+        {value: 'show', label: '鏄剧ず'},
+      ],
+      forbid: subtype !== 'datacard' || appType !== 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'cardFloat',
+      label: '瀵归綈鏂瑰紡',
+      initval: wrap.cardFloat || 'left',
+      tooltip: '璁剧疆涓哄眳涓榻愭垨鍙冲榻愶紝鍙湪鍗$墖涓�1琛屾椂鏈夋晥銆�',
+      required: false,
+      options: [
+        {value: 'left', label: '宸﹀榻�'},
+        {value: 'center', label: '灞呬腑'},
+        {value: 'right', label: '鍙冲榻�'},
+      ],
+      forbid: subtype === 'tablecard'
+    },
+    {
+      type: 'radio',
+      field: 'scale',
+      label: '鏀惧ぇ鏁堟灉',
+      initval: wrap.scale || 'false',
+      tooltip: '榧犳爣鎮诞浜庡崱鐗囦笂鏂规椂锛屽崱鐗囨斁澶�1.05鍊嶃��',
+      required: false,
+      options: [
+        {value: 'false', label: '鏃�'},
+        {value: 'true', label: '鏈�'},
+      ],
+      forbid: subtype === 'tablecard' || appType === 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'printType',
+      label: '缁勪欢绫诲瀷',
+      initval: wrap.printType || 'content',
+      tooltip: '閫夋嫨绫诲瀷涓恒�婇〉鐪�/椤佃剼銆嬫椂锛屾墦鍗扮殑姣忛〉閲岄兘浼氬甫鏈夎缁勪欢銆�',
+      required: false,
+      options: [
+        {value: 'content', label: '鍐呭'},
+        {value: 'headerOrfooter', label: '椤电湁/椤佃剼'},
+      ],
+      forbid: subtype !== 'propcard' || MenuType !== 'billPrint'
+    },
+    {
+      type: 'select',
+      field: 'broadcast',
+      label: '璇煶鎾姤',
+      initval: wrap.broadcast || '',
+      tooltip: '璇煶鎾姤鍦ㄧЩ鍔ㄧapp涓湁鏁堛�傛敞锛氫娇鐢ㄨ闊虫挱鎶ユ椂锛屾暟鎹簮涓嶈浣跨敤鍚屾鏌ヨ锛屾坊鍔犲畾鏃跺櫒鏃讹紝鍙惊鐜挱鎶�',
+      required: false,
+      options: columns,
+      forbid: !columns || appType !== 'mob'
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return cardWrapForm.map(item => {
+    if (['pagestyle', 'cardType'].includes(item.field)) {
+      item.options = item.options.filter(option => !option.forbid)
+    }
+
+    return item
+  })
+} 
\ No newline at end of file
diff --git a/src/menu/components/card/data-card/wrapsetting/index.jsx b/src/menu/components/card/data-card/wrapsetting/index.jsx
deleted file mode 100644
index 8877617..0000000
--- a/src/menu/components/card/data-card/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class CardWrapSetting extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title={config.type === 'table' ? '琛ㄦ牸璁剧疆' : '鍗$墖璁剧疆'}
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default CardWrapSetting
\ No newline at end of file
diff --git a/src/menu/components/card/data-card/wrapsetting/index.scss b/src/menu/components/card/data-card/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/card/data-card/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/data-card/wrapsetting/settingform/index.jsx b/src/menu/components/card/data-card/wrapsetting/settingform/index.jsx
deleted file mode 100644
index d1c8eb0..0000000
--- a/src/menu/components/card/data-card/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,263 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,
-    config: PropTypes.object,
-    wrap: PropTypes.object,
-    inputSubmit: PropTypes.func
-  }
-
-  state = {
-    roleList: [],
-    appType: sessionStorage.getItem('appType'),
-    cardType: this.props.wrap.cardType,
-    MenuType: ''
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    let MenuType = ''
-
-    if (window.GLOB.customMenu && window.GLOB.customMenu.parentId === 'BillPrintTemp') {
-      MenuType = 'billPrint'
-    }
-
-    this.setState({roleList, MenuType})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap, config } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList, MenuType, appType, cardType } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label="鏍囬">
-                {getFieldDecorator('title', {
-                  initialValue: wrap.title || ''
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            {config.subtype === 'propcard' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
-                  <Icon type="question-circle" />
-                  鏁版嵁鏉ユ簮
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: wrap.datatype || 'dynamic'
-                })(
-                  <Radio.Group>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype === 'datacard' || (config.subtype === 'tablecard' && appType === 'mob') ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏁版嵁婧愪腑閫夋嫨鍒嗛〉鏃舵湁鏁堛��">
-                  <Icon type="question-circle" />
-                  鍒嗛〉椋庢牸
-                </Tooltip>
-              }>
-                {getFieldDecorator('pagestyle', {
-                  initialValue: wrap.pagestyle || 'page'
-                })(
-                  <Radio.Group>
-                    <Radio value="page">椤电爜</Radio>
-                    {appType !== 'mob' ? <Radio value="switch">宸﹀彸鍒囨崲</Radio> : null}
-                    {appType === 'mob' ? <Radio value="slide">婊戝姩鍔犺浇</Radio> : null}
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype !== 'tablecard' ? <Col span={12}>
-              <Form.Item label="鍗$墖灞炴��">
-                {getFieldDecorator('cardType', {
-                  initialValue: wrap.cardType || ''
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({cardType: e.target.value})}>
-                    <Radio key="" value=""> 涓嶅彲閫� </Radio>
-                    <Radio key="radio" value={'radio'}> 鍗曢�� </Radio>
-                    {config.subtype !== 'propcard' ? <Radio key="checkbox" value={'checkbox'}> 澶氶�� </Radio> : null}
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype === 'datacard' && appType === 'mob' && cardType === 'checkbox' ? <Col span={12}>
-              <Form.Item label="鍏ㄩ��">
-                {getFieldDecorator('checkAll', {
-                  initialValue: wrap.checkAll || 'hidden'
-                })(
-                  <Radio.Group>
-                    <Radio key="hidden" value="hidden"> 闅愯棌 </Radio>
-                    <Radio key="show" value="show"> 鏄剧ず </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype !== 'tablecard' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="璁剧疆涓哄眳涓榻愭垨鍙冲榻愶紝鍙湪鍗$墖涓�1琛屾椂鏈夋晥銆�">
-                  <Icon type="question-circle" />
-                  鍗$墖鎺掑垪
-                </Tooltip>
-              }>
-                {getFieldDecorator('cardFloat', {
-                  initialValue: wrap.cardFloat || 'left'
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
-                    <Radio key="left" value="left"> 宸﹀榻� </Radio>
-                    <Radio key="center" value="center"> 灞呬腑 </Radio>
-                    <Radio key="right" value="right"> 鍙冲榻� </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype !== 'tablecard' && appType !== 'mob' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="榧犳爣鎮诞浜庡崱鐗囦笂鏂规椂锛屽崱鐗囨斁澶�1.05鍊嶃��">
-                  <Icon type="question-circle" />
-                  鍗$墖鏀惧ぇ
-                </Tooltip>
-              }>
-                {getFieldDecorator('scale', {
-                  initialValue: wrap.scale || 'false'
-                })(
-                  <Radio.Group>
-                    <Radio key="false" value="false"> 鍚� </Radio>
-                    <Radio key="true" value="true"> 鏄� </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {config.subtype === 'propcard' && MenuType === 'billPrint' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨绫诲瀷涓恒�婇〉鐪�/椤佃剼銆嬫椂锛屾墦鍗扮殑姣忛〉閲岄兘浼氬甫鏈夎缁勪欢銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢绫诲瀷
-                </Tooltip>
-              }>
-                {getFieldDecorator('printType', {
-                  initialValue: wrap.printType || 'content'
-                })(
-                  <Radio.Group>
-                    <Radio value="content">鍐呭</Radio>
-                    <Radio value="headerOrfooter">椤电湁/椤佃剼</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/card/data-card/wrapsetting/settingform/index.scss b/src/menu/components/card/data-card/wrapsetting/settingform/index.scss
deleted file mode 100644
index 159130b..0000000
--- a/src/menu/components/card/data-card/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/prop-card/index.jsx b/src/menu/components/card/prop-card/index.jsx
index e132a91..fb98343 100644
--- a/src/menu/components/card/prop-card/index.jsx
+++ b/src/menu/components/card/prop-card/index.jsx
@@ -8,15 +8,16 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from '../data-card/options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CardComponent = asyncComponent(() => import('../cardcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
-const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
 const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
@@ -238,7 +239,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -255,33 +256,38 @@
     this.props.updateConfig(_card)
   }
 
-  addCard = () => {
+  addCard = (copy) => {
     let card = fromJS(this.state.card).toJS()
+    let newcard = {}
 
-    let newcard = {
-      uuid: Utils.getuuid(),
-      setting: { width: 6, type: 'simple'},
-      style: {
-        borderWidth: '1px', borderColor: '#e8e8e8',
-        paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
-        marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
-      },
-      backStyle: {},
-      elements: [],
-      backElements: []
-    }
-
-    if (card.subcards.length > 0) {
-      newcard = fromJS(card.subcards.slice(-1)[0]).toJS()
-      newcard.uuid = Utils.getuuid()
-      newcard.elements = newcard.elements.map(elem => {
-        elem.uuid = Utils.getuuid()
-        return elem
-      })
-      newcard.backElements = newcard.backElements.map(elem => {
-        elem.uuid = Utils.getuuid()
-        return elem
-      })
+    if (copy) { // 绮樿创
+      newcard = copy
+    } else {
+      newcard = {
+        uuid: Utils.getuuid(),
+        setting: { width: 6, type: 'simple'},
+        style: {
+          borderWidth: '1px', borderColor: '#e8e8e8',
+          paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
+        },
+        backStyle: {},
+        elements: [],
+        backElements: []
+      }
+  
+      if (card.subcards.length > 0) {
+        newcard = fromJS(card.subcards.slice(-1)[0]).toJS()
+        newcard.uuid = Utils.getuuid()
+        newcard.elements = newcard.elements.map(elem => {
+          elem.uuid = Utils.getuuid()
+          return elem
+        })
+        newcard.backElements = newcard.backElements.map(elem => {
+          elem.uuid = Utils.getuuid()
+          return elem
+        })
+      }
     }
 
     card.subcards.push(newcard)
@@ -359,6 +365,88 @@
     this.props.updateConfig(card)
   }
 
+  pasteComponent = (res, resolve) => {
+    const { appType } = this.state
+
+    delete res.copyType
+    delete res.$cardType
+
+    res.uuid = Utils.getuuid()
+    res.setting = res.setting || {}
+    res.setting.width = res.setting.width || 6
+
+    let copyBtns = []
+    let mobtypes = ['pop', 'prompt', 'exec', 'innerpage']
+
+    let elements = []
+    res.elements && res.elements.forEach(cell => {
+      if (cell.eleType !== 'button') {
+        cell.uuid = Utils.getuuid()
+        elements.push(cell)
+      } 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
+        elements.push(cell)
+      }
+    })
+
+    res.elements = elements
+
+    let backElements = []
+
+    if (appType !== 'mob') {
+      res.backElements && res.backElements.forEach(cell => {
+        if (cell.eleType !== 'button') {
+          cell.uuid = Utils.getuuid()
+          backElements.push(cell)
+        } 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
+          backElements.push(cell)
+        }
+      })
+    }
+
+    res.backElements = backElements
+
+    if (copyBtns.length > 0) {
+      MKEmitter.emit('copyButtons', copyBtns)
+    }
+
+    resolve({status: true})
+
+    this.addCard(res)
+  }
+
+  getWrapForms = () => {
+    const { card } = this.state
+    return getWrapForm(card.wrap, card.subtype, card.columns)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -388,10 +476,12 @@
         <NormalHeader defaultshow="hidden" config={card} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <Icon className="plus" title="娣诲姞鍗$墖" onClick={this.addCard} type="plus" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <Icon className="plus" title="娣诲姞鍗$墖" onClick={() => this.addCard()} type="plus" />
+            <NormalForm title="灞炴�у崱璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="propcard" card={card}/>
-            <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
+            <PasteComponent options={['cardcell']} updateConfig={this.pasteComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
             <ClockComponent config={card} updateConfig={this.updateComponent}/>
diff --git a/src/menu/components/card/table-card/cardcomponent/settingform/index.jsx b/src/menu/components/card/table-card/cardcomponent/settingform/index.jsx
deleted file mode 100644
index c4054ef..0000000
--- a/src/menu/components/card/table-card/cardcomponent/settingform/index.jsx
+++ /dev/null
@@ -1,132 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Tooltip, Icon, Input, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    cards: PropTypes.object,     // 鍗$墖闆�
-    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    condition: this.props.setting.condition || 'false'
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { setting, cards } = this.props
-    const { getFieldDecorator } = this.props.form
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="褰撻�夋嫨鈥滄湁鈥濇椂锛屽彧鏈夌鍚堟潯浠剁殑鏁版嵁鎵嶄細灞曠ず銆�">
-                  <Icon type="question-circle" />
-                  鏄剧ず鏉′欢
-                </Tooltip>
-              }>
-                {getFieldDecorator('condition', {
-                  initialValue: setting.condition || 'false'
-                })(
-                  <Radio.Group onChange={(e) => this.setState({ condition: e.target.value })}>
-                    <Radio value="true">鏈�</Radio>
-                    <Radio value="false">鏃�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {this.state.condition === 'true' ? <Col span={12}>
-              <Form.Item label="鎺у埗瀛楁">
-                {getFieldDecorator('controlField', {
-                  initialValue: setting.controlField || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鎺у埗瀛楁!'
-                    }
-                  ]
-                })(
-                  <Select>
-                    {cards.columns.map((option, index) =>
-                      <Select.Option key={index} value={option.field}>
-                        {option.label}
-                      </Select.Option>
-                    )}
-                    <Select.Option key={'index'} value={'$Index'}>搴忓彿锛堝墠绔級</Select.Option>
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {this.state.condition === 'true' ? <Col span={12}>
-              <Form.Item label="瀵规瘮鏂瑰紡">
-                {getFieldDecorator('controlType', {
-                  initialValue: setting.controlType || '=',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '瀵规瘮鏂瑰紡!'
-                    }
-                  ]
-                })(
-                  <Radio.Group>
-                    <Radio value="=">=</Radio>
-                    <Radio value="!=">!=</Radio>
-                    <Radio value=">">&gt;</Radio>
-                    <Radio value="<">&lt;</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {this.state.condition === 'true' ? <Col span={12}>
-              <Form.Item label="瀵规瘮鍊�">
-                {getFieldDecorator('controlValue', {
-                  initialValue: setting.controlValue || ''
-                })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)}
-              </Form.Item>
-            </Col> : null}
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/card/table-card/cardcomponent/settingform/index.scss b/src/menu/components/card/table-card/cardcomponent/settingform/index.scss
deleted file mode 100644
index 159130b..0000000
--- a/src/menu/components/card/table-card/cardcomponent/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/card/table-card/index.jsx b/src/menu/components/card/table-card/index.jsx
index a7d5efe..88eaaf7 100644
--- a/src/menu/components/card/table-card/index.jsx
+++ b/src/menu/components/card/table-card/index.jsx
@@ -8,16 +8,17 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from '../data-card/options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
-const CardComponent = asyncComponent(() => import('./cardcomponent'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardSimpleComponent = asyncComponent(() => import('../cardsimplecomponent'))
 const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
-const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
@@ -210,7 +211,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -227,25 +228,30 @@
     this.props.updateConfig(_card)
   }
 
-  addCard = () => {
+  addCard = (copy) => {
     let card = fromJS(this.state.card).toJS()
+    let newcard = {}
 
-    let newcard = {
-      uuid: Utils.getuuid(),
-      setting: { width: 6, type: 'simple'},
-      style: {
-        paddingTop: '5px', paddingBottom: '5px', paddingLeft: '15px', paddingRight: '15px',
-      },
-      elements: []
-    }
-
-    if (card.subcards.length > 0) {
-      newcard = fromJS(card.subcards.slice(-1)[0]).toJS()
-      newcard.uuid = Utils.getuuid()
-      newcard.elements = newcard.elements.map(elem => {
-        elem.uuid = Utils.getuuid()
-        return elem
-      })
+    if (copy) { // 绮樿创
+      newcard = copy
+    } else {
+      newcard = {
+        uuid: Utils.getuuid(),
+        setting: { width: 6, type: 'simple'},
+        style: {
+          paddingTop: '5px', paddingBottom: '5px', paddingLeft: '15px', paddingRight: '15px',
+        },
+        elements: []
+      }
+  
+      if (card.subcards.length > 0) {
+        newcard = fromJS(card.subcards.slice(-1)[0]).toJS()
+        newcard.uuid = Utils.getuuid()
+        newcard.elements = newcard.elements.map(elem => {
+          elem.uuid = Utils.getuuid()
+          return elem
+        })
+      }
     }
 
     card.subcards.push(newcard)
@@ -254,22 +260,28 @@
     this.props.updateConfig(card)
   }
 
-  addSearch = () => {
+  addSearch = (copy) => {
     const { card } = this.state
 
     let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
 
-    newcard.label = 'label'
-    newcard.initval = ''
-    newcard.type = 'select'
-    newcard.resourceType = '0'
-    newcard.options = []
-    newcard.setAll = 'false'
-    newcard.orderType = 'asc'
-    newcard.display = 'dropdown'
-    newcard.match = '='
+    if (copy) {
+      newcard = copy
+      newcard.focus = true
+    } else {
+      newcard.uuid = Utils.getuuid()
+      newcard.focus = true
+  
+      newcard.label = 'label'
+      newcard.initval = ''
+      newcard.type = 'select'
+      newcard.resourceType = '0'
+      newcard.options = []
+      newcard.setAll = 'false'
+      newcard.orderType = 'asc'
+      newcard.display = 'dropdown'
+      newcard.match = '='
+    }
 
     // 娉ㄥ唽浜嬩欢-娣诲姞鎼滅储
     MKEmitter.emit('addSearch', card.uuid, newcard)
@@ -342,6 +354,95 @@
     this.props.updateConfig(card)
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.subtype)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
+  pasteComponent = (res, resolve) => {
+    const { card, appType } = this.state
+
+    let type = res.copyType
+    delete res.copyType
+
+    if (type === 'cardcell') {
+      res.uuid = Utils.getuuid()
+      res.setting = res.setting || {}
+      res.setting.width = res.setting.width || 6
+
+      let copyBtns = []
+      let mobtypes = ['pop', 'prompt', 'exec', 'innerpage']
+
+      let elements = []
+      res.elements && res.elements.forEach(cell => {
+        if (cell.eleType !== 'button') {
+          cell.uuid = Utils.getuuid()
+          elements.push(cell)
+        } 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
+          elements.push(cell)
+        }
+      })
+
+      res.elements = elements
+
+      delete res.$cardType
+      delete res.backElements
+
+      if (copyBtns.length > 0) {
+        MKEmitter.emit('copyButtons', copyBtns)
+      }
+
+      resolve({status: true})
+
+      this.addCard(res)
+    } else if (type === 'search' || type === 'form') {
+      if (appType === 'mob') {
+        resolve({status: false, message: '绉诲姩绔暟鎹崱涓嶆敮鎸佹坊鍔犳悳绱㈡潯浠躲��'})
+      } else {
+        res.uuid = Utils.getuuid()
+        let keys = card.search.map(item => item.field.toLowerCase())
+  
+        if (type === 'form') {
+          if (['number', 'switch', 'textarea', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
+            res.type = 'text'
+          } else if (res.type === 'radio') {
+            res.type = 'select'
+          } else if (res.type === 'checkbox') {
+            res.type = 'multiselect'
+          } else if (res.type === 'datetime') {
+            res.type = 'date'
+          }
+        }
+  
+        if (res.field && keys.includes(res.field.toLowerCase())) {
+          resolve({status: false, message: '鎼滅储瀛楁宸插瓨鍦紒'})
+          return
+        }
+
+        resolve({status: true})
+
+        this.addSearch(res)
+      }
+    }
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -358,11 +459,13 @@
         <NormalHeader config={card} updateComponent={this.updateComponent}/>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <Icon className="plus" title="娣诲姞鍗$墖" onClick={this.addCard} type="plus" />
-            {appType !== 'mob' ? <Icon className="plus" title="娣诲姞鎼滅储" onClick={this.addSearch} type="plus-circle" /> : null}
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <Icon className="plus" title="娣诲姞鍗$墖" onClick={() => this.addCard()} type="plus" />
+            {appType !== 'mob' ? <Icon className="plus" title="娣诲姞鎼滅储" onClick={() => this.addSearch()} type="plus-circle" /> : null}
+            <NormalForm title="琛ㄦ牸璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="tablecard" card={card}/>
-            <PasteComponent config={card} options={['cardcell', 'search', 'form']} updateConfig={this.updateComponent} />
+            <PasteComponent options={['cardcell', 'search', 'form']} updateConfig={this.pasteComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
             <UserComponent config={card}/>
@@ -373,7 +476,7 @@
           <Icon type="tool" />
         </Popover>
         <div style={{minHeight: 'calc(100% - 90px)'}}>
-          {card.subcards.map(subcard => (<CardComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard} move={this.move} deleteElement={this.deleteCard}/>))}
+          {card.subcards.map(subcard => (<CardSimpleComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard} move={this.move} deleteElement={this.deleteCard}/>))}
         </div>
         {card.setting.laypage === 'true' && card.wrap.pagestyle !== 'slide' && appType !== 'mob' ? <Pagination size="small" total={50} /> : null}
         {card.setting.laypage === 'true' && card.wrap.pagestyle !== 'slide' && appType === 'mob' ? <MobPagination /> : null}
diff --git a/src/menu/components/carousel/cardcomponent/index.jsx b/src/menu/components/carousel/cardcomponent/index.jsx
deleted file mode 100644
index 1519f58..0000000
--- a/src/menu/components/carousel/cardcomponent/index.jsx
+++ /dev/null
@@ -1,230 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Modal, Popover, Icon } from 'antd'
-
-import asyncComponent from '@/utils/asyncComponent'
-import asyncIconComponent from '@/utils/asyncIconComponent'
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import { resetStyle } from '@/utils/utils-custom.js'
-import Utils from '@/utils/utils.js'
-import MKEmitter from '@/utils/events.js'
-import './index.scss'
-
-const CardCellComponent = asyncComponent(() => import('@/menu/components/card/cardcellcomponent'))
-const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
-
-class CardBoxComponent extends Component {
-  static propTpyes = {
-    cards: PropTypes.object,         // 鍗$墖琛岄厤缃俊鎭�
-    card: PropTypes.object,          // 鍗$墖閰嶇疆淇℃伅
-    move: PropTypes.func,            // 鍗$墖绉诲姩
-    deleteElement: PropTypes.func,   // 鍗$墖鍒犻櫎
-    updateElement: PropTypes.func    // 鑿滃崟閰嶇疆鏇存柊
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
-    formlist: null,        // 璁剧疆琛ㄥ崟淇℃伅
-    elements: null,        // 缂栬緫缁�
-    visible: false,        // 妯℃�佹鎺у埗
-    settingVisible: false,
-  }
-
-  /**
-   * @description 鎼滅储鏉′欢鍒濆鍖�
-   */
-  UNSAFE_componentWillMount () {
-    const { card } = this.props
-
-    this.setState({
-      card: fromJS(card).toJS(),
-      elements: fromJS(card.elements).toJS(),
-    })
-  }
-
-  componentDidMount () {
-    MKEmitter.addListener('submitStyle', this.getStyle)
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    const { cards } = this.props
-    
-    return !is(fromJS(cards), fromJS(nextProps.cards)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  /**
-   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
-   */
-  componentWillUnmount () {
-    this.setState = () => {
-      return
-    }
-    MKEmitter.removeListener('submitStyle', this.getStyle)
-  }
-
-  getStyle = (comIds, style) => {
-    const { cards } = this.props
-    const { card } = this.state
-
-    if (comIds.length !== 2 || comIds[0] !== cards.uuid || comIds[1] !== card.uuid) return
-
-    let _card = fromJS(card).toJS()
-    _card.style = style
-
-    this.setState({
-      card: _card
-    })
-
-    this.props.updateElement(_card)
-  }
-
-  updateCard = (elements) => {
-    const { card } = this.state
-
-    let _card = {...card, elements: elements}
-
-    this.setState({
-      card: _card
-    })
-
-    this.props.updateElement(_card)
-  }
-
-  addElement = () => {
-    const { cards } = this.props
-    const { card } = this.state
-
-    let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
-    
-    newcard.eleType = 'text'
-    newcard.datatype = 'dynamic'
-    newcard.height = 1
-
-    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
-    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
-  }
-
-  addButton = () => {
-    const { cards } = this.props
-    const { card } = this.state
-
-    let newcard = {}
-    newcard.uuid = Utils.getuuid()
-    newcard.focus = true
-    
-    newcard.eleType = 'button'
-    newcard.label = 'button'
-    newcard.sqlType = ''
-    newcard.Ot = 'requiredSgl'
-    newcard.OpenType = 'prompt'
-    newcard.icon = ''
-    newcard.class = 'primary'
-    newcard.intertype = 'system'
-    newcard.execSuccess = 'grid'
-    newcard.execError = 'never'
-    newcard.popClose = 'never'
-    newcard.errorTime = 10
-    newcard.verify = null
-    newcard.show = 'link'
-
-    // 娉ㄥ唽浜嬩欢-娣诲姞鍏冪礌
-    MKEmitter.emit('cardAddElement', [cards.uuid, card.uuid], newcard)
-  }
-
-  changeStyle = () => {
-    const { cards } = this.props
-    const { card } = this.state
-
-    let options = ['background', 'border', 'padding', 'margin', 'shadow']
-
-    MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, fromJS(card.style).toJS())
-  }
-
-  settingSubmit = () => {
-    const { card } = this.state
-
-    this.settingRef.handleConfirm().then(res => {
-      this.setState({
-        settingVisible: false,
-        card: {...card, setting: res}
-      })
-
-      this.props.updateElement({...card, setting: res})
-    })
-  }
-
-  clickComponent = (e) => {
-    if ((sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'propcard') && this.props.cards.subtype === 'propcard') {
-      e.stopPropagation()
-      MKEmitter.emit('clickComponent', this.state.card, this.props.cards, 'propcard')
-    }
-  }
-
-  render() {
-    const { cards } = this.props
-    const { card, elements, settingVisible, dict } = this.state
-
-    let _style = {...card.style}
-
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-    _style.height = cards.style.height
-    _style = resetStyle(_style)
-
-    return (
-      <div className="card-item" style={_style} onClick={this.clickComponent} id={card.uuid}>
-        <CardCellComponent cards={cards} cardCell={card} side="front" elements={elements} updateElement={this.updateCard}/>
-        <div className="card-control">
-          <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-            <div className="mk-popover-control">
-              <Icon className="plus" title="娣诲姞鍏冪礌" onClick={this.addElement} type="plus" />
-              <Icon className="plus" title="娣诲姞鎸夐挳" onClick={this.addButton} type="plus-square" />
-              <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.setState({settingVisible: true})} />
-              <CopyComponent type="cardcell" card={card}/>
-              <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
-              {cards.subtype === 'propcard' ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-                <div className="mk-popover-control">
-                  <Icon className="plus" title="宸︾Щ" type="arrow-left" onClick={() => this.props.move(card, 'left')} />
-                  <Icon className="close" title="鍙崇Щ" type="arrow-right" onClick={() => this.props.move(card, 'right')} />
-                </div>
-              } trigger="hover" getPopupContainer={() => document.getElementById(card.uuid + 'swap')}>
-                <Icon type="swap" id={card.uuid + 'swap'}/>
-              </Popover> : null}
-              {cards.subtype === 'propcard' ? <Icon className="close" title="鍒犻櫎鍗$墖" type="delete" onClick={() => this.props.deleteElement(card)} /> : null}
-            </div>
-          } trigger="hover">
-            <Icon type="tool" />
-          </Popover>
-        </div>
-        <Modal
-          wrapClassName="popview-modal"
-          title={'鍗$墖璁剧疆'}
-          visible={settingVisible}
-          width={700}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.settingSubmit}
-          onCancel={() => { this.setState({ settingVisible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            cards={cards}
-            setting={card.setting}
-            inputSubmit={this.settingSubmit}
-            wrappedComponentRef={(inst) => this.settingRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default CardBoxComponent
\ No newline at end of file
diff --git a/src/menu/components/carousel/cardcomponent/index.scss b/src/menu/components/carousel/cardcomponent/index.scss
deleted file mode 100644
index e69de29..0000000
--- a/src/menu/components/carousel/cardcomponent/index.scss
+++ /dev/null
diff --git a/src/menu/components/carousel/cardcomponent/settingform/index.jsx b/src/menu/components/carousel/cardcomponent/settingform/index.jsx
deleted file mode 100644
index d8046cc..0000000
--- a/src/menu/components/carousel/cardcomponent/settingform/index.jsx
+++ /dev/null
@@ -1,194 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Tooltip, Icon, Input, Cascader, Select } from 'antd'
-
-import './index.scss'
-
-const { TextArea } = Input
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    cards: PropTypes.object,     // 鍗$墖闆�
-    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    type: this.props.setting.type || 'simple',
-    click: this.props.setting.click || '',
-    isApp: sessionStorage.getItem('appType') === 'pc',
-    menulist: []
-  }
-
-  UNSAFE_componentWillMount() {
-    const { isApp } = this.state
-    let menulist = null
-
-    if (isApp) {
-      menulist = sessionStorage.getItem('appMenus')
-    } else {
-      menulist = sessionStorage.getItem('fstMenuList')
-    }
-
-    if (menulist) {
-      try {
-        menulist = JSON.parse(menulist)
-      } catch {
-        menulist = []
-      }
-    } else {
-      menulist = []
-    }
-
-    this.setState({menulist})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { setting, cards } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { click, menulist, isApp } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            {cards.subtype === 'propcard' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鍗$墖鐐瑰嚮鏃讹紝鍚戝叾浠栫粍浠朵紶閫掔殑BID鍊笺��">
-                  <Icon type="question-circle" />
-                  涓婚敭鍊�
-                </Tooltip>
-              }>
-                {getFieldDecorator('primaryId', {
-                  initialValue: setting.primaryId || ''
-                })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit}/>)}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="鐐瑰嚮浜嬩欢">
-                {getFieldDecorator('click', {
-                  initialValue: click
-                })(
-                  <Radio.Group onChange={(e) => this.setState({click: e.target.value})}>
-                    <Radio value="">鏃�</Radio>
-                    <Radio value="menu">鑿滃崟</Radio>
-                    <Radio value="link">閾炬帴</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {!isApp && click === 'menu' ? <Col span={12}>
-              <Form.Item label="鑿滃崟">
-                {getFieldDecorator('menu', {
-                  initialValue: setting.menu || [],
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鑿滃崟!'
-                    }
-                  ]
-                })(
-                  <Cascader options={menulist} placeholder=""/>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {isApp && click === 'menu' ? <Col span={12}>
-              <Form.Item label="鍏宠仈鑿滃崟">
-                {getFieldDecorator('menu', {
-                  initialValue: setting.menu || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鍏宠仈鑿滃崟!'
-                    }
-                  ]
-                })(
-                  <Select
-                    showSearch
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {menulist.map(option =>
-                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {click === 'link' ? <Col span={24} className="textarea">
-              <Form.Item label="閾炬帴">
-                {getFieldDecorator('linkurl', {
-                  initialValue: setting.linkurl || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '閾炬帴!'
-                    }
-                  ]
-                })( <TextArea rows={2}/> )}
-              </Form.Item>
-            </Col> : null}
-            {isApp ? <Col span={12}>
-              <Form.Item label="鎵撳紑鏂瑰紡">
-                {getFieldDecorator('open', {
-                  initialValue: setting.open || 'blank'
-                })(
-                  <Radio.Group>
-                    <Radio value="blank">鏂扮獥鍙�</Radio>
-                    <Radio value="self">褰撳墠绐楀彛</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {click !== '' ? <Col span={12}>
-              <Form.Item label="鍙傛暟鎷兼帴">
-                {getFieldDecorator('joint', {
-                  initialValue: setting.joint || 'true'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄�</Radio>
-                    <Radio value="false">鍚�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/carousel/cardcomponent/settingform/index.scss b/src/menu/components/carousel/cardcomponent/settingform/index.scss
deleted file mode 100644
index 159130b..0000000
--- a/src/menu/components/carousel/cardcomponent/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/index.jsx b/src/menu/components/carousel/data-card/index.jsx
index 9a3ab4f..2144877 100644
--- a/src/menu/components/carousel/data-card/index.jsx
+++ b/src/menu/components/carousel/data-card/index.jsx
@@ -8,13 +8,14 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
-const CardComponent = asyncComponent(() => import('../cardcomponent'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardSimpleComponent = asyncComponent(() => import('@/menu/components/card/cardsimplecomponent'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
@@ -201,7 +202,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -250,6 +251,16 @@
     }
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.subtype)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -265,7 +276,9 @@
       <div className="menu-data-carousel-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">
-            <WrapComponent config={card} updateConfig={this.updateComponent}/>
+            <NormalForm title="杞挱-鍔ㄦ�佹暟鎹�" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="datacard" card={card}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors"/>
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog}/>
@@ -276,7 +289,7 @@
         } trigger="hover">
           <Icon type="tool"/>
         </Popover>
-        <CardComponent cards={card} card={card.subcards[0]} updateElement={this.updateCard} deleteElement={this.deleteCard}/>
+        <CardSimpleComponent cards={card} card={card.subcards[0]} updateElement={this.updateCard} deleteElement={this.deleteCard}/>
       </div>
     )
   }
diff --git a/src/menu/components/carousel/data-card/options.jsx b/src/menu/components/carousel/data-card/options.jsx
new file mode 100644
index 0000000..76bef49
--- /dev/null
+++ b/src/menu/components/carousel/data-card/options.jsx
@@ -0,0 +1,134 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap, subtype) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const cardWrapForm = [
+    {
+      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 || 'dynamic',
+      tooltip: '閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��',
+      required: false,
+      options: [
+        {value: 'dynamic', label: '鍔ㄦ��'},
+        {value: 'static', label: '闈欐��'},
+      ],
+      forbid: subtype !== 'propcard'
+    },
+    {
+      type: 'radio',
+      field: 'autoplay',
+      label: '鑷姩鍒囨崲',
+      initval: wrap.autoplay || 'false',
+      required: false,
+      options: [
+        {value: 'false', label: '鍚�'},
+        {value: 'true', label: '鏄�'},
+      ]
+    },
+    {
+      type: 'number',
+      field: 'speed',
+      label: '鏃堕棿闂撮殧',
+      initval: wrap.speed || 3,
+      tooltip: '浣跨敤鑷姩鍒囨崲鏃舵湁鏁堬紝榛樿涓�3绉�',
+      min: 1,
+      max: 100,
+      precision: 0,
+      required: false
+    },
+    {
+      type: 'radio',
+      field: 'dots',
+      label: '鎸囩ず鐐�',
+      initval: wrap.dots || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'vertical',
+      label: '鍨傜洿鏄剧ず',
+      initval: wrap.vertical || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ],
+      forbid: appType !== 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'dotPosition',
+      label: '鎸囩ず鐐逛綅缃�',
+      initval: wrap.dotPosition || 'bottom',
+      required: false,
+      options: [
+        {value: 'top', label: '涓�'},
+        {value: 'bottom', label: '涓�'},
+        {value: 'left', label: '宸�'},
+        {value: 'right', label: '鍙�'},
+      ],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'effect',
+      label: '鍔ㄧ敾鏁堟灉',
+      initval: wrap.effect || 'scrollx',
+      required: false,
+      options: [
+        {value: 'scrollx', label: '鍒囨崲'},
+        {value: 'fade', label: '娓愭樉'},
+      ],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return cardWrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/wrapsetting/index.jsx b/src/menu/components/carousel/data-card/wrapsetting/index.jsx
deleted file mode 100644
index 7b28116..0000000
--- a/src/menu/components/carousel/data-card/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="鍗$墖璁剧疆"
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/wrapsetting/index.scss b/src/menu/components/carousel/data-card/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/carousel/data-card/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx b/src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx
deleted file mode 100644
index 21fec41..0000000
--- a/src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,227 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    appType: sessionStorage.getItem('appType'),
-    roleList: []
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap, config } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList, appType } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            {config.subtype === 'propcard' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
-                  <Icon type="question-circle" />
-                  鏁版嵁鏉ユ簮
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: wrap.datatype || 'dynamic'
-                })(
-                  <Radio.Group>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="鑷姩鍒囨崲">
-                {getFieldDecorator('autoplay', {
-                  initialValue: wrap.autoplay || 'false'
-                })(
-                  <Radio.Group>
-                    <Radio value="false">鍚�</Radio>
-                    <Radio value="true">鏄�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="浣跨敤鑷姩鍒囨崲鏃舵湁鏁堬紝榛樿涓�3绉掋��">
-                  <Icon type="question-circle" />
-                  鏃堕棿闂撮殧
-                </Tooltip>
-              }>
-                {getFieldDecorator('speed', {
-                  initialValue: wrap.speed || 3
-                })(<InputNumber min={1} max={100} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="鎸囩ず鐐�">
-                {getFieldDecorator('dots', {
-                  initialValue: wrap.dots || 'true'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄剧ず</Radio>
-                    <Radio value="false">闅愯棌</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {appType === 'mob' ? <Col span={12}>
-              <Form.Item label="鍨傜洿鏄剧ず">
-                {getFieldDecorator('vertical', {
-                  initialValue: wrap.vertical || 'false'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄�</Radio>
-                    <Radio value="false">鍚�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {appType !== 'mob' ? <Col span={12}>
-              <Form.Item label="鎸囩ず鐐逛綅缃�">
-                {getFieldDecorator('dotPosition', {
-                  initialValue: wrap.dotPosition || 'bottom'
-                })(
-                  <Radio.Group>
-                    <Radio value="top">涓�</Radio>
-                    <Radio value="bottom">涓�</Radio>
-                    <Radio value="left">宸�</Radio>
-                    <Radio value="right">鍙�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {appType !== 'mob' ? <Col span={12}>
-              <Form.Item label="鍔ㄧ敾鏁堟灉">
-                {getFieldDecorator('effect', {
-                  initialValue: wrap.effect || 'scrollx'
-                })(
-                  <Radio.Group>
-                    <Radio value="scrollx">鍒囨崲</Radio>
-                    <Radio value="fade">娓愭樉</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss b/src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss
deleted file mode 100644
index 159130b..0000000
--- a/src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/carousel/prop-card/index.jsx b/src/menu/components/carousel/prop-card/index.jsx
index 404d434..dd686fb 100644
--- a/src/menu/components/carousel/prop-card/index.jsx
+++ b/src/menu/components/carousel/prop-card/index.jsx
@@ -8,13 +8,14 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from '../data-card/options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
-const CardComponent = asyncComponent(() => import('../cardcomponent'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardSimpleComponent = asyncComponent(() => import('@/menu/components/card/cardsimplecomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
@@ -218,7 +219,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -327,6 +328,16 @@
     this.props.updateConfig(card)
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.subtype)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -343,7 +354,9 @@
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="plus" title="娣诲姞鍗$墖" onClick={this.addCard} type="plus" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="杞挱-闈欐�佹暟鎹�" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="propcard" card={card}/>
             <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
@@ -357,7 +370,7 @@
           <Icon type="tool" />
         </Popover>
         {card.subcards.length > 0 ? <Carousel dotPosition={card.wrap.dotPosition || 'bottom'} effect={card.wrap.effect || 'scrollx'}>
-          {card.subcards.map((subcard) => (<CardComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
+          {card.subcards.map((subcard) => (<CardSimpleComponent key={subcard.uuid} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
         </Carousel> : null}
       </div>
     )
diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx
index 19cd40e..6930709 100644
--- a/src/menu/components/chart/antv-bar/index.jsx
+++ b/src/menu/components/chart/antv-bar/index.jsx
@@ -198,14 +198,10 @@
    */
   linerender = () => {
     const { card } = this.state
-    let plot = {...card.plot} // 鍘婚櫎title鎵�鍗犵┖闂�
+    const plot = card.plot
     let color = plot.color || 'rgba(0, 0, 0, 0.65)'
     let X_axis = plot.Xaxis || 'x'
     let Y_axis = plot.Yaxis || ['y']
-
-    if (card.plot.title || card.search.length > 0) {
-      plot.height = card.plot.height - 70
-    }
 
     let data = this.getdata(X_axis, Y_axis)
 
@@ -239,7 +235,7 @@
       const chart = new Chart({
         container: card.uuid + 'canvas',
         autoFit: true,
-        height: plot.height || 400
+        height: this.wrap.offsetHeight - 25
       })
   
       chart.data(dv.rows)
@@ -423,16 +419,12 @@
    */
   customrender = (data) => {
     let card = fromJS(this.state.card).toJS()
-    let plot = {...card.plot} // 鍘婚櫎title鎵�鍗犵┖闂�
+    let plot = card.plot
     let color = plot.color || 'rgba(0, 0, 0, 0.65)'
     let fields = []
     let legends = []
     let transfield = {}
     let Bar_axis = []
-
-    if (card.plot.title || card.search.length > 0) {
-      plot.height = card.plot.height - 70
-    }
 
     card.columns.forEach(col => {
       if (col.field) {
@@ -568,7 +560,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: plot.height || 400,
+      height: this.wrap.offsetHeight - 25,
     })
 
     // chart.axis(plot.Xaxis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } })
@@ -920,14 +912,10 @@
    */
   barrender = () => {
     const { card } = this.state
-    let plot = {...card.plot}
+    const plot = card.plot
     let color = plot.color || 'rgba(0, 0, 0, 0.65)'
     let X_axis = plot.Xaxis || 'x'
     let Y_axis = plot.Yaxis || ['y']
-
-    if (card.plot.title || card.search.length > 0) {
-      plot.height = card.plot.height - 70
-    }
 
     let data = this.getdata(X_axis, Y_axis)
     
@@ -962,7 +950,7 @@
       const chart = new Chart({
         container: card.uuid + 'canvas',
         autoFit: true,
-        height: plot.height || 400
+        height: this.wrap.offsetHeight - 25
       })
 
       chart.data(dv.rows)
@@ -1282,7 +1270,6 @@
     newcard.execSuccess = 'grid'
     newcard.execError = 'never'
     newcard.popClose = 'never'
-    newcard.errorTime = 10
     newcard.verify = null
     newcard.show = 'icon'
 
@@ -1293,7 +1280,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -1363,7 +1350,7 @@
           <Icon type="tool" />
         </Popover>
         {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
-        <div className="canvas" id={card.uuid + 'canvas'}></div>
+        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
         {appType !== 'mob' ? <ActionComponent
           type="chart"
           config={card}
diff --git a/src/menu/components/chart/antv-bar/index.scss b/src/menu/components/chart/antv-bar/index.scss
index 0c212d3..b55ec7a 100644
--- a/src/menu/components/chart/antv-bar/index.scss
+++ b/src/menu/components/chart/antv-bar/index.scss
@@ -5,16 +5,19 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  
+  display: flex;
+  flex-flow: column;
+
   .canvas {
     margin: 0px;
     padding: 15px 10px 10px;
     letter-spacing: 0px;
-    height: 100%;
+    // height: 100%;
+    flex: 1;
   }
-  .normal-header + .canvas {
-    height: calc(100% - 45px);
-  }
+  // .normal-header + .canvas {
+  //   height: calc(100% - 45px);
+  // }
 
   .chart-header {
     position: relative;
diff --git a/src/menu/components/chart/antv-dashboard/index.jsx b/src/menu/components/chart/antv-dashboard/index.jsx
index 54f8e71..a6c9c1e 100644
--- a/src/menu/components/chart/antv-dashboard/index.jsx
+++ b/src/menu/components/chart/antv-dashboard/index.jsx
@@ -234,7 +234,7 @@
     const chart = new Chart({
       container: card.uuid + 'dashboard',
       autoFit: true,
-      height: plot.title ? plot.height - 45 : plot.height,
+      height: this.wrap.offsetHeight - 30,
     })
     
     chart.data(data)
@@ -333,7 +333,7 @@
     const chart = new Chart({
       container: card.uuid + 'dashboard',
       autoFit: true,
-      height: plot.title ? plot.height - 45 : plot.height,
+      height: this.wrap.offsetHeight - 30,
       padding: [0, 0, 0, 0],
     })
     chart.data(data)
@@ -489,7 +489,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -529,7 +529,7 @@
           <Icon type="tool" />
         </Popover>
         {card.plot.title ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
-        <div className="canvas" id={card.uuid + 'dashboard'}></div>
+        <div className="canvas" id={card.uuid + 'dashboard'} ref={ref => this.wrap = ref}></div>
       </div>
     )
   }
diff --git a/src/menu/components/chart/antv-dashboard/index.scss b/src/menu/components/chart/antv-dashboard/index.scss
index efb4267..f56897e 100644
--- a/src/menu/components/chart/antv-dashboard/index.scss
+++ b/src/menu/components/chart/antv-dashboard/index.scss
@@ -5,16 +5,19 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
+  display: flex;
+  flex-flow: column;
   
   .canvas {
     margin: 0px;
     padding: 15px;
     letter-spacing: 0px;
-    height: 100%;
+    // height: 100%;
+    flex: 1;
   }
-  .normal-header + .canvas {
-    height: calc(100% - 45px);
-  }
+  // .normal-header + .canvas {
+  //   height: calc(100% - 45px);
+  // }
 
   >.anticon-tool {
     position: absolute;
diff --git a/src/menu/components/chart/antv-pie/index.jsx b/src/menu/components/chart/antv-pie/index.jsx
index 593fcd5..7055125 100644
--- a/src/menu/components/chart/antv-pie/index.jsx
+++ b/src/menu/components/chart/antv-pie/index.jsx
@@ -223,10 +223,6 @@
     let X_axis = plot.Xaxis || 'x'
     let Y_axis = plot.Yaxis || 'y'
     let type = plot.type || 'type'
-    let height = plot.height || 400
-    if (card.plot.title || card.search.length > 0) {
-      height = height - 45
-    }
 
     const _data = this.getnestdata(X_axis, Y_axis, type)
     const dvx = new DataView().source(_data)
@@ -258,7 +254,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: height,
+      height: this.wrap.offsetHeight - 30,
       padding: 0,
     })
 
@@ -420,10 +416,6 @@
     let color = plot.color || 'rgba(0, 0, 0, 0.85)'
     let X_axis = plot.Xaxis || 'x'
     let Y_axis = plot.Yaxis || 'y'
-    let height = plot.height || 400
-    if (card.plot.title || card.search.length > 0) {
-      height = height - 45
-    }
 
     let data = this.getdata(X_axis, Y_axis)
 
@@ -433,7 +425,7 @@
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: height
+      height: this.wrap.offsetHeight - 30
     })
 
     if (plot.shape !== 'nightingale' && plot.show !== 'value') {
@@ -651,7 +643,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -693,7 +685,7 @@
           <Icon type="tool" />
         </Popover>
         {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
-        <div className="canvas" id={card.uuid + 'canvas'}></div>
+        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
       </div>
     )
   }
diff --git a/src/menu/components/chart/antv-pie/index.scss b/src/menu/components/chart/antv-pie/index.scss
index 43bf1c5..2de1b1e 100644
--- a/src/menu/components/chart/antv-pie/index.scss
+++ b/src/menu/components/chart/antv-pie/index.scss
@@ -5,16 +5,19 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
+  display: flex;
+  flex-flow: column;
   
   .canvas {
     margin: 0px;
     padding: 15px;
     letter-spacing: 0px;
-    height: 100%;
+    // height: 100%;
+    flex: 1;
   }
-  .normal-header + .canvas {
-    height: calc(100% - 45px);
-  }
+  // .normal-header + .canvas {
+  //   height: calc(100% - 45px);
+  // }
 
   >.anticon-tool {
     position: absolute;
diff --git a/src/menu/components/chart/antv-scatter/index.jsx b/src/menu/components/chart/antv-scatter/index.jsx
index a002498..9a7b1f3 100644
--- a/src/menu/components/chart/antv-scatter/index.jsx
+++ b/src/menu/components/chart/antv-scatter/index.jsx
@@ -187,16 +187,11 @@
     const { card } = this.state
     const plot = card.plot
     const data = this.getdata()
-    let height = plot.height - 25
-
-    if (card.plot.title || card.search.length > 0) {
-      height = plot.height - 70
-    }
 
     const chart = new Chart({
       container: card.uuid + 'canvas',
       autoFit: true,
-      height: height
+      height: this.wrap.offsetHeight - 25
     })
 
     chart.data(data);
@@ -313,7 +308,6 @@
     newcard.execSuccess = 'grid'
     newcard.execError = 'never'
     newcard.popClose = 'never'
-    newcard.errorTime = 10
     newcard.verify = null
     newcard.show = 'icon'
 
@@ -324,7 +318,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -394,7 +388,7 @@
           <Icon type="tool" />
         </Popover>
         {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
-        <div className="canvas" id={card.uuid + 'canvas'}></div>
+        <div className="canvas" id={card.uuid + 'canvas'} ref={ref => this.wrap = ref}></div>
         {appType !== 'mob' ? <ActionComponent type="chart" config={card} updateaction={this.updateComponent}/> : null}
       </div>
     )
diff --git a/src/menu/components/chart/antv-scatter/index.scss b/src/menu/components/chart/antv-scatter/index.scss
index 4403718..f5f3597 100644
--- a/src/menu/components/chart/antv-scatter/index.scss
+++ b/src/menu/components/chart/antv-scatter/index.scss
@@ -5,16 +5,19 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
+  display: flex;
+  flex-flow: column;
   
   .canvas {
     margin: 0px;
     padding: 15px 10px 10px;
     letter-spacing: 0px;
-    height: 100%;
+    // height: 100%;
+    flex: 1;
   }
-  .normal-header + .canvas {
-    height: calc(100% - 45px);
-  }
+  // .normal-header + .canvas {
+  //   height: calc(100% - 45px);
+  // }
 
   .chart-header {
     position: relative;
diff --git a/src/menu/components/code/sandbox/index.jsx b/src/menu/components/code/sandbox/index.jsx
index e125817..aba18e0 100644
--- a/src/menu/components/code/sandbox/index.jsx
+++ b/src/menu/components/code/sandbox/index.jsx
@@ -7,15 +7,16 @@
 import asyncIconComponent from '@/utils/asyncIconComponent'
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
 const EditorCode = asyncIconComponent(() => import('./editorcode'))
 const CodeContent = asyncComponent(() => import('./codecontent'))
 
@@ -116,7 +117,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -143,6 +144,16 @@
     this.props.updateConfig(config)
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap)
+  }
+
+  updateWrap = (res) => {
+    this.updateconfig({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -158,7 +169,9 @@
       <div className="menu-editor-sand-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">
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="鑷畾涔夌粍浠惰缃�" width={700} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="normaltable" card={card}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <UserComponent config={card}/>
diff --git a/src/menu/components/code/sandbox/options.jsx b/src/menu/components/code/sandbox/options.jsx
new file mode 100644
index 0000000..31f5585
--- /dev/null
+++ b/src/menu/components/code/sandbox/options.jsx
@@ -0,0 +1,62 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const cardWrapForm = [
+    {
+      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 || 'dynamic',
+      tooltip: '閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��',
+      required: false,
+      options: [
+        {value: 'dynamic', label: '鍔ㄦ��'},
+        {value: 'static', label: '闈欐��'},
+      ]
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return cardWrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/index.jsx b/src/menu/components/code/sandbox/wrapsetting/index.jsx
deleted file mode 100644
index bccbfd8..0000000
--- a/src/menu/components/code/sandbox/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="瀵屾枃鏈缃�"
-          visible={visible}
-          width={700}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/index.scss b/src/menu/components/code/sandbox/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/code/sandbox/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx b/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx
deleted file mode 100644
index 76806c6..0000000
--- a/src/menu/components/code/sandbox/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,152 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    roleList: [],
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
-                  <Icon type="question-circle" />
-                  鏁版嵁鏉ユ簮
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: wrap.datatype || 'dynamic'
-                })(
-                  <Radio.Group>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss b/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss
deleted file mode 100644
index c530b18..0000000
--- a/src/menu/components/code/sandbox/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .color-sketch-block {
-    position: relative;
-    top: 7px;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/editorcontent/index.scss b/src/menu/components/editor/braft-editor/editorcontent/index.scss
index dc27e8c..25414a8 100644
--- a/src/menu/components/editor/braft-editor/editorcontent/index.scss
+++ b/src/menu/components/editor/braft-editor/editorcontent/index.scss
@@ -10,5 +10,8 @@
     border: 1px solid #d9d9d9;
     border-radius: 4px;
     overflow-x: hidden;
+    .bf-container .DraftEditor-root, .bf-container .public-DraftEditor-content {
+      min-height: 500px;
+    }
   }
 }
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/index.jsx b/src/menu/components/editor/braft-editor/index.jsx
index cf83121..994da8b 100644
--- a/src/menu/components/editor/braft-editor/index.jsx
+++ b/src/menu/components/editor/braft-editor/index.jsx
@@ -5,7 +5,7 @@
 
 import asyncComponent from '@/utils/asyncComponent'
 import asyncIconComponent from '@/utils/asyncIconComponent'
-
+import getWrapForm from './options'
 import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
@@ -13,10 +13,10 @@
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
 const EditorContent = asyncIconComponent(() => import('./editorcontent'))
 const BraftContent = asyncComponent(() => import('@/tabviews/custom/components/share/braftContent'))
 
@@ -115,7 +115,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -147,6 +147,16 @@
     this.props.updateConfig(config)
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap, card.columns)
+  }
+
+  updateWrap = (res) => {
+    this.updateconfig({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -161,7 +171,9 @@
         <NormalHeader defaultshow="hidden" 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">
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="瀵屾枃鏈缃�" width={700} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="normaltable" card={card}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <UserComponent config={card}/>
diff --git a/src/menu/components/editor/braft-editor/options.jsx b/src/menu/components/editor/braft-editor/options.jsx
new file mode 100644
index 0000000..ec4e0fe
--- /dev/null
+++ b/src/menu/components/editor/braft-editor/options.jsx
@@ -0,0 +1,87 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap, columns) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const cardWrapForm = [
+    {
+      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 || 'dynamic',
+      tooltip: '閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��',
+      required: false,
+      options: [
+        {value: 'dynamic', label: '鍔ㄦ��'},
+        {value: 'static', label: '闈欐��'},
+      ],
+      controlFields: [
+        {field: 'field', values: ['dynamic']},
+        {field: 'encryption', values: ['dynamic']},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'field',
+      label: '鏂囨湰瀛楁',
+      initval: wrap.field || '',
+      tooltip: '閫夋嫨鍔ㄦ�佸�兼椂锛岄渶璁剧疆鏂囨湰瀛楁鎵嶅彲鐢熸晥銆�',
+      required: false,
+      options: columns
+    },
+    {
+      type: 'radio',
+      field: 'encryption',
+      label: '鏁版嵁瑙g爜',
+      initval: wrap.encryption || 'true',
+      tooltip: '浠庢暟鎹簮鑾峰彇鐨勬暟鎹槸鍚﹂渶瑕佽В鐮併��',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄�'},
+        {value: 'false', label: '鍚�'},
+      ]
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return cardWrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/index.jsx b/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
deleted file mode 100644
index bccbfd8..0000000
--- a/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="瀵屾枃鏈缃�"
-          visible={visible}
-          width={700}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/index.scss b/src/menu/components/editor/braft-editor/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/editor/braft-editor/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx
deleted file mode 100644
index b93fffd..0000000
--- a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,199 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    roleList: [],
-    datatype: this.props.wrap.datatype || 'dynamic'
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  changeDataType = (e) => {
-    this.setState({datatype: e.target.value})
-  }
-
-  render() {
-    const { wrap, config } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList, datatype } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label="鏍囬">
-                {getFieldDecorator('title', {
-                  initialValue: wrap.title || ''
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨闈欐�佸�硷紝鏃犻渶閰嶇疆鏁版嵁婧愩��">
-                  <Icon type="question-circle" />
-                  鏁版嵁鏉ユ簮
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: datatype
-                })(
-                  <Radio.Group onChange={this.changeDataType}>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {datatype === 'dynamic' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="閫夋嫨鍔ㄦ�佸�兼椂锛岄渶璁剧疆鏂囨湰瀛楁鎵嶅彲鐢熸晥銆�">
-                  <Icon type="question-circle" />
-                  鏂囨湰瀛楁
-                </Tooltip>
-              }>
-                {getFieldDecorator('field', {
-                  initialValue: wrap.field || ''
-                })(
-                  <Select>
-                    {config.columns.map(option =>
-                      <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {datatype === 'dynamic' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="浠庢暟鎹簮鑾峰彇鐨勬暟鎹槸鍚﹂渶瑕佽В鐮併��">
-                  <Icon type="question-circle" />
-                  鏁版嵁瑙g爜
-                </Tooltip>
-              }>
-                {getFieldDecorator('encryption', {
-                  initialValue: wrap.encryption || 'true'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄�</Radio>
-                    <Radio value="false">鍚�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss b/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss
deleted file mode 100644
index c530b18..0000000
--- a/src/menu/components/editor/braft-editor/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .color-sketch-block {
-    position: relative;
-    top: 7px;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/form/dragtitle/card.jsx b/src/menu/components/form/dragtitle/card.jsx
index 48e07f4..0ebb97b 100644
--- a/src/menu/components/form/dragtitle/card.jsx
+++ b/src/menu/components/form/dragtitle/card.jsx
@@ -1,10 +1,15 @@
 import React from 'react'
+import { fromJS } from 'immutable'
 import { useDrag, useDrop } from 'react-dnd'
 import { Icon, Popover } from 'antd'
 
+import getForm from './options'
+import asyncIconComponent from '@/utils/asyncIconComponent'
 import './index.scss'
 
-const Card = ({ id, card, active, moveCard, findCard, editCard, closeCard, selectCard }) => {
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+
+const Card = ({ id, card, active, moveCard, findCard, closeCard, selectCard, updateGroup }) => {
   const originalIndex = findCard(id).index
   const [{ isDragging }, drag] = useDrag({
     item: { type: 'form', id, originalIndex },
@@ -28,10 +33,6 @@
   })
   const opacity = isDragging ? 0 : 1
 
-  const edit = () => {
-    editCard(id)
-  }
-
   const close = () => {
     closeCard(id)
   }
@@ -40,10 +41,37 @@
     selectCard(id)
   }
 
+  const getForms = () =>{
+    return getForm(card)
+  }
+
+  const updateSetting = (res) => {
+    let _card = fromJS(card).toJS()
+    
+    if (res.prevEnable) {
+      _card.prevButton.enable = res.prevEnable
+      delete res.prevEnable
+    }
+    if (res.subEnable) {
+      _card.subButton.enable = res.subEnable
+      delete res.subEnable
+    }
+    if (res.nextEnable) {
+      _card.nextButton.enable = res.nextEnable
+      delete res.nextEnable
+    }
+
+    _card.setting = res
+
+    updateGroup(_card)
+  }
+
   return (
     <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
       <div className="mk-popover-control">
-        <Icon className="edit" type="edit" onClick={edit} />
+        <NormalForm title="鍒嗙粍缂栬緫" width={850} update={updateSetting} getForms={getForms}>
+          <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+        </NormalForm>
         <Icon className="close" type="close" onClick={close} />
       </div>
     } trigger="hover">
diff --git a/src/menu/components/form/dragtitle/index.jsx b/src/menu/components/form/dragtitle/index.jsx
index 69ce1a4..138a259 100644
--- a/src/menu/components/form/dragtitle/index.jsx
+++ b/src/menu/components/form/dragtitle/index.jsx
@@ -4,7 +4,7 @@
 import Card from './card'
 import './index.scss'
 
-const Container = ({list, selectId, handleList, handleGroup, closeGroup, selectGroup}) => {
+const Container = ({list, selectId, tabtype, handleList, handleGroup, closeGroup, selectGroup}) => {
   const [cards, setCards] = useState(list)
   const moveCard = (id, atIndex) => {
     const { card, index } = findCard(id)
@@ -28,11 +28,6 @@
     }
   }
 
-  const editCard = id => {
-    const { card } = findCard(id)
-    handleGroup(card)
-  }
-
   const closeCard = id => {
     const { card } = findCard(id)
     closeGroup(card)
@@ -44,7 +39,7 @@
   }
 
   return (
-    <div className="normal-form-titles" >
+    <div className={'normal-form-titles ' + (tabtype || '') } >
       {cards.map(card => (
         <Card
           id={card.uuid}
@@ -52,8 +47,8 @@
           active={card.uuid === selectId}
           card={card}
           moveCard={moveCard}
-          editCard={editCard}
           closeCard={closeCard}
+          updateGroup={handleGroup}
           findCard={findCard}
           selectCard={selectCard}
         />
diff --git a/src/menu/components/form/dragtitle/index.scss b/src/menu/components/form/dragtitle/index.scss
index 100acac..f4413f1 100644
--- a/src/menu/components/form/dragtitle/index.scss
+++ b/src/menu/components/form/dragtitle/index.scss
@@ -36,4 +36,54 @@
       background: #1890ff;
     }
   }
+}
+.normal-form-titles.mktab {
+  font-size: 16px;
+  line-height: 36px;
+  min-height: 36px;
+  margin-bottom: 16px;
+  border-bottom: 1px solid #d9d9d9;
+  .form-sort {
+    display: none;
+  }
+  .page-card {
+    margin-bottom: 0px;
+    border-bottom: 2px solid transparent;
+  }
+  .page-card::before {
+    display: none;
+  }
+  .page-card.active {
+    border-bottom-color: #1890ff;
+  }
+}
+.normal-form-titles.mkbtn {
+  font-size: 16px;
+  line-height: 36px;
+  min-height: 36px;
+  margin-bottom: 16px;
+
+  .form-sort {
+    display: none;
+  }
+  .page-card {
+    margin-bottom: 0px;
+    background: #ffffff;
+    color: #1890ff;
+    border: 1px solid #1890ff;
+    border-radius: 0px;
+  }
+  .page-card:first-child {
+    border-radius: 4px 0px 0px 4px;
+  }
+  .page-card:last-child {
+    border-radius: 0px 4px 4px 0px;
+  }
+  .page-card::before {
+    display: none;
+  }
+  .page-card.active {
+    background: #1890ff;
+    color: #ffffff;
+  }
 }
\ No newline at end of file
diff --git a/src/menu/components/form/dragtitle/options.jsx b/src/menu/components/form/dragtitle/options.jsx
new file mode 100644
index 0000000..90f9176
--- /dev/null
+++ b/src/menu/components/form/dragtitle/options.jsx
@@ -0,0 +1,99 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (group) {
+  let appType = sessionStorage.getItem('appType')
+  let fields = [{field: '', label: '绌�'}]
+
+  if (appType === 'mob') {
+    group.fields.forEach(f => {
+      if (f.field && ['text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
+        fields.push(f)
+      }
+    })
+  } else {
+    group.fields.forEach(f => {
+      if (f.field && ['select', 'link', 'text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
+        fields.push(f)
+      }
+    })
+  }
+
+  const groupForm = [
+    {
+      type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: group.setting.title || '',
+      required: true
+    },
+    {
+      type: 'text',
+      field: 'status',
+      label: '鐘舵�佸��',
+      initval: group.setting.status || '',
+      tooltip: '鐢ㄤ簬琛ㄥ崟鍔犺浇鏃剁殑鐘舵�佹帶鍒躲��',
+      required: false,
+      forbid: !group.prevButton
+    },
+    {
+      type: 'select',
+      field: 'focus',
+      label: '鐒︾偣',
+      initval: group.setting.focus || '',
+      required: false,
+      options: fields
+    },
+    {
+      type: 'radio',
+      field: 'align',
+      label: '琛ㄥ崟鎺掑垪',
+      initval: group.setting.align || 'left_right',
+      required: false,
+      options: [
+        {value: 'left_right', label: '宸﹀彸'},
+        {value: 'up_down', label: '涓婁笅'},
+      ],
+      forbid: appType === 'mob'
+    },
+    {
+      type: 'radio',
+      field: 'prevEnable',
+      label: '涓婁竴姝�',
+      initval: group.prevButton ? group.prevButton.enable || 'false' : 'false',
+      tooltip: '绗竴缁勪笉鏄剧ず銆�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ],
+      forbid: !group.prevButton
+    },
+    {
+      type: 'radio',
+      field: 'subEnable',
+      label: '鎻愪氦',
+      initval: group.subButton.enable || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'nextEnable',
+      label: '璺宠繃',
+      initval: group.nextButton ? group.nextButton.enable || 'false' : 'false',
+      tooltip: '鏈�鍚庝竴缁勪笉鏄剧ず銆�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ],
+      forbid: !group.nextButton
+    },
+  ]
+
+  return groupForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/form/formaction/actionform/index.jsx b/src/menu/components/form/formaction/actionform/index.jsx
index 35fb01b..381cf64 100644
--- a/src/menu/components/form/formaction/actionform/index.jsx
+++ b/src/menu/components/form/formaction/actionform/index.jsx
@@ -53,9 +53,10 @@
     } else if (card.type === 'next') {
       return ['type', 'label', 'enable']
     }
-    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open', 'enable'] // 閫夐」鍒楄〃
+    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open', 'enable', 'output'] // 閫夐」鍒楄〃
     
     if (_intertype === 'custom') {
+      _options.pop()
       _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
       if (_procMode === 'system') {
         _options.push('sql', 'sqlType')
diff --git a/src/menu/components/form/formaction/formconfig.jsx b/src/menu/components/form/formaction/formconfig.jsx
index 6eb2048..808208b 100644
--- a/src/menu/components/form/formaction/formconfig.jsx
+++ b/src/menu/components/form/formaction/formconfig.jsx
@@ -251,6 +251,14 @@
       options: menulist
     },
     {
+      type: 'text',
+      key: 'output',
+      label: '杩斿洖鍊�',
+      tooltip: '鎵ц鎴愬姛鍚庣殑杩斿洖鍊笺��',
+      initVal: card.output || '',
+      required: false
+    },
+    {
       type: 'radio',
       key: 'open',
       label: '鎵撳紑鏂瑰紡',
@@ -273,20 +281,6 @@
       tooltip: '鎵ц鎴愬姛鍚庨渶瑕佸埛鏂扮殑缁勪欢銆�',
       required: false,
       options: modules
-    },
-    {
-      type: 'radio',
-      key: 'enable',
-      label: '鏄惁鏄剧ず',
-      initVal: card.enable || 'true',
-      required: false,
-      options: [{
-        value: 'true',
-        text: '鏄剧ず'
-      }, {
-        value: 'false',
-        text: '闅愯棌'
-      }]
-    },
+    }
   ]
 }
diff --git a/src/menu/components/form/formaction/index.jsx b/src/menu/components/form/formaction/index.jsx
index 8127e42..a1819fd 100644
--- a/src/menu/components/form/formaction/index.jsx
+++ b/src/menu/components/form/formaction/index.jsx
@@ -53,7 +53,7 @@
     const { group } = this.props
 
     let _style = element.style ? fromJS(element.style).toJS() : {}
-    let options = ['font', 'border', 'padding', 'margin', 'backgroundColor']
+    let options = ['font', 'border', 'padding', 'margin', 'backgroundColor', 'width']
 
     this.setState({
       card: element
@@ -195,13 +195,13 @@
 
     return (
       <div className="mk-form-action">
-        {group.sort !== 1 ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+        {group.prevButton && group.prevButton.enable !== 'false' && group.sort !== 1 ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.handleAction(group.prevButton)} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.prevButton)} type="font-colors" />
           </div>
         } trigger="hover">
-          <Button type="link" className={'prev ' + group.prevButton.enable} style={resetStyle(group.prevButton.style)}>{group.prevButton.label}</Button>
+          <Button type="link" className="prev" style={resetStyle(group.prevButton.style)}>{group.prevButton.label}</Button>
         </Popover> : null}
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
@@ -212,13 +212,13 @@
         } trigger="hover">
           <Button type="link" className="submit mk-primary" onDoubleClick={this.changeMenu} style={resetStyle(group.subButton.style)}>{group.subButton.label}</Button>
         </Popover>
-        {group.sort !== config.subcards.length ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+        {group.nextButton && group.nextButton.enable !== 'false' && group.sort !== config.subcards.length ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.handleAction(group.nextButton)} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.nextButton)} type="font-colors" />
           </div>
         } trigger="hover">
-          <Button type="link" className={'skip ' + group.nextButton.enable} style={resetStyle(group.nextButton.style)}>{group.nextButton.label}</Button>
+          <Button type="link" className="skip" style={resetStyle(group.nextButton.style)}>{group.nextButton.label}</Button>
         </Popover> : null}
         {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
         <Modal
diff --git a/src/menu/components/form/formaction/index.scss b/src/menu/components/form/formaction/index.scss
index b7a1cd3..88793fc 100644
--- a/src/menu/components/form/formaction/index.scss
+++ b/src/menu/components/form/formaction/index.scss
@@ -4,23 +4,14 @@
   padding-bottom: 10px;
 
   .prev {
-    margin-right: 15px;
-  }
-  .prev.false {
-    span {
-      text-decoration: line-through;
-    }
+    height: auto;
   }
   .submit {
     border: none;
+    height: auto;
   }
   .skip {
-    position: absolute;
-    right: 5px;
-  }
-  .skip:not(.true) {
-    span {
-      text-decoration: line-through;
-    }
+    float: right;
+    height: auto;
   }
 }
diff --git a/src/menu/components/form/normal-form/groupform/index.jsx b/src/menu/components/form/normal-form/groupform/index.jsx
deleted file mode 100644
index 488cd6c..0000000
--- a/src/menu/components/form/normal-form/groupform/index.jsx
+++ /dev/null
@@ -1,153 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Select, Tooltip, Icon } from 'antd'
-import { formRule } from '@/utils/option.js'
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,     // 瀛楀吀椤�
-    group: PropTypes.object,    // 琛ㄥ崟閰嶇疆淇℃伅
-    inputSubmit: PropTypes.any  // 鍥炶溅鎻愪氦浜嬩欢
-  }
-
-  state = {
-    fields: null,
-    appType: sessionStorage.getItem('appType')
-  }
-
-  UNSAFE_componentWillMount () {
-    const { group } = this.props
-    const { appType } = this.state
-    let fields = []
-
-    if (appType === 'mob') {
-      group.fields.forEach(f => {
-        if (f.field && ['text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
-          fields.push(f)
-        }
-      })
-    } else {
-      group.fields.forEach(f => {
-        if (f.field && ['select', 'link', 'text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
-          fields.push(f)
-        }
-      })
-    }
-
-    this.setState({
-      fields: fields
-    })
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { group, dict } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { fields, appType } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <Form {...formItemLayout} className="ant-advanced-search-form modal-setting-form">
-        <Row gutter={24}>
-          <Col span={12}>
-            <Form.Item label="鏍囬">
-              {getFieldDecorator('title', {
-                initialValue: group.setting.title,
-                rules: [
-                  {
-                    max: formRule.input.max,
-                    message: formRule.input.message
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-          <Col span={12}>
-            <Form.Item label={
-              <Tooltip placement="topLeft" title="鐢ㄤ簬琛ㄥ崟鍔犺浇鏃剁殑鐘舵�佹帶鍒躲��">
-                <Icon type="question-circle" />
-                鐘舵�佸��
-              </Tooltip>
-            }>
-              {getFieldDecorator('status', {
-                initialValue: group.setting.status || '',
-                rules: [
-                  {
-                    max: formRule.input.max,
-                    message: formRule.input.message
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-          <Col span={12}>
-            <Form.Item label="鐒︾偣">
-              {getFieldDecorator('focus', {
-                initialValue: group.setting.focus || ''
-              })(
-                <Select
-                  showSearch
-                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                >
-                  <Select.Option value="">
-                    {dict['model.empty']}
-                  </Select.Option>
-                  {fields.map(option =>
-                    <Select.Option id={option.uuid} title={option.label} key={option.uuid} value={option.field}>
-                      {option.label}
-                    </Select.Option>
-                  )}
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-          {appType !== 'mob' ? <Col span={12}>
-            <Form.Item label="琛ㄥ崟鎺掑垪">
-              {getFieldDecorator('align', {
-                initialValue: group.setting.align || 'left_right'
-              })(
-                <Radio.Group>
-                  <Radio value="left_right">宸﹀彸</Radio>
-                  <Radio value="up_down">涓婁笅</Radio>
-                </Radio.Group>
-              )}
-            </Form.Item>
-          </Col> : null}
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/form/normal-form/groupform/index.scss b/src/menu/components/form/normal-form/groupform/index.scss
deleted file mode 100644
index 9a74987..0000000
--- a/src/menu/components/form/normal-form/groupform/index.scss
+++ /dev/null
@@ -1,18 +0,0 @@
-.ant-advanced-search-form.modal-setting-form {
-  .textarea {
-    .ant-form-item-label {
-      width: 16.3%;
-    }
-    .ant-form-item-control-wrapper {
-      width: 83.33333333%;
-    }
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .anticon-question-circle {
-    color: #c49f47;
-    position: relative;
-    left: -3px;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/form/normal-form/index.jsx b/src/menu/components/form/normal-form/index.jsx
index eae27ac..860f126 100644
--- a/src/menu/components/form/normal-form/index.jsx
+++ b/src/menu/components/form/normal-form/index.jsx
@@ -11,17 +11,17 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
-const WrapComponent = asyncIconComponent(() => import('@/menu/components/form/wrapsetting'))
 const CardComponent = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
 const MobCardComponent = asyncComponent(() => import('@/mob/components/formdragelement'))
 const FormTitle = asyncComponent(() => import('../dragtitle'))
-const GroupForm = asyncComponent(() => import('./groupform'))
 const FormAction = asyncComponent(() => import('../formaction'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
@@ -79,9 +79,9 @@
           sort: 1,
           style: {},
           fields: [],
-          prevButton: {label: '涓婁竴姝�', type: 'prev', enable: 'true'},
-          subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
-          nextButton: {label: '璺宠繃', type: 'next', enable: 'false'}
+          prevButton: {label: '涓婁竴姝�', type: 'prev', enable: 'false', style: {marginRight: '15px', paddingTop: '5px', paddingBottom: '5px'}},
+          subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px', paddingTop: '5px', paddingBottom: '5px'}},
+          nextButton: {label: '璺宠繃', type: 'next', enable: 'false', style: {paddingTop: '5px', paddingBottom: '5px'}}
         }]
       }
 
@@ -185,7 +185,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -211,17 +211,16 @@
       sort: card.subcards.length + 1,
       style: {},
       fields: [],
-      prevButton: {label: '涓婁竴姝�', type: 'prev', enable: 'true'},
-      subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
-      nextButton: {label: '璺宠繃', type: 'next', enable: 'false'}
+      prevButton: {label: '涓婁竴姝�', type: 'prev', enable: 'false', style: {marginRight: '15px', paddingTop: '5px', paddingBottom: '5px'}},
+      subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px', paddingTop: '5px', paddingBottom: '5px'}},
+      nextButton: {label: '璺宠繃', type: 'next', enable: 'false', style: {paddingTop: '5px', paddingBottom: '5px'}}
     }
 
     card.subcards.push(newcard)
     
     this.setState({
       card,
-      group: newcard,
-      groupvisible: true
+      group: newcard
     })
     this.props.updateConfig(card)
   }
@@ -244,10 +243,7 @@
   }
 
   changeGroup = (item) => {
-    this.setState({
-      group: item,
-      groupvisible: true
-    })
+    this.updateGroup(item)
   }
 
   closeGroup = (cell) => {
@@ -286,16 +282,6 @@
     this.props.updateConfig(card)
   }
 
-  handleGroupSubmit = () => {
-    const { group } = this.state
-
-    this.groupRef.handleConfirm().then(res => {
-      group.setting = res
-      this.setState({groupvisible: false})
-      this.updateGroup(group)
-    })
-  }
-  
   changecols = (type) => {
     let card = fromJS(this.state.card).toJS()
     let config = fromJS(this.state.group).toJS()
@@ -638,6 +624,14 @@
     })
   }
 
+  getWrapForms = () => {
+    return getWrapForm(this.state.card)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -653,7 +647,9 @@
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="plus" title="娣诲姞鍒嗙粍" onClick={this.addCard} type="plus" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="琛ㄥ崟璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="propcard" card={card}/>
             <PasteComponent config={card} options={['form']} updateConfig={this.pasteForm} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
@@ -700,22 +696,6 @@
           />}
           <FormAction config={card} group={group} updateconfig={this.updateGroup}/>
         </div> : null}
-        <Modal
-          title="鍒嗙粍缂栬緫"
-          visible={this.state.groupvisible}
-          width={850}
-          maskClosable={false}
-          onCancel={() => this.setState({groupvisible: false})}
-          onOk={this.handleGroupSubmit}
-          destroyOnClose
-        >
-          <GroupForm
-            dict={dict}
-            group={group}
-            inputSubmit={this.handleGroupSubmit}
-            wrappedComponentRef={(inst) => this.groupRef = inst}
-          />
-        </Modal>
         <Modal
           title={this.state.dict['model.edit']}
           visible={this.state.visible}
diff --git a/src/menu/components/form/normal-form/options.jsx b/src/menu/components/form/normal-form/options.jsx
new file mode 100644
index 0000000..1d8278c
--- /dev/null
+++ b/src/menu/components/form/normal-form/options.jsx
@@ -0,0 +1,106 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (config) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+  let wrap = config.wrap
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const wrapForm = [
+    {
+      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: '闈欐��'},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'statusControl',
+      label: '鐘舵�佹帶鍒�',
+      initval: wrap.statusControl || '',
+      tooltip: '琛ㄥ崟鍔犺浇鏃剁殑鐘舵�侊紝褰撳瓧娈靛�间笌琛ㄥ崟缁勭殑鐘舵�佸�间竴鑷存椂锛屽惎鐢ㄥ搴旂殑琛ㄥ崟缁勩��',
+      required: false,
+      options: config.columns,
+      forbid: config.subtype === 'tabform'
+    },
+    {
+      type: 'radio',
+      field: 'groupLabel',
+      label: '鍒嗙粍鍚嶇О',
+      initval: wrap.groupLabel || 'show',
+      tooltip: '鍔犺浇鏃舵槸鍚︽樉绀哄垎缁勫悕绉般��',
+      required: false,
+      options: [
+        {value: 'show', label: '鏄剧ず'},
+        {value: 'hidden', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'tabtype',
+      label: '鍒嗙粍椋庢牸',
+      initval: wrap.tabtype || 'mktab',
+      required: false,
+      options: [
+        {value: 'mktab', label: 'tab椤�'},
+        {value: 'mkbtn', label: '鎸夐挳缁�'},
+      ],
+      forbid: config.subtype !== 'tabform'
+    },
+    {
+      type: 'color',
+      field: 'color',
+      label: '棰滆壊鎺у埗',
+      initval: wrap.color || '#1890ff',
+      tooltip: '瀹屾垚鍚庣殑棰滆壊',
+      required: false,
+      forbid: config.subtype === 'tabform'
+    },
+    {
+      type: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: wrap.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType
+    },
+  ]
+
+  return wrapForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/form/tab-form/index.jsx b/src/menu/components/form/tab-form/index.jsx
new file mode 100644
index 0000000..5397c8c
--- /dev/null
+++ b/src/menu/components/form/tab-form/index.jsx
@@ -0,0 +1,715 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover, Modal, Button, Switch, notification } from 'antd'
+import moment from 'moment'
+
+import Api from '@/api'
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+import { getModalForm } from '@/templates/zshare/formconfig'
+import { resetStyle } from '@/utils/utils-custom.js'
+import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
+import getWrapForm from '../normal-form/options'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import './index.scss'
+
+const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
+const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CardComponent = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
+const MobCardComponent = asyncComponent(() => import('@/mob/components/formdragelement'))
+const FormTitle = asyncComponent(() => import('../dragtitle'))
+const FormAction = asyncComponent(() => import('../formaction'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
+
+const { confirm } = Modal
+
+class PropCardEditComponent extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    appType: sessionStorage.getItem('appType'),
+    card: null,
+    back: false,
+    group: null,
+    showField: false,
+    visible: false,
+    editform: null,
+    formlist: null,
+    sqlVerifing: false,
+    standardform: null
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        floor: card.floor,
+        tabId: card.tabId || '',
+        parentId: card.parentId || '',
+        format: 'object',   // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,  // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        dataName: card.dataName || '',
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { },
+        wrap: { name: card.name, width: card.width || 24, datatype: 'static', groupLabel: 'show', color: '#1890ff', tabtype: 'mktab' },
+        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
+        columns: [],
+        scripts: [],
+        subcards: [{
+          uuid: Utils.getuuid(),
+          setting: {title: '绗竴姝�', align: 'left_right'},
+          sort: 1,
+          style: {},
+          fields: [],
+          subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px', paddingTop: '5px', paddingBottom: '5px'}},
+        }]
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+
+        _card.subcards = config.subcards.map(scard => {
+          scard.uuid = Utils.getuuid()
+          scard.fields = scard.fields.map(elem => {
+            elem.uuid = Utils.getuuid()
+            return elem
+          })
+          return scard
+        })
+      }
+      this.setState({
+        card: _card,
+        group: _card.subcards[0] || null
+      })
+      this.props.updateConfig(_card)
+    } else {
+      let _card = fromJS(card).toJS()
+      this.setState({
+        card: _card,
+        group: _card.subcards[0] || null
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { card } = this.state
+
+    if (card.uuid !== parentId) return
+
+    let subcards = card.subcards.map(item => {
+      if (keys.includes(item.uuid)) {
+        item.style = {...item.style, ...style}
+      }
+      return item
+    })
+
+    this.setState({card: {...card, subcards: []}}, () => {
+      this.updateComponent({...card, subcards: subcards})
+    })
+  }
+
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (component) => {
+    this.setState({
+      card: component
+    })
+
+    component.width = component.wrap.width
+    component.name = component.wrap.name
+
+    this.props.updateConfig(component)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  updateCard = (cell) => {
+    let card = fromJS(this.state.card).toJS()
+
+    card.subcards = card.subcards.map(item => {
+      if (item.uuid === cell.uuid) return cell
+      return item
+    })
+
+    this.setState({card})
+
+    this.props.updateConfig(card)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
+  }
+
+  getStyle = (comIds, style) => {
+    const { card } = this.state
+
+    if (comIds.length !== 1 || comIds[0] !== card.uuid) return
+
+    let _card = {...card, style}
+
+    this.setState({
+      card: _card
+    })
+    
+    this.props.updateConfig(_card)
+  }
+
+  addCard = () => {
+    let card = fromJS(this.state.card).toJS()
+
+    let newcard = {
+      uuid: Utils.getuuid(),
+      setting: { title: `绗�${card.subcards.length + 1}姝, align: 'left_right' },
+      sort: card.subcards.length + 1,
+      style: {},
+      fields: [],
+      subButton: {label: '鎻愪氦', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px', paddingTop: '5px', paddingBottom: '5px'}},
+    }
+
+    card.subcards.push(newcard)
+    
+    this.setState({
+      card,
+      group: newcard
+    })
+    this.props.updateConfig(card)
+  }
+
+  changecards = (list) => {
+    let card = fromJS(this.state.card).toJS()
+    card.subcards = list.map((item, index) => {
+      item.sort = index + 1
+      return item
+    })
+
+    this.setState({card})
+    this.props.updateConfig(card)
+  }
+
+  selectGroup = (item) => {
+    this.setState({
+      group: item
+    })
+  }
+
+  closeGroup = (cell) => {
+    const { group } = this.state
+    let card = fromJS(this.state.card).toJS()
+    const _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎鍒嗙粍鍚楋紵',
+      onOk() {
+        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
+        let _group = group
+
+        if (group.uuid === cell.uuid) {
+          _group = card.subcards[0] || null
+        }
+
+        _this.setState({card, group: _group})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  updateGroup = (group) => {
+    let card = fromJS(this.state.card).toJS()
+
+    card.subcards = card.subcards.map(item => {
+      if (item.uuid === group.uuid) {
+        return group
+      }
+      return item
+    })
+
+    this.setState({card, group})
+    this.props.updateConfig(card)
+  }
+  
+  changecols = (type) => {
+    let card = fromJS(this.state.card).toJS()
+    let config = fromJS(this.state.group).toJS()
+    let _this = this
+
+    config.fields = config.fields.map(item => {
+      item.labelwidth = 33.3
+      item.span = 24
+      if (['textarea','split','hint','checkcard','brafteditor'].includes(item.type)) {
+        if (type === 2) {
+          item.labelwidth = 16.3
+        } else if (type === 3) {
+          item.labelwidth = 10.5
+        } else if (type === 4) {
+          item.labelwidth = 8.3
+        }
+      } else if (type === 2) {
+        item.span = 12
+      } else if (type === 3) {
+        item.span = 8
+      } else if (type === 4) {
+        item.span = 6
+      }
+      return item
+    })
+    
+    confirm({
+      content: `纭畾鍒囨崲涓�${type}鍒楀悧锛焋,
+      onOk() {
+        card.subcards = card.subcards.map(item => {
+          if (item.uuid === config.uuid) {
+            return config
+          }
+          return item
+        })
+        _this.setState({group: config, card})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  handleList = (list, newcard) => {
+    let group = fromJS(this.state.group).toJS()
+    let card = fromJS(this.state.card).toJS()
+
+    group.fields = list
+
+    card.subcards = card.subcards.map(item => {
+      if (item.uuid === group.uuid) {
+        return group
+      }
+      return item
+    })
+
+    this.setState({card, group}, () => {
+      if (newcard) {
+        this.handleForm(newcard)
+      }
+    })
+    this.props.updateConfig(card)
+  }
+
+  closeForm = (cell) => {
+    let group = fromJS(this.state.group).toJS()
+    let card = fromJS(this.state.card).toJS()
+    let _this = this
+
+    group.fields = group.fields.filter(item => item.uuid !== cell.uuid)
+
+    card.subcards = card.subcards.map(item => {
+      if (item.uuid === group.uuid) {
+        return group
+      }
+      return item
+    })
+
+    confirm({
+      content: `纭畾鍒犻櫎<<${cell.label}>>鍚楋紵`,
+      onOk() {
+        _this.setState({card, group})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  addForm = () => {
+    const { appType } = this.state
+    let group = fromJS(this.state.group).toJS()
+    let lastItem = group.fields[group.fields.length - 1]
+    let span = appType === 'mob' ? 24 : 12
+    if (lastItem && lastItem.span) {
+      span = lastItem.span
+    }
+
+    let newcard = {
+      uuid: Utils.getuuid(),
+      label: '',
+      field: '',
+      initval: '',
+      type: 'text',
+      resourceType: '0',
+      setAll: 'false',
+      span: span,
+      labelwidth: 33.3,
+      options: [],
+      dataSource: '',
+      decimal: 0,
+      orderType: 'asc',
+      readonly: 'false',
+      required: 'true',
+      focus: true
+    }
+
+    group.fields.push(newcard)
+
+    this.setState({group}, () => {
+      this.handleForm(newcard)
+    })
+  }
+
+  editModalCancel = () => {
+    let group = fromJS(this.state.group).toJS()
+    group.fields = group.fields.filter(item => !item.focus)
+
+    this.setState({group, visible: false, editform: null})
+    this.updateGroup(group)
+  }
+
+  /**
+   * @description 琛ㄥ崟缂栬緫
+   */
+  handleForm = (_item) => {
+    const { card, group, appType } = this.state
+    let _form = fromJS(_item).toJS()
+    let _inputfields = []
+    let _tabfields = []
+    let _linkableFields = []
+    let _linksupFields = [{
+      value: '',
+      text: '绌�'
+    }]
+    let standardform = null
+
+    _inputfields = group.fields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
+    if (appType === 'mob') {
+      _tabfields = group.fields.filter(item => _form.field !== item.field && item.hidden !== 'true' && ['text', 'number'].includes(item.type))
+    } else {
+      _tabfields = group.fields.filter(item => _form.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
+    }
+    _tabfields.unshift({field: '', text: '鍘熻〃鍗�'})
+
+    let uniq = new Map()
+    uniq.set(_form.field, true)
+    let index = null
+    group.fields.forEach((item, i) => {
+      if (_form.uuid === item.uuid) {
+        index = i
+      }
+      
+      if (!['select', 'link', 'radio', 'checkcard'].includes(item.type)) return
+      if (item.field && !uniq.has(item.field)) {
+        uniq.set(item.field, true)
+
+        _linkableFields.push({
+          value: item.field,
+          text: item.label + ' (琛ㄥ崟)'
+        })
+        _linksupFields.push({
+          value: item.field,
+          text: item.label
+        })
+      }
+    })
+    if (index !== null) {
+      if (index === 0) {
+        standardform = group.fields[index + 1] || null
+      } else {
+        standardform = group.fields[index - 1] || null
+      }
+    }
+
+    card.columns.forEach(col => {
+      if (col.field && !uniq.has(col.field)) {
+        uniq.set(col.field, true)
+
+        _linkableFields.push({
+          value: col.field,
+          text: col.label + ' (鏄剧ず鍒�)'
+        })
+      }
+    })
+
+    if (_form.linkSubField && _form.linkSubField.length > 0) {
+      let fields = _inputfields.map(item => item.field)
+      _form.linkSubField = _form.linkSubField.filter(item => fields.includes(item))
+    }
+
+    if (appType !== 'mob' && !_form.span && standardform && standardform.span) {
+      _form.span = standardform.span
+      _form.labelwidth = standardform.labelwidth
+    }
+
+    this.setState({
+      standardform,
+      visible: true,
+      editform: _form,
+      formlist: getModalForm(_form, _inputfields, _tabfields, _linkableFields, _linksupFields, false)
+    })
+  }
+
+  /**
+   * @description 缂栬緫鍚庢彁浜�
+   * 1銆佽幏鍙栫紪杈戝悗鐨勮〃鍗曚俊鎭�
+   * 2銆佸幓闄ゅ彲鑳藉瓨鍦ㄧ殑绀轰緥琛ㄥ崟
+   * 3銆侀�氳繃loading鍒锋柊
+   */
+  handleSubmit = () => {
+    this.formRef.handleConfirm().then(res => {
+      let _config = fromJS(this.state.group).toJS()
+      let fieldrepet = false // 瀛楁閲嶅
+      let labelrepet = false // 鎻愮ず鏂囧瓧閲嶅
+
+      _config.fields = _config.fields.map(item => {
+        if (item.uuid !== res.uuid && res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
+          fieldrepet = true
+        } else if (res.label && item.uuid !== res.uuid && item.label === res.label) {
+          labelrepet = true
+        }
+
+        if (item.uuid === res.uuid) {
+          return res
+        } else {
+          return item
+        }
+      })
+
+      if (fieldrepet) {
+        notification.warning({
+          top: 92,
+          message: '瀛楁宸插瓨鍦紒',
+          duration: 10
+        })
+        return
+      } else if (labelrepet) {
+        notification.warning({
+          top: 92,
+          message: '鍚嶇О宸插瓨鍦紒',
+          duration: 10
+        })
+        return
+      }
+
+      if (['select', 'multiselect', 'link', 'checkbox', 'radio', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
+        this.setState({
+          sqlVerifing: true
+        })
+
+        let param = {
+          func: 's_debug_sql',
+          exec_type: 'y',
+          LText: res.dataSource
+        }
+
+        param.LText = param.LText.replace(/@\$|\$@/ig, '')
+        
+        param.LText = Utils.formatOptions(param.LText)
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt('', param.timestamp)
+
+        if (window.GLOB.mainSystemApi && res.database === 'sso') {
+          param.rduri = window.GLOB.mainSystemApi
+        }
+        
+        Api.getLocalConfig(param).then(result => {
+          if (result.status) {
+            this.setState({
+              sqlVerifing: false,
+              editform: null,
+              visible: false
+            })
+            this.updateGroup(_config)
+          } else {
+            this.setState({sqlVerifing: false})
+            
+            Modal.error({
+              title: result.message
+            })
+          }
+        })
+      } else {
+        this.setState({
+          editform: null,
+          visible: false
+        })
+        this.updateGroup(_config)
+      }
+    })
+  }
+
+  pasteForm = (res) => {
+    let _config = fromJS(this.state.group).toJS()
+    let fieldrepet = false // 瀛楁閲嶅
+    let labelrepet = false // 鎻愮ず鏂囧瓧閲嶅
+
+    _config.fields.forEach(item => {
+      if (res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
+        fieldrepet = true
+      } else if (res.label && item.label === res.label) {
+        labelrepet = true
+      }
+    })
+
+    if (fieldrepet) {
+      notification.warning({
+        top: 92,
+        message: '瀛楁宸插瓨鍦紒',
+        duration: 10
+      })
+      return
+    } else if (labelrepet) {
+      notification.warning({
+        top: 92,
+        message: '鍚嶇О宸插瓨鍦紒',
+        duration: 10
+      })
+      return
+    }
+    _config.fields.push(res)
+
+    this.updateGroup(_config)
+
+    this.handleForm(res)
+
+    notification.success({
+      top: 92,
+      message: '绮樿创鎴愬姛锛�',
+      duration: 2
+    })
+  }
+
+  getWrapForms = () => {
+    return getWrapForm(this.state.card)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  render() {
+    const { card, dict, group, appType } = this.state
+
+    return (
+      <div className="menu-normal-form-edit-box" style={resetStyle(card.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">
+            <Icon className="plus" title="娣诲姞鍒嗙粍" onClick={this.addCard} type="plus" />
+            <NormalForm title="琛ㄥ崟璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
+            <CopyComponent type="propcard" card={card}/>
+            <PasteComponent config={card} options={['form']} updateConfig={this.pasteForm} />
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <UserComponent config={card}/>
+            <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
+            {card.wrap.datatype !== 'static' ? <SettingComponent config={card} updateConfig={this.updateComponent} /> : null}
+            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+        <FormTitle
+          list={card.subcards}
+          tabtype={card.wrap.tabtype || ''}
+          selectId={group ? group.uuid : ''}
+          handleList={this.changecards}
+          handleGroup={this.updateGroup}
+          closeGroup={this.closeGroup}
+          selectGroup={this.selectGroup}
+        />
+        {group ? <div className="form-area">
+          <Icon className="plus" title="娣诲姞琛ㄥ崟" onClick={this.addForm} type="plus" />
+          <FieldsComponent config={group} type="form" updatefield={this.updateGroup} />
+          <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
+          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1鍒�</Button> : null}
+          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2鍒�</Button> : null}
+          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3鍒�</Button> : null}
+          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(4)}>4鍒�</Button> : null}
+          <div style={{clear: 'both'}}></div>
+          {appType !== 'mob' ? <CardComponent
+            list={group.fields}
+            setting={group.setting}
+            showField={this.state.showField}
+            placeholder={dict['header.form.modal.placeholder']}
+            handleList={this.handleList}
+            handleForm={this.handleForm}
+            closeForm={this.closeForm}
+          /> : <MobCardComponent
+            list={group.fields}
+            setting={group.setting}
+            showField={this.state.showField}
+            handleList={this.handleList}
+            handleForm={this.handleForm}
+            closeForm={this.closeForm}
+          />}
+          <FormAction config={card} group={group} updateconfig={this.updateGroup}/>
+        </div> : null}
+        <Modal
+          title={this.state.dict['model.edit']}
+          visible={this.state.visible}
+          width={850}
+          onCancel={this.editModalCancel}
+          onOk={this.handleSubmit}
+          confirmLoading={this.state.sqlVerifing}
+          destroyOnClose
+        >
+          <ModalForm
+            dict={this.state.dict}
+            card={this.state.editform}
+            formlist={this.state.formlist}
+            inputSubmit={this.handleSubmit}
+            standardform={this.state.standardform}
+            wrappedComponentRef={(inst) => this.formRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default PropCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/form/tab-form/index.scss b/src/menu/components/form/tab-form/index.scss
new file mode 100644
index 0000000..0fb6111
--- /dev/null
+++ b/src/menu/components/form/tab-form/index.scss
@@ -0,0 +1,78 @@
+.menu-normal-form-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 30px;
+  
+  .card-control {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    .anticon-tool {
+      right: auto;
+      left: 1px;
+      padding: 1px;
+    }
+  }
+  .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);
+  }
+
+  .page-card {
+    position: relative;
+    background: #ffffff;
+    border-radius: 2px;
+    margin-bottom: 15px;
+  }
+  .form-area {
+    position: relative;
+    >.plus {
+      color: #26C281;
+      cursor: pointer;
+      padding: 4px 10px;
+    }
+    >button {
+      float: right;
+      margin-right: 10px;
+    }
+    >.mk-cols-change {
+      height: 24px;
+      padding: 0 10px;
+    }
+    >.quickly-add {
+      display: inline-block;
+      margin-left: 10px;
+      button {
+        color: #1890ff;
+        background: transparent;
+        border: none;
+        box-shadow: none;
+        padding: 0;
+        height: 24px;
+      }
+    }
+    .modal-fields-row {
+      padding-top: 10px;
+      padding-bottom: 30px;
+    }
+  }
+}
+.menu-normal-form-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-normal-form-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/form/wrapsetting/index.jsx b/src/menu/components/form/wrapsetting/index.jsx
deleted file mode 100644
index c159968..0000000
--- a/src/menu/components/form/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="琛ㄥ崟璁剧疆"
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/menu/components/form/wrapsetting/index.scss b/src/menu/components/form/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/menu/components/form/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/form/wrapsetting/settingform/index.jsx b/src/menu/components/form/wrapsetting/settingform/index.jsx
deleted file mode 100644
index ea4257c..0000000
--- a/src/menu/components/form/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,205 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import asyncComponent from '@/utils/asyncComponent'
-import './index.scss'
-
-const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    roleList: []
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap, config } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鍒濆鍊兼潵婧愪簬鏁版嵁婧愭垨琛ㄥ崟榛樿鍊笺��">
-                  <Icon type="question-circle" />
-                  鍒濆鍊�
-                </Tooltip>
-              }>
-                {getFieldDecorator('datatype', {
-                  initialValue: wrap.datatype || 'dynamic'
-                })(
-                  <Radio.Group>
-                    <Radio value="dynamic">鍔ㄦ��</Radio>
-                    <Radio value="static">闈欐��</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="琛ㄥ崟鍔犺浇鏃剁殑鐘舵�侊紝褰撳瓧娈靛�间笌琛ㄥ崟缁勭殑鐘舵�佸�间竴鑷存椂锛屽惎鐢ㄥ搴旂殑琛ㄥ崟缁勩��">
-                  <Icon type="question-circle" />
-                  鐘舵�佹帶鍒�
-                </Tooltip>
-              }>
-                {getFieldDecorator('statusControl', {
-                  initialValue: wrap.statusControl || ''
-                })(
-                  <Select>
-                    <Select.Option key='' value={''}>鏃�</Select.Option>
-                    {config.columns.map(option =>
-                      <Select.Option key={option.uuid} value={option.field}>{option.label}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鍔犺浇鏃舵槸鍚︽樉绀哄垎缁勫悕绉般��">
-                  <Icon type="question-circle" />
-                  鍒嗙粍鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('groupLabel', {
-                  initialValue: wrap.groupLabel || 'show'
-                })(
-                  <Radio.Group>
-                    <Radio value="show">鏄剧ず</Radio>
-                    <Radio value="hidden">闅愯棌</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="瀹屾垚鍚庣殑棰滆壊">
-                  <Icon type="question-circle" />
-                  棰滆壊鎺у埗
-                </Tooltip>
-              }>
-                {getFieldDecorator('color', {
-                  initialValue: wrap.color || '#1890ff'
-                })(
-                  <ColorSketch />
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/form/wrapsetting/settingform/index.scss b/src/menu/components/form/wrapsetting/settingform/index.scss
deleted file mode 100644
index 65d3417..0000000
--- a/src/menu/components/form/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,15 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .color-sketch-block {
-    position: relative;
-    top: 6px;
-  }
-}
\ No newline at end of file
diff --git a/src/menu/components/group/groupcomponents/card.jsx b/src/menu/components/group/groupcomponents/card.jsx
index ec99ddf..4f58886 100644
--- a/src/menu/components/group/groupcomponents/card.jsx
+++ b/src/menu/components/group/groupcomponents/card.jsx
@@ -14,9 +14,11 @@
 const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
 const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
 const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
 const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
 const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
 const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
+const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 
 const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
@@ -63,8 +65,10 @@
       return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'scatter') {
       return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'form') {
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
       return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'card' && card.subtype === 'datacard') {
       return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
     } else if (card.type === 'card' && card.subtype === 'propcard') {
@@ -81,6 +85,8 @@
       return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'code') {
       return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'balcony') {
+      return (<Balcony 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 ac14bfa..52d00b5 100644
--- a/src/menu/components/group/groupcomponents/index.jsx
+++ b/src/menu/components/group/groupcomponents/index.jsx
@@ -67,7 +67,7 @@
 
       let name = ''
       let names = {
-        bbar: '鏌辩姸鍥�',
+        bar: '鏌辩姸鍥�',
         line: '鎶樼嚎鍥�',
         tabs: '鏍囩缁�',
         pie: '楗煎浘',
@@ -81,6 +81,7 @@
         dashboard: '浠〃鐩�',
         scatter: '鏁g偣鍥�',
         tree: '鏍戝舰鍒楄〃',
+        balcony: '娴姩鍗�',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/components/group/normal-group/index.jsx b/src/menu/components/group/normal-group/index.jsx
index a0ac08f..ac34bbd 100644
--- a/src/menu/components/group/normal-group/index.jsx
+++ b/src/menu/components/group/normal-group/index.jsx
@@ -101,7 +101,7 @@
   changeStyle = () => {
     const { group } = this.state
 
-    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin'], group.style)
+    MKEmitter.emit('changeStyle', [group.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], group.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/components/search/main-search/index.jsx b/src/menu/components/search/main-search/index.jsx
index 2de3892..91421d3 100644
--- a/src/menu/components/search/main-search/index.jsx
+++ b/src/menu/components/search/main-search/index.jsx
@@ -18,6 +18,7 @@
 const { confirm } = Modal
 
 const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const FieldsComponent = asyncIconComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 const SearchForm = asyncIconComponent(() => import('@/templates/sharecomponent/searchcomponent/searchform'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
@@ -107,7 +108,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   /**
@@ -363,6 +364,12 @@
     })
   }
 
+  updatefields = (config) => {
+    this.setState({card: config}, ()=> {
+      this.props.updateConfig(config)
+    })
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -376,6 +383,7 @@
 
     return (
       <div className={`main-search-edit-list ${card.wrap.float} ${card.wrap.show || ''}`} onClick={this.clickComponent} id={card.uuid} style={_style}>
+        <FieldsComponent config={card} type="search" updatefield={this.updatefields} />
         <Switch checkedChildren={dict['model.switch.open']} size="small" unCheckedChildren={dict['model.switch.close']} defaultChecked={showField} onChange={this.onFieldChange} />
         <DragElement
           list={card.search}
diff --git a/src/menu/components/search/main-search/index.scss b/src/menu/components/search/main-search/index.scss
index 09caf96..d7eb2f7 100644
--- a/src/menu/components/search/main-search/index.scss
+++ b/src/menu/components/search/main-search/index.scss
@@ -102,6 +102,20 @@
       margin-left: 5px;
     }
   }
+  .quickly-add {
+    position: absolute;
+    z-index: 3;
+    right: 55px;
+    bottom: 3px;
+    .ant-btn-block {
+      background-color: transparent;
+      color: #1890ff;
+      border: none;
+      box-shadow: none!important;
+      height: 18px;
+      padding: 0 10px;
+    }
+  }
 }
 .main-search-edit-list:not(.right) {
   .pre-action {
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index 26122e5..1e55d87 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -10,12 +10,12 @@
 const { TextArea } = Input
 const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
 const actionTypeOptions = {
-  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
-  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
-  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
+  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open', 'output'],
+  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open', 'output'],
+  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open', 'output'],
   excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'sheet', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width'],
   excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent', 'resetPageIndex', 'pagination', 'search', 'width'],
-  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'popClose', 'resetPageIndex', 'width'],
+  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'popClose', 'resetPageIndex', 'width', 'display', 'ratio', 'placement'],
   tab: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'linkmenu', 'width'],
   innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'icon', 'class', 'width', 'open'],
   funcbutton: ['label', 'OpenType', 'funcType', 'show', 'icon', 'class', 'width']
@@ -38,6 +38,7 @@
     funcType: null,  // 鍔熻兘绫诲瀷
     procMode: null,  // 鍙傛暟鏂瑰紡
     pageTemplate: null,
+    appType: sessionStorage.getItem('appType'),
     Ot: null,
     requireOptions: [{
       value: 'notRequired',
@@ -174,9 +175,12 @@
         } else if (_intertype === 'inner') {
           _options.push('innerFunc')
         }
+      } else if (_funcType === 'mkBinding' || _funcType === 'mkUnBinding') {
+        _options.push('execSuccess', 'execError')
       }
     } else if (_opentype !== 'popview' && _opentype !== 'tab') {
       if (_intertype === 'custom') {
+        _options = _options.filter(m => m !== 'output')
         _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
         if (_procMode === 'system') {
           _options.push('sql', 'sqlType')
@@ -193,7 +197,13 @@
     }
 
     if (_Ot !== 'notRequired' && _opentype !== 'excelOut') {
-      _options.push('controlField', 'controlVal')
+      if (this.state.appType === 'mob') {
+        if (_opentype !== 'funcbutton') {
+          _options.push('controlField', 'controlVal')
+        }
+      } else {
+        _options.push('controlField', 'controlVal')
+      }
     }
     if (_Ot === 'requiredSgl' && ['pop', 'prompt', 'exec'].includes(_opentype)) {
       _options.push('swipe')
diff --git a/src/menu/components/share/actioncomponent/dragaction/card.jsx b/src/menu/components/share/actioncomponent/dragaction/card.jsx
index d0af55e..4a6e8b5 100644
--- a/src/menu/components/share/actioncomponent/dragaction/card.jsx
+++ b/src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -38,6 +38,7 @@
 
   let btnElement = null
   let _style = resetStyle(card.style)
+
   if (card.show === 'icon') {
     btnElement = (
       <Button
diff --git a/src/menu/components/share/actioncomponent/formconfig.jsx b/src/menu/components/share/actioncomponent/formconfig.jsx
index b038d5c..c423af5 100644
--- a/src/menu/components/share/actioncomponent/formconfig.jsx
+++ b/src/menu/components/share/actioncomponent/formconfig.jsx
@@ -74,7 +74,6 @@
     if (appMenus) {
       try {
         appMenus = JSON.parse(appMenus)
-        appMenus = appMenus.map(item => ({value: item.MenuID, text: item.MenuName}))
       } catch {
         appMenus = []
       }
@@ -83,7 +82,13 @@
     }
 
     if (appType === 'mob') {
-      opentypes = opentypes.filter(item => ['pop', 'prompt', 'exec', 'innerpage'].includes(item.value))
+      opentypes = opentypes.filter(item => ['pop', 'prompt', 'exec', 'innerpage', 'funcbutton'].includes(item.value))
+      funTypes = [
+        { value: 'mkBinding', text: '寮�閫氭壂鐮佺櫥褰�' },
+        { value: 'mkUnBinding', text: '鐢ㄦ埛瑙g粦' },
+        { value: 'scan', text: '鎵竴鎵�' },
+        { value: 'reAuth', text: '閲嶆柊鎺堟潈' },
+      ]
     } else {
       opentypes = opentypes.filter(item => item.value !== 'tab')
     }
@@ -415,7 +420,7 @@
     {
       type: 'radio',
       key: 'show',
-      label: "鏄剧ず涓�",
+      label: '鏄剧ず涓�',
       initVal: card.show || 'button',
       required: true,
       options: [{
@@ -551,6 +556,15 @@
       options: appMenus
     },
     {
+      type: 'text',
+      key: 'output',
+      label: '杩斿洖鍊�',
+      tooltip: '鎵ц鎴愬姛鍚庣殑杩斿洖鍊笺��',
+      initVal: card.output || '',
+      forbid: appType !== 'pc' && appType !== 'mob',
+      required: false
+    },
+    {
       type: 'radio',
       key: 'open',
       label: '鎵撳紑鏂瑰紡',
@@ -564,7 +578,53 @@
         value: 'self',
         text: '褰撳墠绐楀彛'
       }]
-    }
+    },
+    {
+      type: 'radio',
+      key: 'display',
+      label: '鏄剧ず鏂瑰紡',
+      initVal: card.display || 'modal',
+      required: true,
+      options: [{
+        value: 'modal',
+        text: '妯℃�佹'
+      }, {
+        value: 'drawer',
+        text: '鎶藉眽'
+      }]
+    },
+    {
+      type: 'number',
+      key: 'ratio',
+      min: 1,
+      max: 24,
+      precision: 0,
+      label: '姣斾緥',
+      initVal: card.ratio || 85,
+      tooltip: '灏忎簬100涓哄搴︼紙鎴栭珮搴︼級鐧惧垎姣旓紝澶т簬100涓哄儚绱犲�笺��',
+      required: true
+    },
+    {
+      type: 'radio',
+      key: 'placement',
+      label: '鎶藉眽鏂瑰悜',
+      initVal: card.placement || 'right',
+      tooltip: '浣跨敤鎶藉眽鏃舵湁鏁堛��',
+      required: false,
+      options: [{
+        value: 'right',
+        text: '鍙充晶'
+      }, {
+        value: 'left',
+        text: '宸︿晶'
+      }, {
+        value: 'top',
+        text: '涓婁晶'
+      }, {
+        value: 'bottom',
+        text: '涓嬩晶'
+      }]
+    },
   ]
 
   return forms
diff --git a/src/menu/components/share/pastecomponent/index.jsx b/src/menu/components/share/pastecomponent/index.jsx
index 553d24d..e367d11 100644
--- a/src/menu/components/share/pastecomponent/index.jsx
+++ b/src/menu/components/share/pastecomponent/index.jsx
@@ -148,7 +148,7 @@
         MKEmitter.emit('copyButtons', copyBtns)
       }
 
-      if (config.type === 'form' && config.subtype === 'stepform') {
+      if (config.type === 'form') {
         this.props.updateConfig(res)
         this.setState({visible: false})
         return
diff --git a/src/menu/components/share/sourcecomponent/index.jsx b/src/menu/components/share/sourcecomponent/index.jsx
index a5b88bc..569fc8d 100644
--- a/src/menu/components/share/sourcecomponent/index.jsx
+++ b/src/menu/components/share/sourcecomponent/index.jsx
@@ -6,7 +6,7 @@
 import InputForm from './inputform'
 import './index.scss'
 
-class CopyComponent extends Component {
+class SourceComponent extends Component {
   static propTpyes = {
     type: PropTypes.string,
     placement: PropTypes.any,
@@ -91,4 +91,4 @@
   }
 }
 
-export default CopyComponent
\ No newline at end of file
+export default SourceComponent
\ No newline at end of file
diff --git a/src/menu/components/share/sourcecomponent/inputform/index.jsx b/src/menu/components/share/sourcecomponent/inputform/index.jsx
index f178a9e..cb5888a 100644
--- a/src/menu/components/share/sourcecomponent/inputform/index.jsx
+++ b/src/menu/components/share/sourcecomponent/inputform/index.jsx
@@ -167,7 +167,7 @@
         {keyword === 'upload' ? <Form.Item label="涓婁紶" labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={{xs: { span: 24 }, sm: { span: 20 }}}>
           <FileUpload config={{
             initval: '',
-            suffix: type === 'video' ? '.mp4,.webm,.ogg' : '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
+            suffix: type === 'video' ? '.mp4,.webm,.ogg' : '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico',
             maxfile: 1,
             fileType: type === 'video' ? 'text' : 'picture'
           }} onChange={this.changeFile} />
diff --git a/src/menu/components/table/normal-table/columns/index.jsx b/src/menu/components/table/normal-table/columns/index.jsx
index c88a996..ee4f5ed 100644
--- a/src/menu/components/table/normal-table/columns/index.jsx
+++ b/src/menu/components/table/normal-table/columns/index.jsx
@@ -158,6 +158,7 @@
       return (
         <td style={{...style, minWidth: column.Width || 100}} className={className}>
           {column.field || (column.type === 'index' ? '$Index' : '')}
+          {column.Hide === 'true' ? <Icon style={{marginLeft: '5px', color: 'orange', fontSize: '12px'}} type="close-circle" /> : null}
           {column.marks && column.marks.length ? <Icon className="profile" type="ant-design"/> : null}
         </td>
       )
@@ -550,9 +551,9 @@
     return (
       <div className={`normal-table-columns ${config.setting.laypage} ${config.wrap.tableType} ${config.wrap.mode || ''}`} id={tableId}>
         <div className="col-control">
-          <Icon title="澶嶅埗" type="copy" onClick={this.copycolumn} />
+          <Icon title="澶嶅埗鏄剧ず鍒�" type="copy" onClick={this.copycolumn} />
           <MarkColumn columns={fields} type="line" marks={lineMarks} onSubmit={this.updateLineMarks} />
-          <Icon title="鍚屾" type="file-sync" onClick={this.syncfield} />
+          <Icon title="鍚屾瀛楁闆�" type="file-sync" onClick={this.syncfield} />
         </div>
         <DndProvider>
           <Table
diff --git a/src/menu/components/table/normal-table/index.jsx b/src/menu/components/table/normal-table/index.jsx
index 4bd4dd7..f7b5e07 100644
--- a/src/menu/components/table/normal-table/index.jsx
+++ b/src/menu/components/table/normal-table/index.jsx
@@ -221,7 +221,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -279,7 +279,6 @@
     newcard.interface = card.setting.interface || ''
     newcard.execSuccess = 'grid'
     newcard.execError = 'never'
-    newcard.errorTime = 10
     newcard.verify = null
     newcard.show = 'button'
     newcard.style = {marginRight: '15px'}
diff --git a/src/menu/components/tabs/antv-tabs/dragabletabs.jsx b/src/menu/components/tabs/antv-tabs/dragabletabs.jsx
index b2d7ef7..fbb97de 100644
--- a/src/menu/components/tabs/antv-tabs/dragabletabs.jsx
+++ b/src/menu/components/tabs/antv-tabs/dragabletabs.jsx
@@ -90,7 +90,7 @@
 
   render() {
     const { order } = this.state
-    const { children } = this.props
+    const { children, ...resProps } = this.props
     const tabs = []
     React.Children.forEach(children, c => {
       tabs.push(c)
@@ -118,7 +118,7 @@
 
     return (
       <DndProvider>
-        <Tabs renderTabBar={this.renderTabBar} {...this.props}>
+        <Tabs renderTabBar={this.renderTabBar} {...resProps}>
           {orderTabs}
         </Tabs>
       </DndProvider>
diff --git a/src/menu/components/tabs/antv-tabs/index.jsx b/src/menu/components/tabs/antv-tabs/index.jsx
index 0cb7d7d..41e0b3d 100644
--- a/src/menu/components/tabs/antv-tabs/index.jsx
+++ b/src/menu/components/tabs/antv-tabs/index.jsx
@@ -10,14 +10,15 @@
 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 zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
 const SettingComponent = asyncIconComponent(() => import('../tabsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteController = asyncIconComponent(() => import('@/menu/pastecontroller'))
-const TabLabelComponent = asyncComponent(() => import('../tablabelform'))
 const TabComponents = asyncComponent(() => import('../tabcomponents'))
 
 const { TabPane } = Tabs
@@ -35,7 +36,7 @@
     appType: sessionStorage.getItem('appType'),
     tabs: null,
     editab: null,
-    labelvisible: false
+    defaultActiveKey: ''
   }
 
   UNSAFE_componentWillMount () {
@@ -60,11 +61,13 @@
         ]
       }
       this.setState({
+        defaultActiveKey: _tabs.subtabs[0].uuid,
         tabs: _tabs
       })
       this.props.updateConfig(_tabs)
     } else {
       this.setState({
+        defaultActiveKey: window.GLOB.TabsMap.get(tabs.uuid) || '',
         tabs: fromJS(tabs).toJS()
       })
     }
@@ -130,7 +133,7 @@
   changeStyle = () => {
     const { tabs } = this.state
 
-    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin'], tabs.style)
+    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
   }
 
   getStyle = (comIds, style) => {
@@ -185,63 +188,6 @@
 
     this.setState({tabs})
     this.props.updateConfig(tabs)
-  }
-
-  tabAdd = (e) => {
-    const { tabs } = this.state
-
-    e.stopPropagation()
-
-    this.setState({
-      editab: {
-        uuid: '',
-        parentId: tabs.uuid,
-        floor: tabs.floor,
-        label: '',
-        icon: '',
-        components: []
-      },
-      labelvisible: true
-    })
-  }
-
-  editTab = (tab) => {
-    this.setState({
-      editab: tab,
-      labelvisible: true
-    })
-  }
-
-  tabLabelSubmit = () => {
-    let tabs = fromJS(this.state.tabs).toJS()
-    let editab = fromJS(this.state.editab).toJS()
-
-    this.tabLabelRef.handleConfirm().then(res => {
-      editab.label = res.label
-      editab.icon = res.icon
-      editab.hasSearch = res.hasSearch || ''
-      editab.blacklist = res.blacklist
-
-      if (editab.uuid) {
-        tabs.subtabs = tabs.subtabs.map(t => {
-          if (t.uuid === editab.uuid) {
-            return editab
-          } else {
-            return t
-          }
-        })
-      } else {
-        editab.uuid = Utils.getuuid()
-        tabs.subtabs.push(editab)
-      }
-
-      this.setState({
-        editab: null,
-        labelvisible: false,
-        tabs
-      })
-      this.props.updateConfig(tabs)
-    })
   }
 
   delTab = (tab) => {
@@ -345,18 +291,76 @@
     }
   }
 
+  getTabForms = (tab) => {
+    const { tabs } = this.state
+
+    if (!tab) {
+      tab = {
+        uuid: '',
+        parentId: tabs.uuid,
+        floor: tabs.floor,
+        label: '',
+        icon: '',
+        components: []
+      }
+    }
+
+    this.setState({
+      editab: tab
+    })
+
+    return getTabForm(tab, tabs.setting)
+  }
+
+  updateTab = (res) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let editab = fromJS(this.state.editab).toJS()
+
+    editab.label = res.label
+    editab.icon = res.icon
+    editab.hasSearch = res.hasSearch || ''
+    editab.blacklist = res.blacklist
+
+    if (editab.uuid) {
+      tabs.subtabs = tabs.subtabs.map(t => {
+        if (t.uuid === editab.uuid) {
+          return editab
+        } else {
+          return t
+        }
+      })
+    } else {
+      editab.uuid = Utils.getuuid()
+      tabs.subtabs.push(editab)
+    }
+
+    this.setState({
+      editab: null,
+      tabs
+    })
+
+    this.props.updateConfig(tabs)
+  }
+
+  onChange = (key) => {
+    const { tabs } = this.state
+    window.GLOB.TabsMap.set(tabs.uuid, key)
+  }
+
   render() {
-    const { tabs, dict, labelvisible, editab, appType } = this.state
+    const { tabs, appType, defaultActiveKey } = this.state
     let _style = resetStyle(tabs.style)
 
     return (
       <div className={'menu-tabs-edit-box ' + tabs.setting.display} style={_style} onClick={this.clickComponent} id={tabs.uuid}>
-        <DraggableTabs tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch}>
+        <DraggableTabs defaultActiveKey={defaultActiveKey} tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch} onChange={this.onChange}>
           {tabs.subtabs.map(tab => (
             <TabPane tab={
               <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                 <div className="mk-popover-control">
-                  <Icon className="edit" title="edit" type="edit" onClick={() => this.editTab(tab)} />
+                  <NormalForm title="鏍囩缂栬緫" width={600} update={this.updateTab} getForms={() => this.getTabForms(tab)}>
+                    <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+                  </NormalForm>
                   <PasteController type="tab" Tab={tab} insert={this.insert} />
                   <Icon className="close" title="delete" type="close" onClick={() => this.delTab(tab)} />
                 </div>
@@ -372,7 +376,9 @@
         </DraggableTabs>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <Icon className="plus" title="娣诲姞鏍囩" type="plus" onClick={this.tabAdd} />
+            <NormalForm title="娣诲姞鏍囩" width={600} update={this.updateTab} getForms={() => this.getTabForms()}>
+              <Icon type="plus" className="plus" title="娣诲姞鏍囩"/>
+            </NormalForm>
             <SettingComponent config={tabs} updateConfig={this.updateComponent} />
             <CopyComponent type="tabs" card={tabs}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
@@ -381,25 +387,6 @@
         } trigger="hover">
           <Icon type="tool" />
         </Popover>
-        <Modal
-          wrapClassName="popview-modal"
-          title={'鏍囩缂栬緫'}
-          visible={labelvisible}
-          width={600}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.tabLabelSubmit}
-          onCancel={() => { this.setState({ labelvisible: false }) }}
-          destroyOnClose
-        >
-          <TabLabelComponent
-            dict={dict}
-            tab={editab}
-            setting={tabs.setting}
-            inputSubmit={this.tabLabelSubmit}
-            wrappedComponentRef={(inst) => this.tabLabelRef = inst}
-          />
-        </Modal>
       </div>
     )
   }
diff --git a/src/menu/components/tabs/antv-tabs/options.jsx b/src/menu/components/tabs/antv-tabs/options.jsx
new file mode 100644
index 0000000..c18ca35
--- /dev/null
+++ b/src/menu/components/tabs/antv-tabs/options.jsx
@@ -0,0 +1,63 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (tab, setting) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const tabForm = [
+    {
+      type: 'text',
+      field: 'label',
+      label: '鍚嶇О',
+      initval: tab.label || '',
+      required: true,
+      focus: true,
+      span: 22
+    },
+    {
+      type: 'mkicon',
+      field: 'icon',
+      label: '鍥炬爣',
+      initval: tab.icon || '',
+      required: false,
+      allowClear: true,
+      span: 22
+    },
+    {
+      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: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: tab.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType,
+      span: 22
+    },
+  ]
+
+  return tabForm
+} 
\ No newline at end of file
diff --git a/src/menu/components/tabs/tabcomponents/card.jsx b/src/menu/components/tabs/tabcomponents/card.jsx
index 32d1080..bfc1471 100644
--- a/src/menu/components/tabs/tabcomponents/card.jsx
+++ b/src/menu/components/tabs/tabcomponents/card.jsx
@@ -10,6 +10,7 @@
 const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
 const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
 const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
+const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
 const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
 const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
 const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
@@ -17,6 +18,7 @@
 const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
 const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
@@ -68,8 +70,10 @@
       return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'scatter') {
       return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'form') {
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
       return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'tabs') {
       return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard} />)
     } else if (card.type === 'card' && card.subtype === 'datacard') {
@@ -90,6 +94,8 @@
       return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'code') {
       return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'balcony') {
+      return (<Balcony 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 412a698..657c5d9 100644
--- a/src/menu/components/tabs/tabcomponents/index.jsx
+++ b/src/menu/components/tabs/tabcomponents/index.jsx
@@ -92,7 +92,7 @@
 
       let name = ''
       let names = {
-        bbar: '鏌辩姸鍥�',
+        bar: '鏌辩姸鍥�',
         line: '鎶樼嚎鍥�',
         tabs: '鏍囩缁�',
         pie: '楗煎浘',
@@ -106,6 +106,7 @@
         dashboard: '浠〃鐩�',
         scatter: '鏁g偣鍥�',
         tree: '鏍戝舰鍒楄〃',
+        balcony: '娴姩鍗�',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/components/tabs/tablabelform/index.jsx b/src/menu/components/tabs/tablabelform/index.jsx
deleted file mode 100644
index 335976a..0000000
--- a/src/menu/components/tabs/tablabelform/index.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Icon, Select, Radio } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,
-    setting: PropTypes.object,
-    tab: PropTypes.object,
-    inputSubmit: PropTypes.func
-  }
-
-  state = {roleList: [], appType: sessionStorage.getItem('appType')}
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { tab, setting } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList, appType } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 12 }
-      }
-    }
-
-    return (
-      <Form {...formItemLayout}>
-        <Row gutter={24}>
-          <Col span={24}>
-            <Form.Item label="鍚嶇О">
-              {getFieldDecorator('label', {
-                initialValue: tab.label,
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['form.required.input'] + '鍚嶇О!'
-                  }
-                ]
-              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label="鍥炬爣">
-              {getFieldDecorator('icon', {
-                initialValue: tab.icon || ''
-              })(
-                <Select>
-                  <Select.Option key="empty" value=""> 鏃� </Select.Option>
-                  <Select.Option key="android" value="android"> <Icon type="android" /> </Select.Option>
-                  <Select.Option key="apple" value="apple"> <Icon type="apple" /> </Select.Option>
-                  <Select.Option key="windows" value="windows"> <Icon type="windows" /> </Select.Option>
-                  <Select.Option key="alipay" value="alipay"> <Icon type="alipay" /> </Select.Option>
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-          {appType === 'mob' && setting.position === 'top' && setting.display === 'inline-block' ? <Col span={24}>
-            <Form.Item label="鎼滅储">
-              {getFieldDecorator('hasSearch', {
-                initialValue: tab.hasSearch || 'false'
-              })(
-                <Radio.Group>
-                  <Radio value="false">鏃�</Radio>
-                  <Radio value="icon">鏈�</Radio>
-                </Radio.Group>
-              )}
-            </Form.Item>
-          </Col> : null}
-          <Col span={24}>
-            <Form.Item label="榛戝悕鍗�">
-              {getFieldDecorator('blacklist', {
-                initialValue: tab.blacklist || []
-              })(
-                <Select
-                  showSearch
-                  mode="multiple"
-                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                >
-                  {roleList.map(option =>
-                    <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                  )}
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/menu/components/tree/antd-tree/index.jsx b/src/menu/components/tree/antd-tree/index.jsx
index 18f83e7..f392778 100644
--- a/src/menu/components/tree/antd-tree/index.jsx
+++ b/src/menu/components/tree/antd-tree/index.jsx
@@ -114,7 +114,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
diff --git a/src/menu/datasource/verifycard/columnform/index.jsx b/src/menu/datasource/verifycard/columnform/index.jsx
index 791118d..4af4387 100644
--- a/src/menu/datasource/verifycard/columnform/index.jsx
+++ b/src/menu/datasource/verifycard/columnform/index.jsx
@@ -43,19 +43,6 @@
       <Form {...formItemLayout} className="verify-form" id="verifycard1">
         <Row gutter={24}>
           <Col span={6}>
-            <Form.Item label={'鍚嶇О'}>
-              {getFieldDecorator('label', {
-                initialValue: '',
-                rules: [
-                  {
-                    required: true,
-                    message: dict['form.required.input'] + '鍚嶇О!'
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" />)}
-            </Form.Item>
-          </Col>
-          <Col span={6}>
             <Form.Item label={'瀛楁'}>
               {getFieldDecorator('field', {
                 initialValue: '',
@@ -63,6 +50,19 @@
                   {
                     required: true,
                     message: dict['form.required.input'] + '瀛楁!'
+                  }
+                ]
+              })(<Input placeholder="" autoComplete="off" />)}
+            </Form.Item>
+          </Col>
+          <Col span={6}>
+            <Form.Item label={'鍚嶇О'}>
+              {getFieldDecorator('label', {
+                initialValue: '',
+                rules: [
+                  {
+                    required: true,
+                    message: dict['form.required.input'] + '鍚嶇О!'
                   }
                 ]
               })(<Input placeholder="" autoComplete="off" />)}
@@ -80,6 +80,8 @@
                 ]
               })(
                 <Select>
+                  <Select.Option value="Nvarchar(10)"> Nvarchar(10) </Select.Option>
+                  <Select.Option value="Nvarchar(20)"> Nvarchar(20) </Select.Option>
                   <Select.Option value="Nvarchar(50)"> Nvarchar(50) </Select.Option>
                   <Select.Option value="Nvarchar(100)"> Nvarchar(100) </Select.Option>
                   <Select.Option value="Nvarchar(512)"> Nvarchar(512) </Select.Option>
@@ -106,7 +108,8 @@
                   <Select.Option value="Decimal(18,16)"> Decimal(18,16) </Select.Option>
                   <Select.Option value="Decimal(18,17)"> Decimal(18,17) </Select.Option>
                   <Select.Option value="Decimal(18,18)"> Decimal(18,18) </Select.Option>
-                  {/* <Select.Option value="date"> date </Select.Option> */}
+                  <Select.Option value="date"> date </Select.Option>
+                  <Select.Option value="datetime"> datetime </Select.Option>
                 </Select>
               )}
             </Form.Item>
diff --git a/src/menu/datasource/verifycard/customscript/index.jsx b/src/menu/datasource/verifycard/customscript/index.jsx
index 27fb640..efddfaf 100644
--- a/src/menu/datasource/verifycard/customscript/index.jsx
+++ b/src/menu/datasource/verifycard/customscript/index.jsx
@@ -216,7 +216,7 @@
           </Col>
           <Col span={24} className="sqlfield">
             <Form.Item label={'鍙敤瀛楁'}>
-              id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}{window.GLOB.urlFields && window.GLOB.urlFields.length > 0 ? ', ' + window.GLOB.urlFields.join(', ') : ''}
+              id, bid, loginuid, sessionuid, userid, username, fullname, RoleID, departmentcode, organization, login_city, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}{window.GLOB.urlFields && window.GLOB.urlFields.length > 0 ? ', ' + window.GLOB.urlFields.join(', ') : ''}
             </Form.Item>
           </Col>
           <Col span={10} style={{width: '43%'}}>
diff --git a/src/menu/datasource/verifycard/index.jsx b/src/menu/datasource/verifycard/index.jsx
index 2e247f9..e98c763 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -58,6 +58,8 @@
         dataIndex: 'datatype',
         inputType: 'select',
         options: [
+          { value: 'Nvarchar(10)', text: 'Nvarchar(10)' },
+          { value: 'Nvarchar(20)', text: 'Nvarchar(20)' },
           { value: 'Nvarchar(50)', text: 'Nvarchar(50)' },
           { value: 'Nvarchar(100)', text: 'Nvarchar(100)' },
           { value: 'Nvarchar(512)', text: 'Nvarchar(512)' },
@@ -84,6 +86,8 @@
           { value: 'Decimal(18,16)', text: 'Decimal(18,16)' },
           { value: 'Decimal(18,17)', text: 'Decimal(18,17)' },
           { value: 'Decimal(18,18)', text: 'Decimal(18,18)' },
+          { value: 'date', text: 'date' },
+          { value: 'datetime', text: 'datetime' },
         ],
         editable: true,
         width: '25%',
@@ -703,8 +707,8 @@
               type="fields"
               updatefield={this.updatefields}
             />
-            <Icon type="copy" onClick={this.copyColumns} style={{position: 'absolute', cursor: 'pointer', zIndex: 1, top: '-35px', right: '0px', color: '#1890ff'}} />
-            <EditTable actions={['edit', 'move', 'copy', 'del']} type="datasourcefield" data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
+            <Icon type="copy" title="浠ラ�楀彿鎷兼帴褰㈠紡澶嶅埗瀛楁" onClick={this.copyColumns} style={{position: 'absolute', cursor: 'pointer', zIndex: 1, top: '-35px', right: '0px', color: '#1890ff'}} />
+            <EditTable actions={['edit', 'move', 'copy', 'del', 'clear']} type="datasourcefield" data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
           </TabPane>
           <TabPane tab={
             <span>
diff --git a/src/menu/datasource/verifycard/utils.jsx b/src/menu/datasource/verifycard/utils.jsx
index fa55bc3..4f48441 100644
--- a/src/menu/datasource/verifycard/utils.jsx
+++ b/src/menu/datasource/verifycard/utils.jsx
@@ -42,7 +42,7 @@
     _customScript = _customScript.replace(/@sum\$|\$sum@/ig, '')
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index b12b583..327482c 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -19,6 +19,7 @@
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
 const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
 const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
@@ -69,8 +70,10 @@
       return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'scatter') {
       return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'form') {
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
       return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'tabs') {
       return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'card' && card.subtype === 'datacard') {
diff --git a/src/menu/modalconfig/index.jsx b/src/menu/modalconfig/index.jsx
index cf6bab2..7aed763 100644
--- a/src/menu/modalconfig/index.jsx
+++ b/src/menu/modalconfig/index.jsx
@@ -16,6 +16,7 @@
 import SettingForm from '@/templates/modalconfig/settingform'
 import asyncComponent from '@/utils/asyncComponent'
 import { SearchItems } from '@/templates/modalconfig/source'
+import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
 const { Panel } = Collapse
@@ -43,7 +44,8 @@
     originConfig: null,    // 鍘熷鑿滃崟
     sqlVerifing: false,    // sql楠岃瘉
     showField: false,      // 鏄剧ず琛ㄥ崟瀛楁鍊�
-    standardform: null
+    standardform: null,
+    saving: false
   }
 
   /**
@@ -61,6 +63,10 @@
     })
   }
 
+  componentDidMount () {
+    MKEmitter.addListener('completeSave', this.completeSave)
+  }
+
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
    */
@@ -68,6 +74,7 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('completeSave', this.completeSave)
   }
 
   /**
@@ -288,13 +295,16 @@
   submitConfig = () => {
     const { config } = this.state
 
-    this.setState({originConfig: fromJS(config).toJS()})
+    this.setState({originConfig: fromJS(config).toJS(), saving: true})
     this.props.handleSave(config)
-    notification.success({
-      top: 92,
-      message: '淇濆瓨鎴愬姛銆�',
-      duration: 2
-    })
+
+    setTimeout(() => {
+      MKEmitter.emit('triggerMenuSave')
+    }, 100)
+  }
+
+  completeSave = () => {
+    this.setState({saving: false})
   }
 
   cancelConfig = () => {
@@ -400,7 +410,7 @@
   }
 
   render () {
-    const { config, dict } = this.state
+    const { config, dict, saving } = this.state
 
     return (
       <div className="modal-form-board">
@@ -425,7 +435,7 @@
             <Card title={dict['header.menu.form.configurable']} bordered={false} extra={
               <div>
                 <EditComponent dict={dict} options={['form']} config={this.state.config} refresh={(res) => this.updateConfig(res.config)}/>
-                <Button type="primary" onClick={this.submitConfig}>淇濆瓨</Button>
+                <Button type="primary" loading={saving} onClick={this.submitConfig}>淇濆瓨</Button>
                 <Button onClick={this.cancelConfig}>杩斿洖</Button>
               </div>
             } style={{ width: '100%' }}>
diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index e9e1833..fe6dd69 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -30,7 +30,8 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '鍙诞鍔ㄥ崱', width: 24 },
-  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', width: 24 },
+  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24 },
+  { type: 'menu', url: form, component: 'form', subtype: 'tabform', title: '琛ㄥ崟锛坱ab椤碉級', width: 24 },
   { 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: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
diff --git a/src/menu/pastecontroller/index.jsx b/src/menu/pastecontroller/index.jsx
index 7b3153c..2a7aebe 100644
--- a/src/menu/pastecontroller/index.jsx
+++ b/src/menu/pastecontroller/index.jsx
@@ -51,7 +51,7 @@
         }
 
         tab.components = tab.components.map(cell => {
-          cell = this.resetconfig(cell, tab, copyBtns)
+          cell = this.resetconfig(cell, tab, false, copyBtns)
           return cell
         })
       })
@@ -62,6 +62,11 @@
       })
     } else if (item.type === 'menubar') {
       item.subMenus = item.subMenus.map(cell => {
+        cell.uuid = Utils.getuuid()
+        return cell
+      })
+    } else if (item.type === 'balcony') {
+      item.elements = item.elements.map(cell => {
         cell.uuid = Utils.getuuid()
         return cell
       })
@@ -148,6 +153,17 @@
         }
         return col
       })
+    } else if (item.type === 'form') {
+      item.subcards = item.subcards.map(cell => {
+        cell.uuid = Utils.getuuid()
+
+        cell.fields = cell.fields.map(m => {
+          m.uuid = Utils.getuuid()
+  
+          return m
+        })
+        return cell
+      })
     }
 
     if (item.btnlog) {
@@ -201,20 +217,18 @@
     const { Tab } = this.props
 
     let isgroup = Tab && Tab.type === 'group' ? true : false
-    let options = ['tabs', 'datacard', 'propcard', 'mainsearch', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter']
+    let options = ['tabs', 'datacard', 'propcard', 'mainsearch', 'balcony', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter']
 
     if (sessionStorage.getItem('appType') === 'mob') {
       options.push('menubar')
     }
+
+    if (isgroup) {
+      options = options.filter(item => !['tabs', 'mainsearch', 'group'].includes(item))
+    }
+
     this.pasteFormRef.handleConfirm().then(res => {
-      if (!isgroup && !options.includes(res.copyType)) {
-        notification.warning({
-          top: 92,
-          message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�',
-          duration: 5
-        })
-        return
-      } else if (isgroup && !['datacard', 'propcard', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter'].includes(res.copyType)) {
+      if (!options.includes(res.copyType)) {
         notification.warning({
           top: 92,
           message: '閰嶇疆淇℃伅鏍煎紡閿欒锛�',
diff --git a/src/menu/picturecontroller/editform/index.jsx b/src/menu/picturecontroller/editform/index.jsx
index 934ebd7..c739488 100644
--- a/src/menu/picturecontroller/editform/index.jsx
+++ b/src/menu/picturecontroller/editform/index.jsx
@@ -82,7 +82,7 @@
               })(
                 <FileUpload config={{
                   initval: '',
-                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
+                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico',
                   maxfile: 1,
                   fileType: 'picture'
                 }} />
diff --git a/src/menu/popview/index.jsx b/src/menu/popview/index.jsx
index 55a0f13..bed4339 100644
--- a/src/menu/popview/index.jsx
+++ b/src/menu/popview/index.jsx
@@ -62,6 +62,7 @@
 
   componentDidMount () {
     MKEmitter.addListener('delButtons', this.delButtons)
+    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
     MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     this.updateCustomComponent()
@@ -79,6 +80,7 @@
       return
     }
     MKEmitter.removeListener('delButtons', this.delButtons)
+    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
     MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
   }
@@ -422,6 +424,7 @@
           config.open_edition = res.open_edition || ''
 
           this.setState({
+            config,
             oriConfig: fromJS(config).toJS()
           })
 
@@ -447,29 +450,22 @@
         if (!res) return
         
         if (res.status) {
-          this.setState({
-            menuloading: false,
-            config: {...config, components: []}
-          }, () => {
-            this.setState({
-              config: {...this.state.config, components: this.state.oriConfig.components}
-            })
-          })
           notification.success({
             top: 92,
             message: '淇濆瓨鎴愬姛',
             duration: 2
           })
         } else {
-          this.setState({
-            menuloading: false
-          })
           notification.warning({
             top: 92,
             message: res.message,
             duration: 5
           })
         }
+        this.setState({
+          menuloading: false
+        })
+        MKEmitter.emit('completeSave')
       })
     }, 300)
   }
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index 69d11e7..d1c5585 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -65,7 +65,7 @@
   }
 
   onCloseDrawer = () => {
-    let { card } = this.state
+    let card = fromJS(this.state.card).toJS()
 
     let check = false
     if (card.borderWidth === '0px') {
@@ -78,15 +78,15 @@
       check = true
     } else if (card.borderRightWidth === '0px') {
       delete card.borderRightWidth
-      delete card.borderRightWidth
+      delete card.borderRightColor
       check = true
     } else if (card.borderTopWidth === '0px') {
       delete card.borderTopWidth
-      delete card.borderTopWidth
+      delete card.borderTopColor
       check = true
     } else if (card.borderBottomWidth === '0px') {
       delete card.borderBottomWidth
-      delete card.borderBottomWidth
+      delete card.borderBottomColor
       check = true
     }
 
@@ -218,7 +218,44 @@
    * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
    */
   changeShadowColor = (val) => {
-    this.updateStyle({shadow: val})
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${val}`
+
+    this.updateStyle({shadowColor: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeShadowBlur = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${card.vShadow || '0px'} ${val || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({shadowBlur: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeHShadow = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${val || '0px'} ${card.vShadow || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({hShadow: val, boxShadow})
+  }
+
+  /**
+   * @description 淇敼闃村奖棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeVShadow = (val) => {
+    const { card } = this.state
+
+    let boxShadow = `${card.hShadow || '0px'} ${val || '0px'} ${card.shadowBlur || '0px'} ${card.shadowColor || 'transparent'}`
+
+    this.updateStyle({vShadow: val, boxShadow})
   }
 
   imgChange = (val) => {
@@ -479,6 +516,8 @@
                       <Option value="100%">100%</Option>
                       <Option value="100% 100%">100% 100%</Option>
                       <Option value="auto 100%">auto 100%</Option>
+                      <Option value="100% auto">100% auto</Option>
+                      <Option value="auto">auto</Option>
                       <Option value="contain">contain</Option>
                       <Option value="cover">cover</Option>
                     </Select>
@@ -609,7 +648,34 @@
                     label={<Icon title="闃村奖棰滆壊" type="bg-colors" />}
                     labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                   >
-                    <ColorSketch value={card.shadow || 'transparent'} onChange={this.changeShadowColor} />
+                    <ColorSketch value={card.shadowColor || 'transparent'} onChange={this.changeShadowColor} />
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="妯$硦璺濈" type="column-width" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.shadowBlur || '0px'} options={['px']} onChange={this.changeShadowBlur}/>
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="姘村钩浣嶇疆" type="arrow-right" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.hShadow || '0px'} options={['px']} onChange={this.changeHShadow}/>
+                  </Form.Item>
+                </Col>
+                <Col span={24}>
+                  <Form.Item
+                    colon={false}
+                    label={<Icon title="鍨傜洿浣嶇疆" type="arrow-down" />}
+                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
+                  >
+                    <StyleInput defaultValue={card.vShadow || '0px'} options={['px']} onChange={this.changeVShadow}/>
                   </Form.Item>
                 </Col>
               </Panel> : null}
diff --git a/src/menu/stylecontroller/styleInput/index.jsx b/src/menu/stylecontroller/styleInput/index.jsx
index eadf6ef..6d9dee9 100644
--- a/src/menu/stylecontroller/styleInput/index.jsx
+++ b/src/menu/stylecontroller/styleInput/index.jsx
@@ -106,7 +106,7 @@
     const { unit } = this.state
     let val = e.target.value
 
-    if (/\d+\.$/.test(val)) {
+    if (/\d+\.$|^-$/.test(val)) {
       this.setState({
         value: val
       })
diff --git a/src/menu/sysinterface/settingform/simplescript/index.jsx b/src/menu/sysinterface/settingform/simplescript/index.jsx
index c0d7832..5e23d8e 100644
--- a/src/menu/sysinterface/settingform/simplescript/index.jsx
+++ b/src/menu/sysinterface/settingform/simplescript/index.jsx
@@ -394,7 +394,7 @@
             </Col>
             <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id
+                bid, loginuid, sessionuid, userid, username, fullname, RoleID, departmentcode, organization, login_city, appkey, time_id
               </Form.Item>
             </Col>
             {type === 'back' ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
diff --git a/src/menu/sysinterface/settingform/utils.jsx b/src/menu/sysinterface/settingform/utils.jsx
index 3cd33d8..d568eb6 100644
--- a/src/menu/sysinterface/settingform/utils.jsx
+++ b/src/menu/sysinterface/settingform/utils.jsx
@@ -17,7 +17,7 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/mob/colorsketch/index.jsx b/src/mob/colorsketch/index.jsx
index 7eb42c9..4a92c0e 100644
--- a/src/mob/colorsketch/index.jsx
+++ b/src/mob/colorsketch/index.jsx
@@ -9,7 +9,7 @@
 const presetColors = [
   '#f5222d', '#fa541c', '#fa8c16', '#faad14', '#fadb14', '#a0d911', '#52c41a', '#13c2c2', '#1890ff', '#2f54eb', '#722ed1',
   '#eb2f96', '#595959', '#ffa39e', '#ffbb96', '#ffd591', '#ffe58f', '#fffb8f', '#eaff8f', '#b7eb8f', '#87e8de', '#91d5ff',
-  '#adc6ff', '#d3adf7', '#ffadd2', '#d9d9d9', '#434343', '#000000', '#ffffff', 'transparent'
+  '#adc6ff', '#d3adf7', '#EBE9E9', '#d9d9d9', '#434343', '#000000', '#ffffff', 'transparent'
 ]
 
 class ColorSketch extends Component {
diff --git a/src/mob/components/formdragelement/card.jsx b/src/mob/components/formdragelement/card.jsx
index 7447e32..bf36729 100644
--- a/src/mob/components/formdragelement/card.jsx
+++ b/src/mob/components/formdragelement/card.jsx
@@ -78,7 +78,7 @@
         <div className="am-input-label">{card.label}</div>
         <div className="am-input-control">
           <div style={{textAlign: 'left', position: 'relative', height, lineHeight: 1.5}}>
-            {card.initval ? card.initval : <span style={{color: '#bcbcbc'}}>璇疯緭鍏�</span> }
+            {card.initval ? card.initval : <span style={{color: '#bcbcbc'}}>{card.placeholder || ''}</span> }
             {card.count === 'true' ? <span style={{position: 'absolute', right: 0, bottom: 0}}>0/{card.fieldlength}</span> : null}
           </div>
         </div>
diff --git a/src/mob/components/formdragelement/index.scss b/src/mob/components/formdragelement/index.scss
index 28a32dc..aa3c250 100644
--- a/src/mob/components/formdragelement/index.scss
+++ b/src/mob/components/formdragelement/index.scss
@@ -10,7 +10,7 @@
   }
   .am-list-item {
     font-size: 16px;
-    padding-left: 10px;
+    // padding-left: 10px;
     position: relative;
     display: flex;
     height: 44px;
diff --git a/src/mob/components/menubar/normal-menubar/index.jsx b/src/mob/components/menubar/normal-menubar/index.jsx
index e02f32e..95c3d50 100644
--- a/src/mob/components/menubar/normal-menubar/index.jsx
+++ b/src/mob/components/menubar/normal-menubar/index.jsx
@@ -8,11 +8,12 @@
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import Utils from '@/utils/utils.js'
+import getWrapForm from './options'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 const MenuComponent = asyncComponent(() => import('./menucomponent'))
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
@@ -125,6 +126,11 @@
 
     card.subMenus = card.subMenus.map(item => {
       if (item.uuid === cell.uuid) return cell
+      if (cell.oriuuid && item.uuid === cell.oriuuid) {
+        delete cell.oriuuid
+
+        return cell
+      }
       return item
     })
 
@@ -155,7 +161,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], card.style)
   }
 
   getStyle = (comIds, style) => {
@@ -180,14 +186,12 @@
       setting: { type: 'menu', width: 6, sign: 'icon', icon: 'user', name: '瀹㈡埛', url: '', color: '#ffffff', iconFont: 20, padding: 12, background: '#1890ff', imgWidth: '' },
       style: {
         paddingTop: '15px', paddingBottom: '15px'
-      },
-      isnew: true
+      }
     }
 
     if (card.subMenus.length > 0) {
       newcard = fromJS(card.subMenus.slice(-1)[0]).toJS()
       newcard.uuid = Utils.getuuid()
-      newcard.isnew = true
     }
 
     card.subMenus.push(newcard)
@@ -216,6 +220,16 @@
     this.props.updateConfig(card)
   }
 
+  getWrapForms = () => {
+    const { card } = this.state
+
+    return getWrapForm(card.wrap)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   clickComponent = (e) => {
     if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
       e.stopPropagation()
@@ -242,12 +256,14 @@
 
     return (
       <div className="menu-menubar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
-        <NormalHeader config={card} updateComponent={this.updateComponent}/>
+        {card.wrap.title ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
             <Icon className="plus" title="娣诲姞鑿滃崟" onClick={this.addMenu} type="plus" />
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
-            <CopyComponent type="normalmenu" card={card}/>
+            <NormalForm title="鑿滃崟璁剧疆" width={800} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
+            <CopyComponent type="menubar" card={card}/>
             <PasteComponent config={card} options={['menucell']} updateConfig={this.updateComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <UserComponent config={card}/>
diff --git a/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx b/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
index b85cfd5..b09a66f 100644
--- a/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
+++ b/src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
@@ -1,19 +1,17 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Modal, Popover, Icon, Col } from 'antd'
+import { Popover, Icon, Col } from 'antd'
 
 import asyncIconComponent from '@/utils/asyncIconComponent'
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
 import Utils from '@/utils/utils.js'
-import SettingForm from './settingform'
+import getSettingForm from './options'
 import { resetStyle } from '@/utils/utils-custom.js'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
-const { confirm } = Modal
 const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 
 class MenuBoxComponent extends Component {
   static propTpyes = {
@@ -26,10 +24,7 @@
   }
 
   state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    card: null,
-    formlist: null,
-    visible: false
+    card: null
   }
 
   UNSAFE_componentWillMount () {
@@ -41,20 +36,11 @@
   }
 
   componentDidMount () {
-    const { card } = this.props
     MKEmitter.addListener('submitStyle', this.getStyle)
-
-    if (card.isnew) {
-      this.setState({
-        visible: true
-      })
-    }
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    const { cards } = this.props
-    
-    return !is(fromJS(cards.wrap), fromJS(nextProps.cards.wrap)) || !is(fromJS(this.state), fromJS(nextState))
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -93,43 +79,23 @@
     MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, _style)
   }
 
-  settingSubmit = () => {
+  getSettingForms = () => {
     const { card } = this.state
 
-    this.settingRef.handleConfirm().then(res => {
-      let _card = {...card, setting: res}
-
-      if (!card.isnew && card.setting.type === 'menu' && _card.setting.type !== 'menu') {
-        const _this = this
-        confirm({
-          content: '鑿滃崟灞炴�х敱鈥滆彍鍗曗�濆垏鎹㈣嚦鍏朵粬绫诲瀷鏃讹紝鑿滃崟灏嗚閲嶇疆锛屽嵆瑙i櫎涔嬪墠鑿滃崟鐨勭粦瀹氬叧绯伙紝纭畾淇敼鍚楋紵',
-          onOk() {
-            _card.uuid = Utils.getuuid()
-            _this.setState({ visible: false, card: _card })
-            _this.props.updateElement(_card)
-          },
-          onCancel() {}
-        })
-      } else {
-        delete _card.isnew
-        this.setState({ visible: false, card: _card })
-        this.props.updateElement(_card)
-      }
-    })
+    return getSettingForm(card.setting)
   }
 
-  cancel = () => {
+  updateSetting = (res) => {
     const { card } = this.state
+    let _card = {...card, setting: res}
 
-    if (card.isnew) {
-      let _card = fromJS(card).toJS()
-      delete _card.isnew
-
-      this.setState({ visible: false, card: _card })
-      this.props.updateElement(_card)
-    } else {
-      this.setState({ visible: false })
+    if (card.setting.type === 'menu' && _card.setting.type !== 'menu') {
+      _card.oriuuid = _card.uuid
+      _card.uuid = Utils.getuuid()
     }
+
+    this.setState({ card: _card })
+    this.props.updateElement(_card)
   }
 
   changeMenu = () => {
@@ -148,14 +114,10 @@
   }
 
   render() {
-    const { cards, offset } = this.props
-    const { card, visible, dict } = this.state
+    const { offset } = this.props
+    const { card } = this.state
 
     let _style = {...card.style}
-
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
 
     _style = resetStyle(_style)
 
@@ -163,7 +125,9 @@
       <Col span={card.setting.width || 6} offset={offset || 0}>
         <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
           <div className="mk-popover-control">
-            <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.setState({visible: true})} />
+            <NormalForm title="鑿滃崟缂栬緫" width={900} update={this.updateSetting} getForms={this.getSettingForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <CopyComponent type="menucell" card={card}/>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
@@ -191,25 +155,6 @@
             <div className="menu-name">{card.setting.name}</div>
           </div>
         </Popover>
-        <Modal
-          wrapClassName="popview-modal"
-          title={'鑿滃崟璁剧疆'}
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.settingSubmit}
-          onCancel={this.cancel}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            cards={cards}
-            setting={card.setting}
-            inputSubmit={this.settingSubmit}
-            wrappedComponentRef={(inst) => this.settingRef = inst}
-          />
-        </Modal>
       </Col>
     )
   }
diff --git a/src/mob/components/menubar/normal-menubar/menucomponent/options.jsx b/src/mob/components/menubar/normal-menubar/menucomponent/options.jsx
new file mode 100644
index 0000000..a035065
--- /dev/null
+++ b/src/mob/components/menubar/normal-menubar/menucomponent/options.jsx
@@ -0,0 +1,179 @@
+/**
+ * @description Setting琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (setting) {
+  let menulist = sessionStorage.getItem('appMenus')
+
+  if (menulist) {
+    try {
+      menulist = JSON.parse(menulist)
+    } catch {
+      menulist = []
+    }
+  } else {
+    menulist = []
+  }
+
+  const menuWrapForm = [
+    {
+      type: 'text',
+      field: 'name',
+      label: '鑿滃崟鍚嶇О',
+      initval: setting.name || '',
+      required: true
+    },
+    {
+      type: 'text',
+      field: 'MenuNo',
+      label: '鑿滃崟鍙傛暟',
+      initval: setting.MenuNo || '',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'width',
+      label: '瀹藉害',
+      initval: setting.width || 24,
+      tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
+      min: 1,
+      max: 24,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'select',
+      field: 'type',
+      label: '鑿滃崟灞炴��',
+      initval: setting.type || 'menu',
+      required: true,
+      options: [
+        {value: 'menu', label: '鑿滃崟'},
+        {value: 'linkmenu', label: '鍏宠仈鑿滃崟'},
+        {value: 'sysmenu', label: '绯荤粺椤�'},
+        {value: 'link', label: '閾炬帴'},
+      ],
+      controlFields: [
+        {field: 'sysmenu', values: ['sysmenu']},
+        {field: 'copyMenuId', values: ['menu']},
+        {field: 'linkMenuId', values: ['linkmenu']},
+        {field: 'linkurl', values: ['link']},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'sysmenu',
+      label: '绯荤粺椤甸潰',
+      initval: setting.sysmenu || '',
+      required: true,
+      options: [
+        {value: 'AIService', label: '鏅鸿兘瀹㈡湇'},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'copyMenuId',
+      label: '澶嶅埗鑿滃崟',
+      initval: setting.copyMenuId || '',
+      tooltip: '澶嶅埗鑿滃崟浠呭湪褰撳墠鑿滃崟鍒濆鍖栨椂鏈夋晥銆�',
+      required: false,
+      options: menulist
+    },
+    {
+      type: 'select',
+      field: 'linkMenuId',
+      label: '鍏宠仈鑿滃崟',
+      initval: setting.linkMenuId || '',
+      required: true,
+      options: menulist
+    },
+    {
+      type: 'textarea',
+      field: 'linkurl',
+      label: '閾炬帴',
+      initval: setting.linkurl || '',
+      required: true,
+      span: 24
+    },
+    {
+      type: 'radio',
+      field: 'sign',
+      label: '鏍囧織',
+      initval: setting.sign || 'icon',
+      required: true,
+      options: [
+        {value: 'icon', label: '鍥炬爣'},
+        {value: 'image', label: '鍥剧墖'},
+      ],
+      controlFields: [
+        {field: 'icon', values: ['icon']},
+        {field: 'iconFont', values: ['icon']},
+        {field: 'padding', values: ['icon']},
+        {field: 'color', values: ['icon']},
+        {field: 'background', values: ['icon']},
+        {field: 'url', values: ['image']},
+        {field: 'imgWidth', values: ['image']},
+      ]
+    },
+    {
+      type: 'mkicon',
+      field: 'icon',
+      label: '鍥炬爣',
+      initval: setting.icon || '',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'iconFont',
+      label: '瀛椾綋澶у皬',
+      initval: setting.iconFont || 20,
+      min: 12,
+      max: 200,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'padding',
+      label: '鍐呰竟璺�',
+      initval: setting.padding || 12,
+      min: 0,
+      max: 200,
+      precision: 0,
+      required: true
+    },
+    {
+      type: 'color',
+      field: 'color',
+      label: '瀛椾綋棰滆壊',
+      initval: setting.color || '#ffffff',
+      required: false
+    },
+    {
+      type: 'color',
+      field: 'background',
+      label: '鑳屾櫙鑹�',
+      initval: setting.background || '#1890ff',
+      required: false
+    },
+    {
+      type: 'source',
+      field: 'url',
+      label: '鍥剧墖鍦板潃',
+      initval: setting.url || '',
+      required: true
+    },
+    {
+      type: 'number',
+      field: 'imgWidth',
+      label: '鍥剧墖瀹藉害',
+      initval: setting.imgWidth || 36,
+      tooltip: '鍥剧墖瀹藉害涓庨珮搴︾浉褰擄紝浣跨敤鐨勫浘鐗囨瘮渚嬪簲涓�1:1銆�',
+      min: 10,
+      max: 500,
+      precision: 0,
+      required: false
+    },
+  ]
+
+  return menuWrapForm
+} 
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.jsx b/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.jsx
deleted file mode 100644
index b193269..0000000
--- a/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.jsx
+++ /dev/null
@@ -1,302 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Tooltip, Icon, Input, InputNumber, Select } from 'antd'
-
-import asyncComponent from '@/utils/asyncComponent'
-import './index.scss'
-
-const { TextArea } = Input
-const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
-const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
-const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    cards: PropTypes.object,     // 鍗$墖闆�
-    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    type: this.props.setting.type,
-    sign: this.props.setting.sign,
-    menulist: []
-  }
-
-  UNSAFE_componentWillMount() {
-    let menulist = sessionStorage.getItem('appMenus')
-
-    if (menulist) {
-      try {
-        menulist = JSON.parse(menulist)
-      } catch {
-        menulist = []
-      }
-    } else {
-      menulist = []
-    }
-    this.setState({menulist})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { setting } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { menulist, type, sign } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menubar-menu-card-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label="鑿滃崟鍚嶇О">
-                {getFieldDecorator('name', {
-                  initialValue: setting.name || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '鑿滃崟鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="鑿滃崟鍙傛暟">
-                {getFieldDecorator('MenuNo', {
-                  initialValue: setting.MenuNo || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '鑿滃崟鍙傛暟!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  鍗$墖瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: setting.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="鑿滃崟灞炴��">
-                {getFieldDecorator('type', {
-                  initialValue: type,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '绫诲瀷!'
-                    }
-                  ]
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({type: e.target.value})}>
-                    <Radio value="menu">鑿滃崟</Radio>
-                    <Radio value="linkmenu">鍏宠仈鑿滃崟</Radio>
-                    <Radio value="link">閾炬帴</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {type === 'menu' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="澶嶅埗鑿滃崟浠呭湪褰撳墠鑿滃崟涓嶅瓨鍦ㄦ椂鏈夋晥銆�">
-                  <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
-                  澶嶅埗鑿滃崟
-                </Tooltip>
-              }>
-                {getFieldDecorator('copyMenuId', {
-                  initialValue: setting.copyMenuId || ''
-                })(
-                  <Select allowClear>
-                    {menulist.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {type === 'linkmenu' ? <Col span={12}>
-              <Form.Item label="鍏宠仈鑿滃崟">
-                {getFieldDecorator('linkMenuId', {
-                  initialValue: setting.linkMenuId || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鍏宠仈鑿滃崟!'
-                    }
-                  ]
-                })(
-                  <Select
-                    showSearch
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {menulist.map(option =>
-                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {type === 'link' ? <Col span={24} className="textarea">
-              <Form.Item label="閾炬帴">
-                {getFieldDecorator('linkurl', {
-                  initialValue: setting.linkurl || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '閾炬帴!'
-                    }
-                  ]
-                })( <TextArea rows={2}/> )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label="鏍囧織">
-                {getFieldDecorator('sign', {
-                  initialValue: sign,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鏍囧織!'
-                    }
-                  ]
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({sign: e.target.value})}>
-                    <Radio value="icon">鍥炬爣</Radio>
-                    <Radio value="image">鍥剧墖</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {sign === 'icon' ? <Col span={12}>
-              <Form.Item label="鍥炬爣">
-                {getFieldDecorator('icon', {
-                  initialValue: setting.icon || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.select'] + '鍥炬爣!'
-                    }
-                  ]
-                })(
-                  <MkIcon />
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'icon' ? <Col span={12}>
-              <Form.Item label="瀛椾綋澶у皬">
-                {getFieldDecorator('iconFont', {
-                  initialValue: setting.iconFont || 20
-                })(
-                  <InputNumber min={12} max={200} precision={0} onPressEnter={this.handleSubmit}/>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'icon' ? <Col span={12}>
-              <Form.Item label="鍐呰竟璺�">
-                {getFieldDecorator('padding', {
-                  initialValue: setting.padding || 12
-                })(
-                  <InputNumber min={0} max={200} precision={0} onPressEnter={this.handleSubmit}/>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'icon' ? <Col span={12}>
-              <Form.Item label="瀛椾綋棰滆壊">
-                {getFieldDecorator('color', {
-                  initialValue: setting.color || '#ffffff'
-                })(
-                  <ColorSketch />
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'icon' ? <Col span={12}>
-              <Form.Item label="鑳屾櫙鑹�">
-                {getFieldDecorator('background', {
-                  initialValue: setting.background || '#1890ff'
-                })(
-                  <ColorSketch />
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'image' ? <Col span={12}>
-              <Form.Item label="鍥剧墖鍦板潃">
-                {getFieldDecorator('url', {
-                  initialValue: setting.url || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '鍥剧墖鍦板潃!'
-                    }
-                  ]
-                })(
-                  <SourceComponent type="" placement="right"/>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {sign === 'image' ? <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鍥剧墖瀹藉害涓庨珮搴︾浉褰擄紝浣跨敤鐨勫浘鐗囨瘮渚嬪簲涓�1:1銆�">
-                  <Icon type="question-circle" />
-                  鍥剧墖瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('imgWidth', {
-                  initialValue: setting.imgWidth || 36
-                })(
-                  <InputNumber min={10} max={500} precision={0} onPressEnter={this.handleSubmit}/>
-                )}
-              </Form.Item>
-            </Col> : null}
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.scss b/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.scss
deleted file mode 100644
index 91dfcf6..0000000
--- a/src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.model-menubar-menu-card-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-  .textarea {
-    .ant-form-item-label {
-      width: 16%;
-    }
-    .ant-form-item-control-wrapper {
-      width: 84%;
-    }
-  }
-  .ant-radio-wrapper {
-    margin-right: 3px;
-  }
-  .color-sketch-block {
-    margin-top: 7px;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/options.jsx b/src/mob/components/menubar/normal-menubar/options.jsx
new file mode 100644
index 0000000..c8d9f80
--- /dev/null
+++ b/src/mob/components/menubar/normal-menubar/options.jsx
@@ -0,0 +1,35 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap) {
+  const menuWrapForm = [
+    {
+      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
+    }
+  ]
+
+  return menuWrapForm
+} 
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/wrapsetting/index.jsx b/src/mob/components/menubar/normal-menubar/wrapsetting/index.jsx
deleted file mode 100644
index 81346a6..0000000
--- a/src/mob/components/menubar/normal-menubar/wrapsetting/index.jsx
+++ /dev/null
@@ -1,83 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title={config.type === 'table' ? '琛ㄦ牸璁剧疆' : '鍗$墖璁剧疆'}
-          visible={visible}
-          width={800}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            config={config}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/wrapsetting/index.scss b/src/mob/components/menubar/normal-menubar/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/mob/components/menubar/normal-menubar/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.jsx b/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.jsx
deleted file mode 100644
index e501fa1..0000000
--- a/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,155 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    roleList: [],
-  }
-
-  UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
-
-    this.setState({roleList})
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { roleList } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menubar-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label="鏍囬">
-                {getFieldDecorator('title', {
-                  initialValue: wrap.title || ''
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐢ㄤ簬缁勪欢闂寸殑鍖哄垎銆�">
-                  <Icon type="question-circle" />
-                  缁勪欢鍚嶇О
-                </Tooltip>
-              }>
-                {getFieldDecorator('name', {
-                  initialValue: wrap.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '缁勪欢鍚嶇О!'
-                    }
-                  ]
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��">
-                  <Icon type="question-circle" />
-                  瀹藉害
-                </Tooltip>
-              }>
-                {getFieldDecorator('width', {
-                  initialValue: wrap.width || 24,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['form.required.input'] + '瀹藉害!'
-                    }
-                  ]
-                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="瀵归綈鏂瑰紡">
-                {getFieldDecorator('float', {
-                  initialValue: wrap.float || 'left'
-                })(
-                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
-                    <Radio key="left" value="left"> 宸﹀榻� </Radio>
-                    <Radio key="center" value="center"> 灞呬腑 </Radio>
-                    <Radio key="right" value="right"> 鍙冲榻� </Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.scss b/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.scss
deleted file mode 100644
index 92283b8..0000000
--- a/src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menubar-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/components/navbar/normal-navbar/index.jsx b/src/mob/components/navbar/normal-navbar/index.jsx
index 3565e4a..0d38e64 100644
--- a/src/mob/components/navbar/normal-navbar/index.jsx
+++ b/src/mob/components/navbar/normal-navbar/index.jsx
@@ -118,7 +118,7 @@
   changeStyle = () => {
     const { card } = this.state
 
-    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding'], card.style)
+    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding', 'shadow'], card.style)
   }
 
   clickComponent = (e) => {
@@ -145,9 +145,6 @@
     const { card } = this.state
 
     let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
     _style.height = card.wrap.height
 
     return (
diff --git a/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx b/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx
index 2fafa7b..2c1173d 100644
--- a/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx
+++ b/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx
@@ -146,9 +146,10 @@
               {getFieldDecorator('property', {
                 initialValue: menu.property || 'menu'
               })(
-                <Radio.Group onChange={this.changeProperty} style={{whiteSpace: 'nowrap'}}>
+                <Radio.Group onChange={this.changeProperty} className="over">
                   <Radio value="menu">鑿滃崟</Radio>
                   <Radio value="linkmenu">鍏宠仈鑿滃崟</Radio>
+                  <Radio value="sysmenu">绯荤粺椤�</Radio>
                   <Radio value="link">閾炬帴</Radio>
                 </Radio.Group>
               )}
@@ -166,6 +167,21 @@
               )}
             </Form.Item>
           </Col>
+          {property === 'sysmenu' ? <Col span={12}>
+            <Form.Item label="绯荤粺椤甸潰">
+              {getFieldDecorator('sysmenu', {
+                initialValue: menu.sysmenu || '',
+                rules: [{
+                  required: true,
+                  message: '璇烽�夋嫨绯荤粺椤甸潰!'
+                }]
+              })(
+                <Select>
+                  <Select.Option value="AIService">鏅鸿兘瀹㈡湇</Select.Option>
+                </Select>
+              )}
+            </Form.Item>
+          </Col> : null}
           {property === 'link' ? <Col span={24}>
             <Form.Item label="閾炬帴鍦板潃" className="textarea">
               {getFieldDecorator('link', {
diff --git a/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss b/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss
index b94aad3..c4e38c9 100644
--- a/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss
+++ b/src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss
@@ -7,4 +7,11 @@
       width: 84%;
     }
   }
+
+  .ant-radio-group.over {
+    white-space: nowrap;
+    .ant-radio-wrapper {
+      margin-right: 0;
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx b/src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx
index b17ff7d..25a5ee3 100644
--- a/src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx
+++ b/src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx
@@ -23,7 +23,7 @@
       { title: '鑿滃崟鍚嶇О', dataIndex: 'name', key: 'name' },
       { title: '鑿滃崟鍙傛暟', dataIndex: 'MenuNo', key: 'MenuNo' },
       { title: '鑿滃崟灞炴��', dataIndex: 'property', key: 'property',  render: text => {
-        const trans = {menu: '鑿滃崟', link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟'}
+        const trans = {menu: '鑿滃崟', link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟', sysmenu: '绯荤粺椤�'}
 
         return trans[text]
       }},
@@ -129,7 +129,7 @@
       if (editMenu.MenuID && editMenu.property === 'menu' && _menu.property !== 'menu') {
         const _this = this
         confirm({
-          content: '鑿滃崟灞炴�х敱鈥滆彍鍗曗�濆垏鎹㈣嚦鍏朵粬绫诲瀷鏃讹紝鑿滃崟灏嗚閲嶇疆锛屽嵆瑙i櫎涔嬪墠鑿滃崟鐨勭粦瀹氬叧绯伙紝纭畾淇敼鍚楋紵',
+          content: '鑿滃崟灏嗚閲嶇疆锛岀‘瀹氫慨鏀瑰悧锛�',
           onOk() {
             _data = _data.map(item => {
               if (item.MenuID === _menu.MenuID) {
@@ -177,7 +177,7 @@
         <Modal
           title="缂栬緫"
           visible={visible}
-          width={750}
+          width={900}
           maskClosable={false}
           onOk={this.menuSubmit}
           onCancel={() => { this.setState({ visible: false }) }}
diff --git a/src/mob/components/tabs/antv-tabs/dragabletabs.jsx b/src/mob/components/tabs/antv-tabs/dragabletabs.jsx
new file mode 100644
index 0000000..fbb97de
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/dragabletabs.jsx
@@ -0,0 +1,129 @@
+import React, { Component } from 'react'
+import { Tabs } from 'antd'
+import { is, fromJS } from 'immutable'
+import { DndProvider, DragSource, DropTarget } from 'react-dnd'
+
+// Drag & Drop node
+class TabNode extends Component {
+  render() {
+    const { connectDragSource, connectDropTarget, children } = this.props
+
+    return connectDragSource(connectDropTarget(children))
+  }
+}
+
+const cardTarget = {
+  drop(props, monitor) {
+    const dragKey = monitor.getItem().index
+    const hoverKey = props.index
+
+    if (dragKey === hoverKey) {
+      return;
+    }
+
+    props.moveTabNode(dragKey, hoverKey)
+    monitor.getItem().index = hoverKey
+  }
+}
+
+const cardSource = {
+  beginDrag(props) {
+    return {
+      id: props.id,
+      index: props.index,
+    }
+  }
+}
+
+const WrapTabNode = DropTarget('DND_NODE', cardTarget, connect => ({
+  connectDropTarget: connect.dropTarget(),
+}))(
+  DragSource('DND_NODE', cardSource, (connect, monitor) => ({
+    connectDragSource: connect.dragSource(),
+    isDragging: monitor.isDragging(),
+  }))(TabNode)
+)
+
+class DraggableTabs extends Component {
+  state = {
+    order: []
+  }
+
+  moveTabNode = (dragKey, hoverKey) => {
+    const newOrder = this.state.order.slice()
+    const { children } = this.props
+
+    React.Children.forEach(children, c => {
+      if (newOrder.indexOf(c.key) === -1) {
+        newOrder.push(c.key)
+      }
+    })
+
+    const dragIndex = newOrder.indexOf(dragKey)
+    const hoverIndex = newOrder.indexOf(hoverKey)
+
+    newOrder.splice(dragIndex, 1)
+    newOrder.splice(hoverIndex, 0, dragKey)
+    
+    this.setState({
+      order: newOrder
+    })
+    this.props.tabsMove(newOrder)
+  }
+
+  renderTabBar = (props, DefaultTabBar) => (
+    <DefaultTabBar {...props}>
+      {node => (
+        <WrapTabNode key={node.key} index={node.key} moveTabNode={this.moveTabNode}>
+          {node}
+        </WrapTabNode>
+      )}
+    </DefaultTabBar>
+  )
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) ||
+      !is(fromJS(nextProps.children), fromJS(this.props.children)) ||
+      nextProps.tabPosition !== this.props.tabPosition ||
+      nextProps.type !== this.props.type
+  }
+
+  render() {
+    const { order } = this.state
+    const { children, ...resProps } = this.props
+    const tabs = []
+    React.Children.forEach(children, c => {
+      tabs.push(c)
+    })
+
+    const orderTabs = tabs.slice().sort((a, b) => {
+      const orderA = order.indexOf(a.key)
+      const orderB = order.indexOf(b.key)
+
+      if (orderA !== -1 && orderB !== -1) {
+        return orderA - orderB
+      }
+      if (orderA !== -1) {
+        return -1
+      }
+      if (orderB !== -1) {
+        return 1
+      }
+
+      const ia = tabs.indexOf(a)
+      const ib = tabs.indexOf(b)
+
+      return ia - ib
+    })
+
+    return (
+      <DndProvider>
+        <Tabs renderTabBar={this.renderTabBar} {...resProps}>
+          {orderTabs}
+        </Tabs>
+      </DndProvider>
+    )
+  }
+}
+
+export default DraggableTabs
\ No newline at end of file
diff --git a/src/mob/components/tabs/antv-tabs/index.jsx b/src/mob/components/tabs/antv-tabs/index.jsx
new file mode 100644
index 0000000..0c7d839
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/index.jsx
@@ -0,0 +1,419 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Tabs, Icon, Popover, Modal } from 'antd'
+
+import MKEmitter from '@/utils/events.js'
+import asyncComponent from '@/utils/asyncComponent'
+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 from './options'
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import './index.scss'
+
+const SettingComponent = asyncIconComponent(() => import('@/menu/components/tabs/tabsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteController = asyncIconComponent(() => import('@/menu/pastecontroller'))
+// const TabLabelComponent = asyncComponent(() => import('@/menu/components/tabs/tablabelform'))
+const TabComponents = asyncComponent(() => import('../tabcomponents'))
+
+const { TabPane } = Tabs
+const { confirm } = Modal
+
+class antvTabs extends Component {
+  static propTpyes = {
+    tabs: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    appType: sessionStorage.getItem('appType'),
+    tabs: null,
+    editab: null,
+    defaultActiveKey: ''
+  }
+
+  UNSAFE_componentWillMount () {
+    const { tabs } = this.props
+
+    if (tabs.isNew) {
+      let _tabs = {
+        uuid: tabs.uuid,
+        type: tabs.type,
+        floor: tabs.floor,
+        tabId: tabs.tabId || '',
+        parentId: tabs.parentId || '',
+        subtype: tabs.subtype,
+        width: 24,
+        name: tabs.name,
+        setting: {width: 24, position: 'top', tabStyle: 'line', name: tabs.name},
+        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
+        subtabs: [
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 1', icon: '', components: [] },
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 2', icon: '', components: [] },
+          { uuid: Utils.getuuid(), parentId: tabs.uuid, floor: tabs.floor, label: 'Tab 3', icon: '', components: [] }
+        ]
+      }
+
+      if (this.state.appType === 'mob') {
+        _tabs.setting.display = 'flex'
+      }
+      
+      this.setState({
+        defaultActiveKey: _tabs.subtabs[0].uuid,
+        tabs: _tabs
+      })
+      this.props.updateConfig(_tabs)
+    } else {
+      this.setState({
+        defaultActiveKey: window.GLOB.TabsMap.get(tabs.uuid) || '',
+        tabs: fromJS(tabs).toJS()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+    MKEmitter.addListener('submitSearch', this.getSearch)
+    MKEmitter.addListener('tabsChange', this.handleTabsChange)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+    MKEmitter.removeListener('submitSearch', this.getSearch)
+    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { tabs } = this.state
+
+    if (tabs.subtabs.findIndex(tab => tab.uuid === parentId) === -1) return
+
+    let _tabs = fromJS(tabs).toJS()
+    let _tabs_ = fromJS(tabs).toJS()
+
+    let components = []
+    _tabs.subtabs.forEach(tab => {
+      if (tab.uuid === parentId) {
+        components = tab.components.map(item => {
+          if (keys.includes(item.uuid)) {
+            item.style = {...item.style, ...style}
+          }
+          return item
+        })
+        tab.components = []
+      }
+    })
+
+    _tabs_.subtabs = _tabs_.subtabs.map(tab => {
+      if (tab.uuid === parentId) {
+        tab.components = components
+      }
+      return tab
+    })
+
+    this.setState({tabs: _tabs}, () => {
+      this.updateComponent(_tabs_)
+    })
+  }
+
+  changeStyle = () => {
+    const { tabs } = this.state
+
+    MKEmitter.emit('changeStyle', [tabs.uuid], ['background', 'border', 'padding', 'margin', 'shadow'], tabs.style)
+  }
+
+  changeTabStyle = () => {
+    const { tabs } = this.state
+
+    MKEmitter.emit('changeStyle', [tabs.uuid, 'tab'], ['font', 'padding'], tabs.tabStyle || {})
+  }
+
+  getStyle = (comIds, style) => {
+    const { tabs } = this.state
+
+    if (comIds[0] !== tabs.uuid) return
+
+    if (comIds.length === 1) {
+      let _card = {...tabs, style}
+
+      this.setState({
+        tabs: _card
+      })
+      
+      this.props.updateConfig(_card)
+    } else if (comIds[1] === 'tab') {
+      let _card = {...tabs, tabStyle: style}
+
+      this.setState({
+        tabs: _card
+      })
+      
+      this.props.updateConfig(_card)
+    }
+  }
+
+  handleTabsChange = (parentId) => {
+    const { tabs } = this.state
+
+    if (parentId === tabs.parentId) {
+      MKEmitter.emit('tabsChange', tabs.uuid)
+    }
+  }
+
+  updateComponent = (component) => {
+    const { tabs } = this.state
+
+    if (!is(fromJS(tabs.setting), fromJS(component.setting)) || !is(fromJS(tabs.style), fromJS(component.style))) {
+      // 娉ㄥ唽浜嬩欢-鏍囩鍙樺寲锛岄�氱煡鏍囩鍐呭厓绱�
+      MKEmitter.emit('tabsChange', tabs.uuid)
+    }
+
+    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: '纭畾鍒犻櫎鏍囩锛�',
+      content: '',
+      onOk() {
+        _this.setState({tabs})
+        _this.props.updateConfig(tabs)
+
+        if (uuids.length === 0) return
+        
+        MKEmitter.emit('delButtons', uuids)
+      },
+      onCancel() {}
+    })
+  }
+
+  moveSwitch = (order) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let subtab = {}
+    tabs.subtabs.forEach(item => {
+      subtab[item.uuid] = item
+    })
+
+    tabs.subtabs = []
+
+    order.forEach(item => {
+      if (subtab[item]) {
+        tabs.subtabs.push(subtab[item])
+      }
+    })
+
+    this.setState({tabs})
+    this.props.updateConfig(tabs)
+  }
+
+  insert = (item, tab) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+
+    tabs.subtabs.forEach(stab => {
+      if (stab.uuid === tab.uuid) {
+        stab.components.push(item)
+      }
+    })
+
+    this.setState({tabs})
+    this.props.updateConfig(tabs)
+  }
+
+  getSearch = (config) => {
+    const { tabs } = this.state
+
+    if (tabs.uuid !== config.uuid) return
+
+    let _tabs = fromJS(tabs).toJS()
+
+    _tabs.subtabs = _tabs.subtabs.map(t => {
+      if (t.uuid === config.tabId) {
+        t.search = config.search
+      }
+      return t
+    })
+
+    this.setState({
+      tabs: _tabs
+    })
+    this.props.updateConfig(_tabs)
+  }
+
+  setSearch = (tab) => {
+    const { tabs } = this.state
+    let card = {
+      uuid: tabs.uuid,
+      tabId: tab.uuid,
+      search: tab.search
+    }
+
+    if (!card.search) {
+      card.search = {
+        floor: 1,
+        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
+        groups: [],
+        fields: []
+      }
+    }
+    MKEmitter.emit('changeSearch', card)
+  }
+
+  getTabForms = (tab) => {
+    const { tabs } = this.state
+
+    if (!tab) {
+      tab = {
+        uuid: '',
+        parentId: tabs.uuid,
+        floor: tabs.floor,
+        label: '',
+        icon: '',
+        components: []
+      }
+    }
+
+    this.setState({
+      editab: tab
+    })
+
+    return getTabForm(tab, tabs.setting)
+  }
+
+  updateTab = (res) => {
+    let tabs = fromJS(this.state.tabs).toJS()
+    let editab = fromJS(this.state.editab).toJS()
+
+    editab.label = res.label
+    editab.icon = res.icon
+    editab.hasSearch = res.hasSearch || ''
+    editab.blacklist = res.blacklist
+
+    if (editab.uuid) {
+      tabs.subtabs = tabs.subtabs.map(t => {
+        if (t.uuid === editab.uuid) {
+          return editab
+        } else {
+          return t
+        }
+      })
+    } else {
+      editab.uuid = Utils.getuuid()
+      tabs.subtabs.push(editab)
+    }
+
+    this.setState({
+      editab: null,
+      tabs
+    })
+
+    this.props.updateConfig(tabs)
+  }
+
+  onChange = (key) => {
+    const { tabs } = this.state
+    window.GLOB.TabsMap.set(tabs.uuid, key)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.tabs)
+    }
+  }
+
+  render() {
+    const { tabs, appType, defaultActiveKey } = this.state
+    let _style = resetStyle(tabs.style)
+    let _tabStyle = resetStyle(tabs.tabStyle)
+
+    return (
+      <div className={'mob-tabs-edit-box ' + tabs.setting.display} style={_style} onClick={this.clickComponent} id={tabs.uuid}>
+        <DraggableTabs defaultActiveKey={defaultActiveKey} tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch} onChange={this.onChange}>
+          {tabs.subtabs.map(tab => (
+            <TabPane tab={
+              <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+                <div className="mk-popover-control">
+                  <NormalForm title="鏍囩缂栬緫" width={600} update={this.updateTab} getForms={() => this.getTabForms(tab)}>
+                    <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+                  </NormalForm>
+                  <PasteController type="tab" Tab={tab} insert={this.insert} />
+                  <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeTabStyle} type="font-colors" />
+                  <Icon className="close" title="delete" type="close" onClick={() => this.delTab(tab)} />
+                </div>
+              } trigger="hover">
+                <span style={_tabStyle}>{tab.icon ? <Icon type={tab.icon} /> : null}{tab.label}</span>
+              </Popover>
+            } key={tab.uuid}>
+              {appType === 'mob' && tabs.setting.position === 'top' && tabs.setting.display === 'inline-block' && tab.hasSearch === 'icon' ?
+                <Icon className="search-icon" onDoubleClick={() => this.setSearch(tab)} type="search" /> : null}
+              <TabComponents config={tab} handleList={this.updateTabComponent} deleteCard={this.deleteCard} />
+            </TabPane>
+          ))}
+        </DraggableTabs>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <NormalForm title="娣诲姞鏍囩" width={600} update={this.updateTab} getForms={() => this.getTabForms()}>
+              <Icon type="plus" className="plus" title="娣诲姞鏍囩"/>
+            </NormalForm>
+            <SettingComponent config={tabs} updateConfig={this.updateComponent} />
+            <CopyComponent type="tabs" card={tabs}/>
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+      </div>
+    )
+  }
+}
+
+export default antvTabs
\ No newline at end of file
diff --git a/src/mob/components/tabs/antv-tabs/index.scss b/src/mob/components/tabs/antv-tabs/index.scss
new file mode 100644
index 0000000..b38d227
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/index.scss
@@ -0,0 +1,130 @@
+.mob-tabs-edit-box {
+  position: relative;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+
+  >.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);
+  }
+
+  .ant-tabs-bar.ant-tabs-top-bar {
+    margin-bottom: 0;
+  }
+  .ant-tabs.ant-tabs-left, .ant-tabs.ant-tabs-bottom {
+    .tab-shell-inner {
+      padding-top: 25px;
+    }
+  }
+
+  .ant-tabs-tabpane-active {
+    min-height: 200px;
+  }
+
+  .ant-tabs .ant-tabs-top-bar > .ant-tabs-nav-container {
+    >.ant-tabs-tab-next:not(.ant-tabs-tab-arrow-show) + .ant-tabs-nav-wrap > .ant-tabs-nav-scroll > .ant-tabs-nav {
+      width: 100%;
+      > div > .ant-tabs-tab-disabled {
+        float: right;
+      }
+    }
+  }
+  
+  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
+    text-align: right;
+    > span {
+      display: inline-block;
+      padding: 8px 24px;
+    }
+  }
+  .ant-tabs .ant-tabs-right-bar .ant-tabs-tab {
+    text-align: left;
+    > span {
+      display: inline-block;
+      padding: 8px 24px;
+    }
+  }
+  .ant-tabs-tab {
+    padding: 0px!important;
+    text-align: center;
+    > span {
+      display: inline-block;
+      padding: 12px 16px;
+    }
+  }
+  .ant-tabs-tab.ant-tabs-tab-active {
+    > span {
+      color: #1890ff!important;
+    }
+  }
+  .ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-ink-bar {
+    top: 0px;
+  }
+  .ant-tabs-card {
+    .ant-tabs-left-bar, .ant-tabs-right-bar {
+      .ant-tabs-tab {
+        > span {
+          padding: 0px 16px;
+        }
+      }
+      .ant-tabs-tab-active {
+        padding-left: 0px!important;
+        padding-right: 0px!important;
+      }
+    }
+    
+    .ant-tabs-card-bar {
+      .ant-tabs-tab {
+        > span {
+          display: inline-block;
+          padding: 0px 16px;
+        }
+      }
+    }
+  }
+  .search-icon {
+    position: absolute;
+    top: 10px;
+    right: 40px;
+    font-size: 18px;
+    cursor: pointer;
+    padding: 3px;
+  }
+}
+.mob-tabs-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
+
+.mob-shell {
+  .mob-tabs-edit-box.flex {
+    >.ant-tabs.ant-tabs-top, >.ant-tabs.ant-tabs-bottom {
+      >.ant-tabs-bar {
+        >.ant-tabs-nav-container {
+          >.ant-tabs-nav-wrap {
+            >.ant-tabs-nav-scroll {
+              >.ant-tabs-nav {
+                display: block;
+                >div {
+                  display: flex;
+                  >.ant-tabs-tab {
+                    flex: 1;
+                  }
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/mob/components/tabs/antv-tabs/options.jsx b/src/mob/components/tabs/antv-tabs/options.jsx
new file mode 100644
index 0000000..c18ca35
--- /dev/null
+++ b/src/mob/components/tabs/antv-tabs/options.jsx
@@ -0,0 +1,63 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (tab, setting) {
+  let appType = sessionStorage.getItem('appType')
+  let roleList = sessionStorage.getItem('sysRoles')
+
+  if (roleList) {
+    try {
+      roleList = JSON.parse(roleList)
+    } catch {
+      roleList = []
+    }
+  } else {
+    roleList = []
+  }
+
+  const tabForm = [
+    {
+      type: 'text',
+      field: 'label',
+      label: '鍚嶇О',
+      initval: tab.label || '',
+      required: true,
+      focus: true,
+      span: 22
+    },
+    {
+      type: 'mkicon',
+      field: 'icon',
+      label: '鍥炬爣',
+      initval: tab.icon || '',
+      required: false,
+      allowClear: true,
+      span: 22
+    },
+    {
+      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: 'multiselect',
+      field: 'blacklist',
+      label: '榛戝悕鍗�',
+      initval: tab.blacklist || [],
+      required: false,
+      options: roleList,
+      forbid: !!appType,
+      span: 22
+    },
+  ]
+
+  return tabForm
+} 
\ No newline at end of file
diff --git a/src/mob/components/tabs/tabcomponents/card.jsx b/src/mob/components/tabs/tabcomponents/card.jsx
new file mode 100644
index 0000000..96b9d8d
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/card.jsx
@@ -0,0 +1,111 @@
+import React from 'react'
+import { useDrag, useDrop } from 'react-dnd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
+const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
+const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
+const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
+const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
+const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
+const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
+const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
+const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
+const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
+const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
+const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
+const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
+const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
+const NormalMenuBar = asyncComponent(() => import('@/mob/components/menubar/normal-menubar'))
+const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
+const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
+
+const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
+  const originalIndex = findCard(id).index
+  const [{ isDragging }, drag] = useDrag({
+    item: { type: 'menu', id, originalIndex, floor: card.floor },
+    collect: monitor => ({
+      isDragging: monitor.isDragging(),
+    }),
+  })
+  const [, drop] = useDrop({
+    accept: 'menu',
+    canDrop: () => true,
+    drop: (item) => {
+      const { id: draggedId, originalIndex, floor } = item
+      if (originalIndex === undefined) {
+        item.dropTargetId = id
+      } else if (draggedId && floor === card.floor) {
+        if (draggedId === id) return
+        const { index: originIndex } = findCard(draggedId)
+
+        if (originIndex === -1) return
+
+        const { index: overIndex } = findCard(id)
+
+        moveCard(draggedId, overIndex)
+      }
+    }
+  })
+
+  let style = { opacity: 1}
+  if (isDragging) {
+    style = { opacity: 0.3}
+  }
+
+  const getCardComponent = () => {
+    if (card.type === 'bar' || card.type === 'line') {
+      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
+    } else if (card.type === 'search') {
+      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'pie') {
+      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'dashboard') {
+      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'tree') {
+      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'scatter') {
+      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
+      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'tabs') {
+      return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard} />)
+    } else if (card.type === 'card' && card.subtype === 'datacard') {
+      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 === 'carousel' && card.subtype === 'datacard') {
+      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') {
+      return (<TableCard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'table' && card.subtype === 'normaltable') {
+      return (<NormalTable card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'group' && card.subtype === 'normalgroup') {
+      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'editor') {
+      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'menubar') {
+      return (<NormalMenuBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'balcony') {
+      return (<Balcony card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    }
+  }
+
+  return (
+    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
+      {getCardComponent()}
+    </div>
+  )
+}
+export default Card
diff --git a/src/mob/components/tabs/tabcomponents/index.jsx b/src/mob/components/tabs/tabcomponents/index.jsx
new file mode 100644
index 0000000..7bac2ee
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/index.jsx
@@ -0,0 +1,173 @@
+import React, { useState } from 'react'
+import { useDrop } from 'react-dnd'
+import { is, fromJS } from 'immutable'
+import update from 'immutability-helper'
+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'
+
+const { confirm } = Modal
+
+const Container = ({ config, handleList }) => {
+  const [cards, setCards] = useState(config.components)
+  const moveCard = (id, atIndex) => {
+    const { card, index } = findCard(id)
+    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
+    handleList({...config, components: _cards})
+  }
+
+  if (!is(fromJS(cards), fromJS(config.components))) {
+    setCards(config.components)
+  }
+  
+  const findCard = id => {
+    const card = cards.filter(c => `${c.uuid}` === id)[0]
+    return {
+      card,
+      index: cards.indexOf(card),
+    }
+  }
+
+  const updateConfig = (element) => {
+    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
+  }
+
+  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
+        }
+      })
+    }
+
+    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() {}
+    })
+  }
+
+  const [, drop] = useDrop({
+    accept: 'menu',
+    drop(item) {
+      if (item.hasOwnProperty('originalIndex') || item.added) {
+        return
+      }
+      item.added = true
+
+      if (item.component === 'search') { // 鎼滅储缁勪欢涓嶅彲閲嶅娣诲姞
+        if (cards.filter(card => card.type === 'search').length > 0) {
+          notification.warning({
+            top: 92,
+            message: '鎼滅储鏉′欢涓嶅彲閲嶅娣诲姞锛�',
+            duration: 5
+          })
+          return
+        }
+      } else if (item.component === 'tabs' && config.floor === 3) {
+        notification.warning({
+          top: 92,
+          message: '鏍囩椤垫渶澶氫负涓夐噸缁撴瀯锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let name = ''
+      let names = {
+        bar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+        tabs: '鏍囩缁�',
+        pie: '楗煎浘',
+        search: '鎼滅储',
+        table: '琛ㄦ牸',
+        group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
+        carousel: '杞挱',
+        dashboard: '浠〃鐩�',
+        form: '琛ㄥ崟',
+        card: '鍗$墖',
+        navbar: '瀵艰埅鏍�',
+        menubar: '鑿滃崟鏍�',
+        balcony: '娴姩鍗�',
+        login: '鐧诲綍'
+      }
+      let i = 1
+      
+      while (!name && names[item.component]) {
+        let _name = names[item.component] + i
+        if (config.components.filter(com => com.name === _name).length === 0) {
+          name = _name
+        }
+        i++
+      }
+      
+      let newcard = {
+        uuid: Utils.getuuid(),
+        tabId: config.uuid,
+        parentId: config.parentId,
+        type: item.component,
+        subtype: item.subtype,
+        config: item.config,
+        width: item.width || 24,
+        dataName: Utils.getdataName(),
+        name: name,
+        floor: config.floor ? (config.floor + 1) : 2, // 缁勪欢鐨勫眰绾�
+        isNew: true                                   // 鏂版坊鍔犳爣蹇楋紝鐢ㄤ簬鍒濆鍖�
+      }
+      
+      let targetId = ''
+
+      if (item.dropTargetId) {
+        targetId = item.dropTargetId
+        delete item.dropTargetId
+      } else if (cards.length > 0) {
+        targetId = cards.slice(-1)[0].uuid
+      }
+
+      const { index: overIndex } = findCard(`${targetId}`)
+      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
+
+      handleList({...config, components: _cards})
+    }
+  })
+
+  return (
+    <div ref={drop} className="ant-row tab-shell-inner">
+      {cards.map(card => (
+        <Card
+          id={card.uuid}
+          key={card.uuid}
+          config={config}
+          card={card}
+          moveCard={moveCard}
+          delCard={deleteCard}
+          findCard={findCard}
+          updateConfig={updateConfig}
+        />
+      ))}
+      {cards.length === 0 ?
+        <Empty description="璇锋坊鍔犵粍浠�" /> : null
+      }
+    </div>
+  )
+}
+export default Container
diff --git a/src/mob/components/tabs/tabcomponents/index.scss b/src/mob/components/tabs/tabcomponents/index.scss
new file mode 100644
index 0000000..f21f784
--- /dev/null
+++ b/src/mob/components/tabs/tabcomponents/index.scss
@@ -0,0 +1,14 @@
+.tab-shell-inner {
+  margin: 0px;
+
+  .anticon {
+    cursor: unset;
+  }
+
+  .mk-component-card {
+    position: relative;
+  }
+  >.ant-empty {
+    padding: 60px 0px 70px;
+  }
+}
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/index.jsx b/src/mob/components/topbar/normal-navbar/index.jsx
index db75aed..f92afda 100644
--- a/src/mob/components/topbar/normal-navbar/index.jsx
+++ b/src/mob/components/topbar/normal-navbar/index.jsx
@@ -2,15 +2,16 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover } from 'antd'
+import { ExpandOutlined, ReloadOutlined } from '@ant-design/icons'
 
 import asyncIconComponent from '@/utils/asyncIconComponent'
-
+import getWrapForm from './options'
 import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
-const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
+const NormalForm = asyncIconComponent(() => import('@/components/normalform'))
 
 class NormalNavbar extends Component {
   static propTpyes = {
@@ -36,7 +37,7 @@
         width: 24,
         subtype: card.subtype,
         wrap: { type: 'navbar', height: 50, title: 'NavBar', back: 'true', search: 'false', logout: 'false' },
-        style: {borderBottomColor: '#bcbcbc', borderBottomWidth: '1px', paddingLeft: '10px', paddingRight: '10px', lineHeight: '2.8', fontSize: '18px' },
+        style: {boxShadow: '0 0 3px #D9D9D9', shadowColor: '#D9D9D9', shadowBlur: '3px', paddingLeft: '10px', paddingRight: '10px', lineHeight: '2.8', fontSize: '18px' },
       }
 
       if (card.config) {
@@ -155,19 +156,37 @@
     MKEmitter.emit('changeSearch', card)
   }
 
+  getWrapForms = () => {
+    const { wrap } = this.state.card
+
+    return getWrapForm(wrap)
+  }
+
+  updateWrap = (res) => {
+    this.updateComponent({...this.state.card, wrap: res})
+  }
+
   render() {
     const { card } = this.state
 
-    let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
+    let right = null
+    if (card.wrap.logout === 'true') {
+      right = <Icon type="logout" />
+    }
+    if (card.wrap.scan === 'true') {
+      right = !right ? <ExpandOutlined /> : <Icon type="ellipsis" />
+    }
+    if (card.wrap.refresh === 'true') {
+      right = !right ? <ReloadOutlined /> : <Icon type="ellipsis" />
     }
 
     return (
-      <div className="normal-topbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+      <div className="normal-topbar-edit-box" style={card.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">
-            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <NormalForm title="瀵艰埅鏍忚缃�" width={750} update={this.updateWrap} getForms={this.getWrapForms}>
+              <Icon type="edit" style={{color: '#1890ff'}} title="缂栬緫"/>
+            </NormalForm>
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
           </div>
@@ -184,7 +203,7 @@
           }
           <div className="am-navbar-right">
             {card.wrap.search === 'true' ? <Icon type="search" onDoubleClick={this.setSearch}/> : null}
-            {card.wrap.logout === 'true' ? <Icon type="ellipsis" /> : null}
+            {right}
           </div>
         </div>
       </div>
diff --git a/src/mob/components/topbar/normal-navbar/index.scss b/src/mob/components/topbar/normal-navbar/index.scss
index c7cd342..fc14075 100644
--- a/src/mob/components/topbar/normal-navbar/index.scss
+++ b/src/mob/components/topbar/normal-navbar/index.scss
@@ -28,11 +28,14 @@
     font-style: inherit;
     font-weight: inherit;
     .am-navbar-left {
-      width: 30px;
+      min-width: 10px;
       text-align: left;
       color: #1890ff;
       font-size: 20px;
       line-height: 50px;
+      .anticon-left {
+        margin: 0 8px 0 5px;
+      }
     }
     .am-navbar-title {
       text-align: center;
@@ -61,7 +64,7 @@
     .am-navbar-right {
       text-align: right;
       color: #1890ff;
-      min-width: 30px;
+      min-width: 10px;
       font-size: 20px;
       line-height: 50px;
       .anticon-search {
@@ -69,6 +72,9 @@
         padding: 5px;
         cursor: pointer;
       }
+      >.anticon:not(.anticon-search) {
+        margin: 0px 5px;
+      }
     }
   }
 }
diff --git a/src/mob/components/topbar/normal-navbar/options.jsx b/src/mob/components/topbar/normal-navbar/options.jsx
new file mode 100644
index 0000000..d32a47c
--- /dev/null
+++ b/src/mob/components/topbar/normal-navbar/options.jsx
@@ -0,0 +1,112 @@
+/**
+ * @description Wrap琛ㄥ崟閰嶇疆淇℃伅
+ */
+export default function (wrap) {
+  let menulist = sessionStorage.getItem('appMenus')
+
+  if (menulist) {
+    try {
+      menulist = JSON.parse(menulist)
+    } catch {
+      menulist = []
+    }
+  } else {
+    menulist = []
+  }
+
+  const topbarWrapForm = [
+    {
+      type: 'radio',
+      field: 'type',
+      label: '绫诲瀷',
+      initval: wrap.type || 'navbar',
+      required: false,
+      options: [
+        {value: 'navbar', label: '瀵艰埅鏍�'},
+        {value: 'search', label: '鎼滅储鏍�'},
+      ],
+      controlFields: [
+        {field: 'search', values: ['navbar']},
+      ]
+    },
+    {
+      type: 'text',
+      field: 'title',
+      label: '鏍囬',
+      initval: wrap.title || '',
+      tooltip: '浣跨敤鎼滅储鏍忔椂锛屾爣棰樼敤浜庢悳绱㈡潯浠堕殣钘忔椂鏄剧ず銆�',
+      required: false
+    },
+    {
+      type: 'radio',
+      field: 'back',
+      label: '杩斿洖',
+      initval: wrap.back || 'true',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'search',
+      label: '鎼滅储',
+      initval: wrap.search || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'logout',
+      label: '閫�鍑�',
+      initval: wrap.logout || 'false',
+      tooltip: '鐐瑰嚮閫�鍑烘椂锛岃繑鍥炵涓�涓〉闈€��',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'scan',
+      label: '鎵竴鎵�',
+      initval: wrap.scan || 'false',
+      tooltip: '绯荤粺鍔熻兘锛�1銆佹壂鐮佺櫥褰曪紝浜岀淮鐮佷俊鎭互鈥渓ogin鈥濆紑澶达紝2銆侀〉闈㈣烦杞紝浜岀淮鐮佷俊鎭互鈥渦rl鈥濆紑澶�',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ],
+      controlFields: [
+        {field: 'linkmenu', values: ['true']},
+      ]
+    },
+    {
+      type: 'radio',
+      field: 'refresh',
+      label: '鍒锋柊',
+      initval: wrap.refresh || 'false',
+      required: false,
+      options: [
+        {value: 'true', label: '鏄剧ず'},
+        {value: 'false', label: '闅愯棌'},
+      ]
+    },
+    {
+      type: 'select',
+      field: 'linkmenu',
+      label: '鍏宠仈鑿滃崟',
+      initval: wrap.linkmenu || '',
+      tooltip: '浣跨敤鎵爜鐧诲綍鍔熻兘鏃剁殑璺宠浆椤甸潰锛屼笉浣跨敤鎵爜鐧诲綍鍙拷鐣ャ��',
+      required: false,
+      options: menulist
+    },
+  ]
+
+  return topbarWrapForm
+} 
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx b/src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx
deleted file mode 100644
index 77c0894..0000000
--- a/src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-import SettingForm from './settingform'
-import './index.scss'
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    visible: false,
-    wrap: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({wrap: fromJS(config.wrap).toJS()})
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = () => {
-    this.setState({
-      visible: true
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-
-    this.verifyRef.handleConfirm().then(res => {
-
-      this.setState({
-        wrap: res,
-        visible: false
-      })
-      this.props.updateConfig({...config, wrap: res})
-    })
-  }
-
-  render () {
-    const { visible, dict, wrap } = this.state
-
-    return (
-      <div className="model-menu-setting-wrap">
-        <Icon type="edit" title="缂栬緫" onClick={() => this.editDataSource()} />
-        <Modal
-          wrapClassName="popview-modal"
-          title="瀵艰埅鏍忚缃�"
-          visible={visible}
-          width={750}
-          maskClosable={false}
-          okText={dict['model.submit']}
-          onOk={this.verifySubmit}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <SettingForm
-            dict={dict}
-            wrap={wrap}
-            inputSubmit={this.verifySubmit}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/wrapsetting/index.scss b/src/mob/components/topbar/normal-navbar/wrapsetting/index.scss
deleted file mode 100644
index 04372e6..0000000
--- a/src/mob/components/topbar/normal-navbar/wrapsetting/index.scss
+++ /dev/null
@@ -1,7 +0,0 @@
-.model-menu-setting-wrap {
-  display: inline-block;
-
-  >.anticon-edit {
-    color: #1890ff;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx b/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx
deleted file mode 100644
index 7656c0e..0000000
--- a/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx
+++ /dev/null
@@ -1,130 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd'
-
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
-    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
-  }
-
-  state = {
-    type: this.props.wrap.type || 'navbar'
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { wrap } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { type } = this.state
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="model-menu-setting-form">
-        <Form {...formItemLayout}>
-          <Row gutter={24}>
-            <Col span={12}>
-              <Form.Item label="绫诲瀷">
-                {getFieldDecorator('type', {
-                  initialValue: wrap.type || 'navbar'
-                })(
-                  <Radio.Group onChange={(e) => this.setState({type: e.target.value})}>
-                    <Radio value="navbar">瀵艰埅鏍�</Radio>
-                    <Radio value="search">鎼滅储鏍�</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="浣跨敤鎼滅储鏍忔椂锛屾爣棰樼敤浜庢悳绱㈡潯浠堕殣钘忔椂鏄剧ず銆�">
-                  <Icon type="question-circle" />
-                  鏍囬
-                </Tooltip>
-              }>
-                {getFieldDecorator('title', {
-                  initialValue: wrap.title || ''
-                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="杩斿洖">
-                {getFieldDecorator('back', {
-                  initialValue: wrap.back || 'true'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄剧ず</Radio>
-                    <Radio value="false">闅愯棌</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-            {type === 'navbar' ? <Col span={12}>
-              <Form.Item label="鎼滅储">
-                {getFieldDecorator('search', {
-                  initialValue: wrap.search || 'false'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄剧ず</Radio>
-                    <Radio value="false">闅愯棌</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col> : null}
-            <Col span={12}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title="鐐瑰嚮閫�鍑烘椂锛岃繑鍥炵涓�涓〉闈€��">
-                  <Icon type="question-circle" />
-                  閫�鍑�
-                </Tooltip>
-              }>
-                {getFieldDecorator('logout', {
-                  initialValue: wrap.logout || 'false'
-                })(
-                  <Radio.Group>
-                    <Radio value="true">鏄剧ず</Radio>
-                    <Radio value="false">闅愯棌</Radio>
-                  </Radio.Group>
-                )}
-              </Form.Item>
-            </Col>
-          </Row>
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.scss b/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.scss
deleted file mode 100644
index 159130b..0000000
--- a/src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.scss
+++ /dev/null
@@ -1,11 +0,0 @@
-.model-menu-setting-form {
-  position: relative;
-
-  .anticon-question-circle {
-    color: #c49f47;
-    margin-right: 3px;
-  }
-  .ant-input-number {
-    width: 100%;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/mobshell/card.jsx b/src/mob/mobshell/card.jsx
index 9cd7402..4e8c4e1 100644
--- a/src/mob/mobshell/card.jsx
+++ b/src/mob/mobshell/card.jsx
@@ -9,7 +9,7 @@
 const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
 const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
 const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
-const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
+const AntvTabs = asyncComponent(() => import('@/mob/components/tabs/antv-tabs'))
 const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
 const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
 const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
@@ -17,6 +17,7 @@
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
 const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
@@ -87,8 +88,10 @@
       return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'scatter') {
       return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'form') {
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
       return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'tabs') {
       return (<AntvTabs tabs={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'card' && card.subtype === 'datacard') {
diff --git a/src/mob/mobshell/index.jsx b/src/mob/mobshell/index.jsx
index 5cdb407..a69679c 100644
--- a/src/mob/mobshell/index.jsx
+++ b/src/mob/mobshell/index.jsx
@@ -76,6 +76,8 @@
         return
       }
 
+      let style = null
+
       if (item.component === 'search') { // 鎼滅储缁勪欢涓嶅彲閲嶅娣诲姞
         if (cards.filter(card => card.type === 'search').length > 0) {
           notification.warning({
@@ -94,6 +96,9 @@
           })
           return
         }
+        if (!menu.style.paddingBottom) {
+          style = {...menu.style, paddingBottom: '50px'}
+        }
       } else if (item.component === 'topbar') {
         if (cards.filter(card => card.type === 'topbar').length > 0) {
           notification.warning({
@@ -102,6 +107,9 @@
             duration: 5
           })
           return
+        }
+        if (!menu.style.paddingTop) {
+          style = {...menu.style, paddingTop: '50px'}
         }
       }
 
@@ -122,6 +130,7 @@
         card: '鍗$墖',
         navbar: '瀵艰埅鏍�',
         menubar: '鑿滃崟鏍�',
+        balcony: '娴姩鍗�',
         login: '鐧诲綍'
       }
       let i = 1
@@ -176,7 +185,11 @@
         _cards.push(Navbar)
       }
 
-      handleList({...menu, components: _cards})
+      if (style) {
+        handleList({...menu, style, components: _cards})
+      } else {
+        handleList({...menu, components: _cards})
+      }
       setCards(_cards)
     }
   })
diff --git a/src/mob/modalconfig/index.jsx b/src/mob/modalconfig/index.jsx
index fc634fd..6876fc5 100644
--- a/src/mob/modalconfig/index.jsx
+++ b/src/mob/modalconfig/index.jsx
@@ -15,6 +15,7 @@
 import SourceElement from '@/templates/modalconfig/dragelement/source'
 import SettingForm from '@/templates/modalconfig/settingform'
 import asyncComponent from '@/utils/asyncComponent'
+import MKEmitter from '@/utils/events.js'
 import { SearchItems } from './source'
 import './index.scss'
 
@@ -43,7 +44,8 @@
     originConfig: null,    // 鍘熷鑿滃崟
     sqlVerifing: false,    // sql楠岃瘉
     showField: false,      // 鏄剧ず琛ㄥ崟瀛楁鍊�
-    standardform: null
+    standardform: null,
+    saving: false
   }
 
   /**
@@ -61,6 +63,10 @@
     })
   }
 
+  componentDidMount () {
+    MKEmitter.addListener('completeSave', this.completeSave)
+  }
+
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
    */
@@ -68,6 +74,11 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('completeSave', this.completeSave)
+  }
+
+  completeSave = () => {
+    this.setState({saving: false})
   }
 
   /**
@@ -288,13 +299,12 @@
   submitConfig = () => {
     const { config } = this.state
 
-    this.setState({originConfig: fromJS(config).toJS()})
+    this.setState({originConfig: fromJS(config).toJS(), saving: true})
     this.props.handleSave(config)
-    notification.success({
-      top: 92,
-      message: '淇濆瓨鎴愬姛銆�',
-      duration: 2
-    })
+
+    setTimeout(() => {
+      MKEmitter.emit('triggerMenuSave')
+    }, 100)
   }
 
   cancelConfig = () => {
@@ -374,7 +384,7 @@
   }
 
   render () {
-    const { config, dict } = this.state
+    const { config, dict, saving } = this.state
 
     return (
       <div className="mob-form-board">
@@ -397,7 +407,7 @@
           </div>
           <div className="modal-control">
             <Button icon="setting" onClick={this.changeSetting}>璁剧疆</Button>
-            <Button type="primary" onClick={this.submitConfig}>淇濆瓨</Button>
+            <Button type="primary" loading={saving} onClick={this.submitConfig}>淇濆瓨</Button>
             <Button onClick={this.cancelConfig}>杩斿洖</Button>
             <PasteComponent config={config} updateConfig={this.insert} />
             <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
diff --git a/src/mob/modulesource/option.jsx b/src/mob/modulesource/option.jsx
index 417a095..dfc9675 100644
--- a/src/mob/modulesource/option.jsx
+++ b/src/mob/modulesource/option.jsx
@@ -34,7 +34,8 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '鍙诞鍔ㄥ崱', width: 24 },
-  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', width: 24 },
+  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24 },
+  { type: 'menu', url: form, 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: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
diff --git a/src/mob/searchconfig/searchdragelement/index.scss b/src/mob/searchconfig/searchdragelement/index.scss
index 357ef51..0a803c7 100644
--- a/src/mob/searchconfig/searchdragelement/index.scss
+++ b/src/mob/searchconfig/searchdragelement/index.scss
@@ -10,7 +10,7 @@
   }
   .am-list-item {
     font-size: 16px;
-    padding-left: 10px;
+    // padding-left: 10px;
     position: relative;
     display: flex;
     height: 44px;
diff --git a/src/pc/components/login/normal-login/index.jsx b/src/pc/components/login/normal-login/index.jsx
index 028d757..9f0d75e 100644
--- a/src/pc/components/login/normal-login/index.jsx
+++ b/src/pc/components/login/normal-login/index.jsx
@@ -46,8 +46,9 @@
         wrap: { name: card.name, width: card.width || 24, loginWays: ['uname_pwd'] },
         style: { background: '#ffffff', width: '330px', borderRadius: '4px', marginLeft: '55vw'},
         loginWays: [
-          {type: 'uname_pwd', label: '璐﹀彿瀵嗙爜', remember: 'true', labelStyle: {}, submitStyle: {}, submitLabel: '鐧诲綍'},
-          {type: 'sms_vcode', label: '鐭俊楠岃瘉鐮�', labelStyle: {}, submitStyle: {}, submitLabel: '鐧诲綍'}
+          {type: 'uname_pwd', label: '璐﹀彿瀵嗙爜', remember: 'true'},
+          {type: 'sms_vcode', label: '鐭俊楠岃瘉鐮�'},
+          {type: 'app_scan', label: '鎵爜鐧诲綍'},
         ]
       }
 
@@ -71,8 +72,12 @@
       })
       this.props.updateConfig(_card)
     } else {
+      let _card = fromJS(card).toJS()
+      if (_card.loginWays.length === 2) {
+        _card.loginWays.push({type: 'app_scan', label: '鎵爜鐧诲綍'})
+      }
       this.setState({
-        card: fromJS(card).toJS()
+        card: _card
       })
     }
   }
diff --git a/src/pc/components/login/normal-login/index.scss b/src/pc/components/login/normal-login/index.scss
index f87a9a8..3112f0b 100644
--- a/src/pc/components/login/normal-login/index.scss
+++ b/src/pc/components/login/normal-login/index.scss
@@ -93,6 +93,30 @@
       line-height: 60px;
     }
   }
+  .form-scan-wrap {
+    padding: 30px;
+    text-align: center;
+    font-size: 12px;
+    line-height: 35px;
+    .qr-wrap {
+      position: relative;
+      width: 60%;
+      padding-top: 60%;
+      margin: 0 auto;
+
+      .qrcode-box {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        canvas {
+          width: 100%!important;
+          height: 100%!important;
+        }
+      }
+    }
+  }
 }
 .login-edit-box::after {
   display: block;
diff --git a/src/pc/components/login/normal-login/loginform.jsx b/src/pc/components/login/normal-login/loginform.jsx
index 20b96a4..0bce326 100644
--- a/src/pc/components/login/normal-login/loginform.jsx
+++ b/src/pc/components/login/normal-login/loginform.jsx
@@ -3,8 +3,11 @@
 import { is, fromJS } from 'immutable'
 import { Form, Icon, Input, Button, Checkbox } from 'antd'
 
+import asyncElementComponent from '@/utils/asyncComponent'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
+
+const QrCode = asyncElementComponent(() => import('@/components/qrcode'))
 
 class LoginTabForm extends Component {
   static propTpyes = {
@@ -131,6 +134,12 @@
             </Button>
           </Form.Item>
         </div> : null}
+        {activeWay.type === 'app_scan' ? <div className="form-scan-wrap">
+          <div className="qr-wrap">
+            <QrCode card={{qrWidth: 500, color: '#000000'}} value={'minkesoft'}/>
+          </div>
+          璇蜂娇鐢ㄥ鎴风鎵竴鎵櫥褰�
+        </div> : null}
       </Form>
     )
   }
diff --git a/src/pc/components/login/wrapsetting/settingform/index.jsx b/src/pc/components/login/wrapsetting/settingform/index.jsx
index 97042c8..cf36b2f 100644
--- a/src/pc/components/login/wrapsetting/settingform/index.jsx
+++ b/src/pc/components/login/wrapsetting/settingform/index.jsx
@@ -13,25 +13,13 @@
   }
 
   state = {
-    roleList: [],
     msgTemps: [],
     appMenus: [],
     link: this.props.wrap.link || 'menu'
   }
 
   UNSAFE_componentWillMount () {
-    let roleList = sessionStorage.getItem('sysRoles')
     let msgTemps = sessionStorage.getItem('msgTemplate')
-
-    if (roleList) {
-      try {
-        roleList = JSON.parse(roleList)
-      } catch {
-        roleList = []
-      }
-    } else {
-      roleList = []
-    }
 
     if (msgTemps) {
       try {
@@ -54,7 +42,7 @@
       appMenus = []
     }
 
-    this.setState({roleList, msgTemps, appMenus})
+    this.setState({msgTemps, appMenus})
   }
 
   handleConfirm = () => {
@@ -89,7 +77,7 @@
   render() {
     const { wrap } = this.props
     const { getFieldDecorator } = this.props.form
-    const { roleList, msgTemps, appMenus, link } = this.state
+    const { msgTemps, appMenus, link } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -137,8 +125,9 @@
                 })(
                   <Checkbox.Group
                     options={[
-                      { label: '璐﹀彿瀵嗙爜', value: 'uname_pwd' },
-                      { label: '鐭俊楠岃瘉鐮�', value: 'sms_vcode' },
+                      { label: '璐﹀彿', value: 'uname_pwd' },
+                      { label: '鐭俊', value: 'sms_vcode' },
+                      { label: '鎵爜', value: 'app_scan' },
                     ]}
                   />
                 )}
@@ -238,23 +227,6 @@
                   >
                     {msgTemps.map(option =>
                       <Select.Option key={option.ID} value={option.ID}>{option.SignName + ' - ' + option.TemplateCode}</Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col>
-            <Col span={12}>
-              <Form.Item label="榛戝悕鍗�">
-                {getFieldDecorator('blacklist', {
-                  initialValue: wrap.blacklist || []
-                })(
-                  <Select
-                    showSearch
-                    mode="multiple"
-                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                  >
-                    {roleList.map(option =>
-                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
                     )}
                   </Select>
                 )}
diff --git a/src/pc/components/navbar/normal-navbar/index.jsx b/src/pc/components/navbar/normal-navbar/index.jsx
index b70d77b..11ce5dd 100644
--- a/src/pc/components/navbar/normal-navbar/index.jsx
+++ b/src/pc/components/navbar/normal-navbar/index.jsx
@@ -166,13 +166,8 @@
   render() {
     const { card } = this.state
 
-    let _style = {...card.style}
-    if (_style.shadow) {
-      _style.boxShadow = '0 0 4px ' + _style.shadow
-    }
-
     return (
-      <div className="normal-navbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
+      <div className="normal-navbar-edit-box" style={card.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">
             <MenuComponent config={card} updateConfig={this.updateComponent} />
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
index 8174d84..26f5f43 100644
--- a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
@@ -139,7 +139,7 @@
       if (editMenu.MenuID && editMenu.property === 'menu' && _menu.property !== 'menu') {
         const _this = this
         confirm({
-          content: '鑿滃崟灞炴�х敱鈥滆彍鍗曗�濆垏鎹㈣嚦鍏朵粬绫诲瀷鏃讹紝鑿滃崟灏嗚閲嶇疆锛屽嵆瑙i櫎涔嬪墠鑿滃崟鐨勭粦瀹氬叧绯伙紝纭畾淇敼鍚楋紵',
+          content: '鑿滃崟灏嗚閲嶇疆锛岀‘瀹氫慨鏀瑰悧锛�',
           onOk() {
             _data = _data.map(item => {
               if (item.MenuID === _menu.MenuID) {
@@ -322,7 +322,7 @@
       if (editMenu.MenuID && editMenu.property === 'menu' && _menu.property !== 'menu') {
         const _this = this
         confirm({
-          content: '鑿滃崟灞炴�х敱鈥滆彍鍗曗�濆垏鎹㈣嚦鍏朵粬绫诲瀷鏃讹紝鑿滃崟灏嗚閲嶇疆锛屽嵆瑙i櫎涔嬪墠鑿滃崟鐨勭粦瀹氬叧绯伙紝纭畾淇敼鍚楋紵',
+          content: '鑿滃崟灏嗚閲嶇疆锛岀‘瀹氫慨鏀瑰悧锛�',
           onOk() {
             _data = _data.map(item => {
               if (item.MenuID === _menu.MenuID) {
@@ -515,7 +515,7 @@
       if (editMenu.MenuID && editMenu.property === 'menu' && _menu.property !== 'menu') {
         const _this = this
         confirm({
-          content: '鑿滃崟灞炴�х敱鈥滆彍鍗曗�濆垏鎹㈣嚦鍏朵粬绫诲瀷鏃讹紝鑿滃崟灏嗚閲嶇疆锛屽嵆瑙i櫎涔嬪墠鑿滃崟鐨勭粦瀹氬叧绯伙紝纭畾淇敼鍚楋紵',
+          content: '鑿滃崟灏嗚閲嶇疆锛岀‘瀹氫慨鏀瑰悧锛�',
           onOk() {
             _data = _data.map(item => {
               if (item.MenuID === _menu.MenuID) {
diff --git a/src/pc/menushell/card.jsx b/src/pc/menushell/card.jsx
index c3311f6..6e114c7 100644
--- a/src/pc/menushell/card.jsx
+++ b/src/pc/menushell/card.jsx
@@ -17,6 +17,7 @@
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
 const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/menu/components/form/tab-form'))
 const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
 const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 const NormalNavbar = asyncComponent(() => import('@/pc/components/navbar/normal-navbar'))
@@ -79,8 +80,10 @@
       return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'navbar') {
       return (<NormalNavbar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
-    } else if (card.type === 'form') {
+    } else if (card.type === 'form' && card.subtype === 'stepform') {
       return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    } else if (card.type === 'form' && card.subtype === 'tabform') {
+      return (<TabForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'search') {
       return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     } else if (card.type === 'pie') {
diff --git a/src/pc/menushell/index.jsx b/src/pc/menushell/index.jsx
index 24e3910..6143786 100644
--- a/src/pc/menushell/index.jsx
+++ b/src/pc/menushell/index.jsx
@@ -76,6 +76,8 @@
         return
       }
 
+      let style = null
+
       if (item.component === 'search') { // 鎼滅储缁勪欢涓嶅彲閲嶅娣诲姞
         if (cards.filter(card => card.type === 'search').length > 0) {
           notification.warning({
@@ -93,6 +95,10 @@
             duration: 5
           })
           return
+        }
+
+        if (!menu.style.paddingTop) {
+          style = {...menu.style, paddingTop: '50px'}
         }
       }
 
@@ -112,6 +118,7 @@
         dashboard: '浠〃鐩�',
         tree: '鏍戝舰鍒楄〃',
         card: '鍗$墖',
+        balcony: '娴姩鍗�',
         login: '鐧诲綍'
       }
       let i = 1
@@ -148,7 +155,11 @@
       const { index: overIndex } = findCard(`${targetId}`)
       const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
 
-      handleList({...menu, components: _cards})
+      if (style) {
+        handleList({...menu, style, components: _cards})
+      } else {
+        handleList({...menu, components: _cards})
+      }
       setCards(_cards)
     }
   })
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
index b7e6090..59832b5 100644
--- a/src/pc/modulesource/option.jsx
+++ b/src/pc/modulesource/option.jsx
@@ -33,7 +33,8 @@
   { type: 'menu', url: card1, component: 'card', subtype: 'datacard', title: '鏁版嵁鍗�', width: 24 },
   { type: 'menu', url: card2, component: 'card', subtype: 'propcard', title: '灞炴�у崱', width: 24 },
   { type: 'menu', url: card2, component: 'balcony', subtype: 'balcony', title: '鍙诞鍔ㄥ崱', width: 24 },
-  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', width: 24 },
+  { type: 'menu', url: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟锛堝垎姝ワ級', width: 24 },
+  { type: 'menu', url: form, 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: NormalTable, component: 'table', subtype: 'normaltable', title: '甯哥敤琛�', width: 24 },
diff --git a/src/pc/quotecomponent/index.jsx b/src/pc/quotecomponent/index.jsx
index a0bb263..1054d31 100644
--- a/src/pc/quotecomponent/index.jsx
+++ b/src/pc/quotecomponent/index.jsx
@@ -84,6 +84,12 @@
 
         config.components.unshift(_config)
 
+        if (sessionStorage.getItem('appType') !== 'pc' && !config.style.paddingBottom) {
+          config.style.paddingBottom = '50px'
+        } else if (sessionStorage.getItem('appType') === 'pc' && !config.style.paddingTop) {
+          config.style.paddingTop = '50px'
+        }
+
         this.setState({
           visible: false
         })
diff --git a/src/router/index.js b/src/router/index.js
index f496dec..9af2b9f 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -81,6 +81,11 @@
     let authCode = localStorage.getItem(window.location.href.split('#')[0] + 'AuthCode') // 鍒ゆ柇绯荤粺鏄惁鍦ㄦ巿鏉冩湡闄愬唴
     let _s = md5('mksoft' + moment().format('YYYYMMDD'))
     let isauth = authCode && authCode.includes(_s)
+    let key = md5(window.GLOB.appId + 'minke_software' + window.GLOB.appkey).toUpperCase().substr(-6)
+
+    if (window.GLOB.licenseKey === key) {
+      isauth = true
+    }
 
     if (userId && isauth) {
       return (<item.component {...props}/>)
diff --git a/src/tabviews/calendar/index.jsx b/src/tabviews/calendar/index.jsx
index b8b7c2a..f63be99 100644
--- a/src/tabviews/calendar/index.jsx
+++ b/src/tabviews/calendar/index.jsx
@@ -344,6 +344,9 @@
     let regoptions = []
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -399,9 +402,9 @@
       regoptions.forEach(item => {
         param.custom_script = param.custom_script.replace(item.reg, item.value)
       })
-
-      param.custom_script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      
+      param.custom_script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${param.custom_script}
       `
 
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index 763405f..fdaa284 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -90,6 +90,7 @@
         config.MenuID = this.props.MenuID
         config.MenuName = MenuName
         config.setting.MenuName = MenuName
+        config.setting.$name = MenuName
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -665,10 +666,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, search, _orderBy, pageIndex, pageSize, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.MenuName || ''
-    }
-
     let result = await Api.genericInterface(param)
 
     this.getStatFieldsValue()
@@ -725,10 +722,6 @@
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, search, _orderBy, pageIndex, pageSize, BID, this.props.menuType, id)
-
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.MenuName || ''
-    }
 
     let result = await Api.genericInterface(param)
     if (result.status) {
@@ -796,10 +789,6 @@
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, statFields, search, _orderBy, BID, this.props.menuType)
-
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.MenuName || ''
-    }
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
diff --git a/src/tabviews/custom/components/card/balcony/index.jsx b/src/tabviews/custom/components/card/balcony/index.jsx
index 879d8f5..5026c93 100644
--- a/src/tabviews/custom/components/card/balcony/index.jsx
+++ b/src/tabviews/custom/components/card/balcony/index.jsx
@@ -1,10 +1,9 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Spin, notification } from 'antd'
+import { Spin, notification, Checkbox } 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'
@@ -15,7 +14,6 @@
 class BalconyComponent extends Component {
   static propTpyes = {
     BID: PropTypes.any,
-    menu: PropTypes.object,
     data: PropTypes.array,
     config: PropTypes.object,
     menuType: PropTypes.any,
@@ -28,11 +26,14 @@
     loading: false,
     sync: false,
     data: {},
-    show: true
+    BData: null,
+    syncData: [],
+    show: true,
+    checked: false
   }
 
   UNSAFE_componentWillMount () {
-    const { data, BID, menu } = this.props
+    const { data, BID } = this.props
     let _config = fromJS(this.props.config).toJS()
     let _cols = new Map()
 
@@ -75,32 +76,11 @@
     let show = true
     let syncConfig = null
     if (_config.wrap.linkType === 'sync') {
-      _config.wrap.syncModule = _config.wrap.syncModule.pop()
-      
-      let filterComponent = (components) => {
-        components.forEach(item => {
-          if (syncConfig) return
-          if (item.type === 'tabs') {
-            item.subtabs.forEach(tab => {
-              filterComponent(tab.components)
-            })
-          } else if (item.type === 'group') {
-            filterComponent(item.components)
-          } else if (_config.wrap.syncModule === item.uuid) {
-            syncConfig = {
-              uuid: item.uuid,
-              wrap: item.wrap,
-              setting: item.setting,
-              columns: item.columns
-            }
-          }
-        })
-      }
+      syncConfig = _config.syncConfig
 
-      filterComponent(menu.components)
       _config.elements = _config.elements.map(item => {
-        if (item.eleType === 'button') {
-          item.$syncModule = _config.wrap.syncModule
+        if (item.eleType === 'button' || item.eleType === 'formula') {
+          item.$sync = true
         }
         return item
       })
@@ -128,6 +108,7 @@
 
   componentDidMount () {
     MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('syncBalconyData', this.syncBalconyData)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
     MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
   }
@@ -141,6 +122,7 @@
       return
     }
     MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('syncBalconyData', this.syncBalconyData)
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
   }
@@ -201,14 +183,22 @@
     }
   }
 
-  resetParentParam = (MenuID, id) => {
+  syncBalconyData = (menuId, data, checked) => {
+    const { syncConfig } = this.state
+
+    if (!syncConfig || syncConfig.uuid !== menuId) return
+
+    this.setState({syncData: data, checked})
+  }
+
+  resetParentParam = (MenuID, id, data) => {
     const { config, syncConfig } = this.state
 
     if (syncConfig) {
       if (!syncConfig.setting.supModule || syncConfig.setting.supModule !== MenuID) return
   
       if (id !== this.state.BID) {
-        this.setState({ BID: id }, () => {
+        this.setState({ BID: id, BData: data }, () => {
           this.loadData()
         })
       }
@@ -220,7 +210,7 @@
       }
   
       if (id !== this.state.BID) {
-        this.setState({ BID: id }, () => {
+        this.setState({ BID: id, BData: data }, () => {
           this.loadData()
         })
       }
@@ -237,16 +227,16 @@
 
   async loadData () {
     const { menuType } = this.props
-    const { config, arr_field, BID } = this.state
+    const { config, arr_field, BID, BData } = this.state
 
     if (config.wrap.datatype === 'static') {
       this.setState({
-        data: {$$BID: BID || ''},
+        data: {$$BID: BID || '', $$BData: BData},
       })
       return
     } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
-        data: {$$BID: BID || ''},
+        data: {$$BID: BID || '', $$BData: BData},
       })
       return
     }
@@ -264,6 +254,7 @@
     if (result.status) {
       let _data = result.data && result.data[0] ? result.data[0] : {}
       _data.$$BID = BID || ''
+      _data.$$BData = BData
 
       this.setState({
         data: _data,
@@ -281,8 +272,14 @@
     }
   }
 
+  checkAll = (e) => {
+    const { syncConfig } = this.state
+
+    MKEmitter.emit('mkCheckAll', syncConfig.uuid, e.target.checked)
+  }
+
   render() {
-    const { config, loading, data, show, syncConfig } = this.state
+    const { config, loading, data, show, syncConfig, syncData, checked } = this.state
 
     return (
       <div className={'custom-balcony-box' + (!show ? ' hidden' : '')} style={config.style}>
@@ -292,7 +289,8 @@
             <Spin />
           </div> : null
         }
-        <CardCellComponent data={data} cards={syncConfig || config} cardCell={config} elements={config.elements}/>
+        {config.wrap.checkAll === 'show' ? <div className="check-all"><Checkbox checked={checked} onChange={this.checkAll}>鍏ㄩ��</Checkbox></div> : null}
+        <CardCellComponent data={data} syncData={syncData || []} cards={syncConfig || config} cardCell={config} elements={config.elements}/>
       </div>
     )
   }
diff --git a/src/tabviews/custom/components/card/balcony/index.scss b/src/tabviews/custom/components/card/balcony/index.scss
index 37c4a31..e49a9ee 100644
--- a/src/tabviews/custom/components/card/balcony/index.scss
+++ b/src/tabviews/custom/components/card/balcony/index.scss
@@ -4,6 +4,17 @@
   background-repeat: no-repeat;
   background-size: cover;
   position: relative;
+  display: flex;
+
+  >.check-all {
+    width: 70px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+  >.card-cell-list {
+    flex: 1;
+  }
 
   .card-row-list::after {
     content: ' ';
diff --git a/src/tabviews/custom/components/card/cardcellList/index.jsx b/src/tabviews/custom/components/card/cardcellList/index.jsx
index 8b5fa84..5d1e62b 100644
--- a/src/tabviews/custom/components/card/cardcellList/index.jsx
+++ b/src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -7,6 +7,7 @@
 import asyncComponent from './asyncButtonComponent'
 import asyncElementComponent from '@/utils/asyncComponent'
 
+import MKEmitter from '@/utils/events.js'
 import LostPng from '@/assets/img/lost.png'
 import './index.scss'
 
@@ -32,6 +33,7 @@
     cards: PropTypes.object,         // 鑿滃崟閰嶇疆淇℃伅
     cardCell: PropTypes.object,
     data: PropTypes.object,
+    syncData: PropTypes.array,
     elements: PropTypes.array,       // 鍏冪礌闆�
   }
 
@@ -473,10 +475,24 @@
         _style.cursor = 'pointer'
       }
 
+      let scale = url && card.scale === 'true'
+      
       return (
         <Col key={card.uuid} span={card.width}>
           <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
-            <div className="ant-mk-picture" style={_imagestyle}></div>
+            <div
+              className={'ant-mk-picture' + (scale ? ' scale' : '')}
+              onClick={(e) => {
+                if (scale) {
+                  e.stopPropagation()
+                } else {
+                  return
+                }
+
+                MKEmitter.emit('mkImageScale', url)
+              }}
+              style={_imagestyle}
+            ></div>
           </div>
         </Col>
       )
@@ -549,14 +565,73 @@
           </div>
         </Col>
       )
+    } else if (card.eleType === 'formula') {
+      let val = 0
+      let _style = card.style ? {...card.style} : {}
+
+      if (card.$sync) {
+        this.props.syncData.forEach(item => {
+          let _val = card.formula
+          Object.keys(item).forEach(key => {
+            let reg = new RegExp('@' + key + '@', 'ig')
+            _val = _val.replace(reg, item[key])
+          })
+          try {
+            // eslint-disable-next-line
+            _val = eval(_val)
+          } catch {
+            _val = 0
+          }
+
+          val += _val
+        })
+      } else if (data) {
+        let _val = card.formula
+        Object.keys(data).forEach(key => {
+          let reg = new RegExp('@' + key + '@', 'ig')
+          _val = _val.replace(reg, data[key])
+        })
+
+        try {
+          // eslint-disable-next-line
+          _val = eval(_val)
+        } catch {
+          _val = 0
+        }
+
+        val = _val
+      }
+
+      if (val !== '') {
+        val = `${card.prefix || ''}${val}${card.postfix || ''}`
+      }
+
+      if (card.marks) {
+        val = this.getMark(card.marks, _style, val)
+      }
+
+      return (
+        <Col key={card.uuid} span={card.width}>
+          <div style={_style}>
+            <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
+          </div>
+        </Col>
+      )
     } else if (card.eleType === 'button') {
-      let _data = data.$$type === 'extendCard' ? [] : [data]
+      let _data = [data]
+
+      if (data.$$type === 'extendCard') {
+        _data = []
+      } else if (card.$sync) {
+        _data = this.props.syncData
+      }
 
       if (['exec', 'prompt', 'pop'].includes(card.OpenType)) {
         return (
           <Col key={card.uuid} className="mk-cell-btn" span={card.width}>
             <NormalButton
               BID={data.$$BID}
+              BData={data.$$BData || ''}
               btn={card}
               show={card.show}
               style={card.style}
@@ -596,6 +671,7 @@
           <Col key={card.uuid} className="mk-cell-btn" span={card.width}>
             <PopupButton
               BID={data.$$BID}
+              BData={data.$$BData || ''}
               btn={card}
               show={card.show}
               style={card.style}
@@ -647,6 +723,7 @@
             <Col key={card.uuid} className="mk-cell-btn" span={card.width}>
               <PrintButton
                 BID={data.$$BID}
+                BData={data.$$BData || ''}
                 btn={card}
                 show={card.show}
                 style={card.style}
diff --git a/src/tabviews/custom/components/card/cardcellList/index.scss b/src/tabviews/custom/components/card/cardcellList/index.scss
index 97c3ab3..4cadef8 100644
--- a/src/tabviews/custom/components/card/cardcellList/index.scss
+++ b/src/tabviews/custom/components/card/cardcellList/index.scss
@@ -58,9 +58,10 @@
     -webkit-line-clamp: 10;
   }
   .mk-cell-btn {
-    > div {width: 100%;}
+    > div {width: 100%;max-width: 100%;}
     button {
       width: 100%;
+      max-width: 100%;
       height: auto;
       min-height: 32px;
     }
@@ -118,6 +119,22 @@
     word-break: break-word;
     text-overflow: ellipsis;
   }
+  .ant-mk-check {
+    white-space: nowrap;
+    overflow: hidden;
+    word-break: break-word;
+    text-overflow: ellipsis;
+    font-style: inherit;
+    .ant-checkbox-wrapper {
+      font-weight: inherit;
+      font-size: inherit;
+      font-style: inherit;
+      color: inherit;
+      span {
+        font-weight: inherit;
+      }
+    }
+  }
   .ant-slider {
     margin: 0px;
   }
@@ -126,6 +143,9 @@
     background-position: center center;
     background-repeat: no-repeat;
   }
+  .ant-mk-picture.scale {
+    cursor: zoom-in;
+  }
 }
 .card-cell-list::after {
   content: ' ';
diff --git a/src/tabviews/custom/components/card/data-card/index.jsx b/src/tabviews/custom/components/card/data-card/index.jsx
index 9272285..23383a8 100644
--- a/src/tabviews/custom/components/card/data-card/index.jsx
+++ b/src/tabviews/custom/components/card/data-card/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
 import { connect } from 'react-redux'
-import { Spin, Empty, notification, Row, Col, Pagination } from 'antd'
+import { Spin, Empty, notification, message, Row, Col, Pagination } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -146,7 +146,7 @@
 
   componentDidMount () {
     MKEmitter.addListener('reloadData', this.reloadData)
-    MKEmitter.addListener('getSyncData', this.getSyncData)
+    MKEmitter.addListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
     MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
     MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
@@ -186,18 +186,10 @@
       return
     }
     MKEmitter.removeListener('reloadData', this.reloadData)
-    MKEmitter.removeListener('getSyncData', this.getSyncData)
+    MKEmitter.removeListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
     MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
-  }
-
-  getSyncData = (syncModule, btnId) => {
-    const { config, selectedData } = this.state
-
-    if (config.uuid !== syncModule) return
-
-    MKEmitter.emit('triggerBtnId', btnId, (selectedData || []))
   }
 
   /**
@@ -230,6 +222,35 @@
     } else if (position === 'popclose') {                                      // 鏍囩鍏抽棴鍒锋柊
       config.setting.supModule && MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty'))
       btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
+    }
+  }
+
+  mkCheckAll = (menuId, checked) => {
+    const { config, data } = this.state
+
+    if (config.uuid !== menuId) return
+
+    if (checked) {
+      this.setState({
+        activeKey: '',
+        selectKeys: data.map((item, index) => index),
+        selectedData: data
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      MKEmitter.emit('syncBalconyData', config.uuid, data, data.length > 0)
+      if (data.length === 0) {
+        message.warning('鏈幏鍙栧埌鏁版嵁锛�')
+      }
+    } else {
+      this.setState({
+        activeKey: '',
+        selectKeys: [],
+        selectedData: []
+      })
+  
+      MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      MKEmitter.emit('syncBalconyData', config.uuid, [], false)
     }
   }
 
@@ -286,7 +307,7 @@
 
   async loadData () {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, pageIndex, search, BID } = this.state
+    const { config, arr_field, pageIndex, search, BID, BData } = this.state
 
     if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
@@ -299,6 +320,9 @@
         loading: false
       })
       MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      if (config.setting.$hasSyncModule) {
+        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+      }
       return
     }
 
@@ -339,6 +363,7 @@
           item.key = index
           item.$$uuid = item[config.setting.primaryKey] || ''
           item.$$BID = BID || ''
+          item.$$BData = BData || ''
           item.$Index = index + start + ''
           return item
         }),
@@ -346,6 +371,9 @@
         loading: false
       })
       MKEmitter.emit('resetSelectLine', config.uuid, '', '')
+      if (config.setting.$hasSyncModule) {
+        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+      }
     } else {
       this.setState({
         loading: false
@@ -363,7 +391,7 @@
    */ 
   async loadLinedata (id) {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, pageIndex, search, BID } = this.state
+    const { config, arr_field, pageIndex, search, BID, BData } = this.state
 
     let searches = fromJS(search).toJS()
     if (config.setting.useMSearch && mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
@@ -382,10 +410,6 @@
     let _orderBy = config.setting.order || ''
     let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, pageIndex, config.setting.pageSize, BID, menuType, id)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = config.name || ''
-    }
-
     let result = await Api.genericInterface(param)
     if (result.status) {
       let data = fromJS(this.state.data).toJS()
@@ -398,6 +422,7 @@
               _data.key = item.key
               _data.$$uuid = _data[config.setting.primaryKey] || ''
               _data.$$BID = BID || ''
+              item.$$BData = BData || ''
               _data.$Index = item.$Index
               return _data
             } else {
@@ -468,7 +493,7 @@
   }
   
   changeCard = (index, item) => {
-    const { config, selectKeys, selectedData, activeKey } = this.state
+    const { config, selectKeys, selectedData, activeKey, data } = this.state
 
     this.openView(item)
 
@@ -508,6 +533,9 @@
     })
 
     MKEmitter.emit('resetSelectLine', config.uuid, (_item ? _item.$$uuid : ''), _item)
+    if (config.setting.$hasSyncModule) {
+      MKEmitter.emit('syncBalconyData', config.uuid, _selectedData, data.length === _selectedData.length)
+    }
   }
 
   openView = (item) => {
@@ -540,7 +568,7 @@
         newtab.param.$BID = item.$$uuid
       }
 
-      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+      if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
         this.props.modifyTabview([newtab])
       } else {
         let tabs = this.props.tabviews.filter((tab, i) => {
diff --git a/src/tabviews/custom/components/card/data-card/index.scss b/src/tabviews/custom/components/card/data-card/index.scss
index 9e0798f..e8be8a4 100644
--- a/src/tabviews/custom/components/card/data-card/index.scss
+++ b/src/tabviews/custom/components/card/data-card/index.scss
@@ -122,7 +122,7 @@
     }
   }
   .ant-pagination {
-    margin: 10px;
+    padding: 10px;
     text-align: right;
   }
   .trigger-button {
diff --git a/src/tabviews/custom/components/card/prop-card/index.jsx b/src/tabviews/custom/components/card/prop-card/index.jsx
index fc110d8..d00a06d 100644
--- a/src/tabviews/custom/components/card/prop-card/index.jsx
+++ b/src/tabviews/custom/components/card/prop-card/index.jsx
@@ -32,7 +32,8 @@
     activeKey: '',             // 閫変腑鏁版嵁
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
     data: {},                  // 鏁版嵁
-    timer: null                // 瀹氭椂鍣ㄦ椂闂撮棿闅�
+    timer: null,               // 瀹氭椂鍣ㄦ椂闂撮棿闅�
+    BData: null
   }
 
   UNSAFE_componentWillMount () {
@@ -240,12 +241,12 @@
     }
   }
 
-  resetParentParam = (MenuID, id) => {
+  resetParentParam = (MenuID, id, data) => {
     const { config } = this.state
 
     if (config.wrap.datatype === 'static' || !config.setting.supModule || config.setting.supModule !== MenuID) return
     if (id !== this.state.BID) {
-      this.setState({ BID: id }, () => {
+      this.setState({ BID: id, BData: data }, () => {
         this.loadData()
       })
     }
@@ -261,16 +262,16 @@
 
   async loadData (hastimer) {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, BID } = this.state
+    const { config, arr_field, BID, BData } = this.state
 
     if (config.wrap.datatype === 'static') {
       this.setState({
-        data: {$$BID: BID || ''},
+        data: {$$BID: BID || '', $$BData: BData},
       })
       return
     } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
-        data: {$$BID: BID || ''},
+        data: {$$BID: BID || '', $$BData: BData},
       })
       return
     }
@@ -295,6 +296,7 @@
     if (result.status) {
       let _data = result.data && result.data[0] ? result.data[0] : {}
       _data.$$BID = BID || ''
+      _data.$$BData = BData
 
       this.setState({
         activeKey: '',
@@ -356,7 +358,7 @@
         newtab.param.$BID = item.setting.primaryId || ''
       }
 
-      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+      if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
         this.props.modifyTabview([newtab])
       } else {
         let tabs = this.props.tabviews.filter((tab, i) => {
diff --git a/src/tabviews/custom/components/card/table-card/index.jsx b/src/tabviews/custom/components/card/table-card/index.jsx
index 609d46e..bf19684 100644
--- a/src/tabviews/custom/components/card/table-card/index.jsx
+++ b/src/tabviews/custom/components/card/table-card/index.jsx
@@ -31,6 +31,7 @@
     total: 0,                  // 鎬绘暟
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
     data: null,                // 鏁版嵁
+    BData: null
   }
 
   /**
@@ -175,12 +176,12 @@
     }
   }
 
-  resetParentParam = (MenuID, id) => {
+  resetParentParam = (MenuID, id, data) => {
     const { config } = this.state
 
     if (!config.setting.supModule || config.setting.supModule !== MenuID) return
     if (id !== this.state.BID) {
-      this.setState({ BID: id, pageIndex: 1 }, () => {
+      this.setState({ BID: id, BData: data, pageIndex: 1 }, () => {
         this.loadData()
       })
     }
@@ -223,7 +224,7 @@
 
   async loadData () {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, pageIndex, search, BID } = this.state
+    const { config, arr_field, pageIndex, search, BID, BData } = this.state
 
     if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
@@ -267,6 +268,7 @@
           item.key = index
           item.$$uuid = item[config.setting.primaryKey] || ''
           item.$$BID = BID || ''
+          item.$$BData = BData || ''
           item.$Index = index + start + ''
           return item
         }),
diff --git a/src/tabviews/custom/components/carousel/data-card/index.jsx b/src/tabviews/custom/components/carousel/data-card/index.jsx
index e4c768a..8e744fc 100644
--- a/src/tabviews/custom/components/carousel/data-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -29,6 +29,7 @@
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
     card: null,                // 鍗$墖璁剧疆
     data: null,                // 鏁版嵁
+    BData: null
   }
 
   UNSAFE_componentWillMount () {
@@ -163,7 +164,7 @@
 
     if (!config.setting.supModule || config.setting.supModule !== MenuID) return
     if (id !== this.state.BID) {
-      this.setState({ BID: id }, () => {
+      this.setState({ BID: id, BData: data }, () => {
         this.loadData()
       })
     }
@@ -190,7 +191,7 @@
 
   async loadData () {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, BID } = this.state
+    const { config, arr_field, BID, BData } = this.state
 
     if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
@@ -220,6 +221,7 @@
           item.key = index
           item.$$uuid = item[config.setting.primaryKey] || ''
           item.$$BID = BID || ''
+          item.$$BData = BData || ''
           return item
         }),
         loading: false
@@ -266,7 +268,7 @@
         newtab.param.$BID = item.$$uuid
       }
 
-      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+      if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
         this.props.modifyTabview([newtab])
       } else {
         let tabs = this.props.tabviews.filter((tab, i) => {
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.jsx b/src/tabviews/custom/components/carousel/prop-card/index.jsx
index 8402eb6..392ac7a 100644
--- a/src/tabviews/custom/components/carousel/prop-card/index.jsx
+++ b/src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -27,7 +27,8 @@
     config: null,              // 鍥捐〃閰嶇疆淇℃伅
     loading: false,            // 鏁版嵁鍔犺浇鐘舵��
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
-    data: {}                   // 鏁版嵁
+    data: {},                  // 鏁版嵁
+    BData: null
   }
 
   UNSAFE_componentWillMount () {
@@ -165,12 +166,12 @@
     }
   }
 
-  resetParentParam = (MenuID, id) => {
+  resetParentParam = (MenuID, id, data) => {
     const { config } = this.state
 
     if (config.wrap.datatype === 'static' || !config.setting.supModule || config.setting.supModule !== MenuID) return
     if (id !== this.state.BID) {
-      this.setState({ BID: id }, () => {
+      this.setState({ BID: id, BData: data }, () => {
         this.loadData()
       })
     }
@@ -186,16 +187,16 @@
 
   async loadData () {
     const { mainSearch, menuType } = this.props
-    const { config, arr_field, BID } = this.state
+    const { config, arr_field, BID, BData } = this.state
 
     if (config.wrap.datatype === 'static') {
       this.setState({
-        data: {$$BID: BID || ''}
+        data: {$$BID: BID || '', $$BData: BData}
       })
       return
     } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
-        data: {$$BID: BID || ''}
+        data: {$$BID: BID || '', $$BData: BData}
       })
       return
     }
@@ -218,6 +219,7 @@
     if (result.status) {
       let _data = result.data && result.data[0] ? result.data[0] : {}
       _data.$$BID = BID || ''
+      _data.$$BData = BData || ''
 
       this.setState({
         data: _data,
@@ -263,7 +265,7 @@
         newtab.param.$BID = item.setting.primaryId
       }
 
-      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+      if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
         this.props.modifyTabview([newtab])
       } else {
         let tabs = this.props.tabviews.filter((tab, i) => {
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 f279ed3..9f522fb 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -12,9 +12,10 @@
 import Utils from '@/utils/utils.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import MKEmitter from '@/utils/events.js'
+import NormalHeader from '@/tabviews/custom/components/share/normalheader'
 import './index.scss'
 
-const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+// const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 const ExcelOutButton = asyncComponent(() => import('@/tabviews/zshare/actionList/exceloutbutton'))
 const ExcelInButton = asyncComponent(() => import('@/tabviews/zshare/actionList/excelInbutton'))
 
@@ -94,22 +95,7 @@
       })
     }
 
-    let padding = 0
-    if (_config.style.paddingTop && !isNaN(parseInt(_config.style.paddingTop))) {
-      padding += parseInt(_config.style.paddingTop)
-    }
-    if (_config.style.paddingBottom && !isNaN(parseInt(_config.style.paddingBottom))) {
-      padding += parseInt(_config.style.paddingBottom)
-    }
-
-    let height = config.plot.height || 400
-    if (config.plot.title || config.search.length > 0) {
-      _config.plot.height = height - 70 - padding
-    } else {
-      _config.plot.height = height - 25 - padding
-    }
-
-    _config.style.height = height
+    _config.style.height = config.plot.height || 400
 
     let transfield = {}
     _config.columns.forEach(col => {
@@ -906,11 +892,11 @@
   
       _data = dv.rows
     }
-    
+
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: plot.height
+      height: this.wrap.offsetHeight - 25
     })
 
     chart.data(_data)
@@ -1089,7 +1075,7 @@
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: plot.height
+      height: this.wrap.offsetHeight - 25
     })
     
     // 鍧愭爣杞存牸寮忓寲
@@ -1477,7 +1463,7 @@
     const chart = new Chart({
       container: this.state.chartId,
       autoFit: true,
-      height: plot.height
+      height: this.wrap.offsetHeight - 25
     })
 
     chart.data(_data)
@@ -1688,7 +1674,7 @@
 
     chart.on('element:click', (ev) => {
       let data = ev.data.data
-      MKEmitter.emit('resetSelectLine', config.uuid, (data ? data.$$uuid : ''), null)
+      MKEmitter.emit('resetSelectLine', config.uuid, (data ? data.$$uuid : ''), data)
     })
 
     if (plot.interaction && plot.interaction.length) {
@@ -1718,7 +1704,7 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} menuType={this.props.menuType} refresh={this.refreshSearch} />
-        <div className="canvas-wrap">
+        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
           <div className="chart-action">
             {config.action.map(item => {
               if (item.OpenType === 'excelOut') {
@@ -1744,7 +1730,7 @@
               }
             })}
           </div>
-          <div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 25}} id={this.state.chartId}></div>
+          <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
         </div>
         {empty ? <Empty description={false}/> : null}
       </div>
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 714a088..093ce92 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/index.scss
+++ b/src/tabviews/custom/components/chart/antv-bar-line/index.scss
@@ -5,11 +5,13 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
+  display: flex;
+  flex-flow: column;
 
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    min-height: calc(100% - 45px);
+    flex: 1;
     .chart-action {
       position: absolute;
       top: 0px;
diff --git a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
index 041e41d..af8a9bf 100644
--- a/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-dashboard/index.jsx
@@ -7,12 +7,13 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
-import asyncComponent from '@/utils/asyncComponent'
+// import asyncComponent from '@/utils/asyncComponent'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import MKEmitter from '@/utils/events.js'
+import NormalHeader from '@/tabviews/custom/components/share/normalheader'
 import './index.scss'
 
-const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+// const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 
 registerShape('point', 'pointer', {
   draw(cfg, container) {
@@ -96,13 +97,7 @@
       }
     }
 
-    let height = config.plot.height || 400
-    if (config.plot.title) {
-      _config.plot.height = height - 75
-    } else {
-      _config.plot.height = height - 30
-    }
-    _config.style.height = height
+    _config.style.height = config.plot.height || 400
 
     this.setState({
       config: _config,
@@ -355,7 +350,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: plot.height,
+      height: this.wrap.offsetHeight - 30,
     })
     
     chart.data(data)
@@ -462,7 +457,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: plot.height,
+      height: this.wrap.offsetHeight - 30,
       padding: [0, 0, 0, 0],
     })
     chart.data([_data])
@@ -607,8 +602,8 @@
           </div> : null
         }
         <NormalHeader config={config} />
-        <div className="canvas-wrap">
-          <div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 30}} id={this.state.chartId}></div>
+        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+          <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
         </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 d9f3566..10db44d 100644
--- a/src/tabviews/custom/components/chart/antv-dashboard/index.scss
+++ b/src/tabviews/custom/components/chart/antv-dashboard/index.scss
@@ -5,11 +5,13 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
+  display: flex;
+  flex-flow: column;
   
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    min-height: calc(100% - 45px);
+    flex: 1;
     .chart-action {
       position: absolute;
       top: 2px;
diff --git a/src/tabviews/custom/components/chart/antv-pie/index.jsx b/src/tabviews/custom/components/chart/antv-pie/index.jsx
index 002b51b..145efac 100644
--- a/src/tabviews/custom/components/chart/antv-pie/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-pie/index.jsx
@@ -11,12 +11,13 @@
 import Utils from '@/utils/utils.js'
 import { modifyTabview } from '@/store/action'
 import { chartColors } from '@/utils/option.js'
-import asyncComponent from '@/utils/asyncComponent'
+// import asyncComponent from '@/utils/asyncComponent'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import MKEmitter from '@/utils/events.js'
+import NormalHeader from '@/tabviews/custom/components/share/normalheader'
 import './index.scss'
 
-const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+// const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 
 class PieChart extends Component {
   static propTpyes = {
@@ -55,14 +56,7 @@
       _sync = false
     }
 
-    let height = config.plot.height || 400
-    if (config.plot.title || config.search.length > 0) {
-      _config.plot.height = height - 75
-    } else {
-      _config.plot.height = height - 30
-    }
-
-    _config.style.height = height
+    _config.style.height = config.plot.height || 400
 
     let decimal = 0
     _config.columns.forEach(col => {
@@ -558,7 +552,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: plot.height,
+      height: this.wrap.offsetHeight - 30,
       padding: 0,
     })
 
@@ -752,7 +746,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: plot.height
+      height: this.wrap.offsetHeight - 30
     })
 
     if (plot.shape !== 'nightingale' && plot.show !== 'value') {
@@ -1021,8 +1015,8 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} menuType={this.props.menuType} refresh={this.refreshSearch} />
-        <div className="canvas-wrap">
-          <div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 30}} id={this.state.chartId}></div>
+        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
+          <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
         </div>
         {empty ? <Empty description={false}/> : null}
       </div>
diff --git a/src/tabviews/custom/components/chart/antv-pie/index.scss b/src/tabviews/custom/components/chart/antv-pie/index.scss
index 3360ffe..5db4d2f 100644
--- a/src/tabviews/custom/components/chart/antv-pie/index.scss
+++ b/src/tabviews/custom/components/chart/antv-pie/index.scss
@@ -5,11 +5,13 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
+  display: flex;
+  flex-flow: column;
   
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    min-height: calc(100% - 45px);
+    flex: 1;
     .chart-action {
       position: absolute;
       top: 2px;
diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.jsx b/src/tabviews/custom/components/chart/antv-scatter/index.jsx
index 742617f..a085d4e 100644
--- a/src/tabviews/custom/components/chart/antv-scatter/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-scatter/index.jsx
@@ -7,13 +7,14 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
-import asyncComponent from '@/utils/asyncComponent'
+// import asyncComponent from '@/utils/asyncComponent'
 import asyncBtnComponent from './asyncButtonComponent'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import MKEmitter from '@/utils/events.js'
+import NormalHeader from '@/tabviews/custom/components/share/normalheader'
 import './index.scss'
 
-const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
+// const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 const ExcelOutButton = asyncBtnComponent(() => import('@/tabviews/zshare/actionList/exceloutbutton'))
 const ExcelInButton = asyncBtnComponent(() => import('@/tabviews/zshare/actionList/excelInbutton'))
 
@@ -52,14 +53,7 @@
       _sync = false
     }
 
-    let height = config.plot.height || 400
-    if (config.plot.title || config.search.length > 0) {
-      _config.plot.height = height - 70
-    } else {
-      _config.plot.height = height - 25
-    }
-
-    _config.style.height = height
+    _config.style.height = config.plot.height || 400
 
     this.setState({
       config: _config,
@@ -327,7 +321,7 @@
     const chart = new Chart({
       container: chartId,
       autoFit: true,
-      height: plot.height
+      height: this.wrap.offsetHeight - 25
     })
 
     chart.data(data);
@@ -395,7 +389,7 @@
           </div> : null
         }
         <NormalHeader config={config} BID={BID} menuType={this.props.menuType} refresh={this.refreshSearch} />
-        <div className="canvas-wrap">
+        <div className="canvas-wrap" ref={ref => this.wrap = ref}>
           <div className="chart-action">
             {config.action.map(item => {
               if (item.OpenType === 'excelOut') {
@@ -421,7 +415,7 @@
               }
             })}
           </div>
-          <div className={'canvas' + (empty ? ' empty' : '')} style={{height: config.plot.height + 25}} id={this.state.chartId}></div>
+          <div className={'canvas' + (empty ? ' empty' : '')} id={this.state.chartId}></div>
         </div>
         {empty ? <Empty description={false}/> : null}
       </div>
diff --git a/src/tabviews/custom/components/chart/antv-scatter/index.scss b/src/tabviews/custom/components/chart/antv-scatter/index.scss
index c9ad16c..1f52198 100644
--- a/src/tabviews/custom/components/chart/antv-scatter/index.scss
+++ b/src/tabviews/custom/components/chart/antv-scatter/index.scss
@@ -5,11 +5,13 @@
   background-repeat: no-repeat;
   background-size: cover;
   min-height: 100px;
+  display: flex;
+  flex-flow: column;
 
   .canvas-wrap {
     margin: 0 0px;
     position: relative;
-    min-height: calc(100% - 45px);
+    flex: 1;
     .chart-action {
       position: absolute;
       top: 0px;
diff --git a/src/tabviews/custom/components/form/normal-form/index.jsx b/src/tabviews/custom/components/form/normal-form/index.jsx
index f8bb190..54a0d90 100644
--- a/src/tabviews/custom/components/form/normal-form/index.jsx
+++ b/src/tabviews/custom/components/form/normal-form/index.jsx
@@ -44,7 +44,7 @@
 
     let _data = null
     let _sync = false
-    
+
     if (config.wrap.datatype !== 'static') {
       _sync = config.setting.sync === 'true'
 
@@ -186,7 +186,7 @@
 
     if (config.uuid !== menuId) return
 
-    this.loadData(null, 'refresh')
+    this.loadData('refresh')
   }
 
   /**
@@ -195,7 +195,7 @@
    * @param {*} position   // 鍒锋柊浣嶇疆
    * @param {*} btn        // 鎵ц鐨勬寜閽�
    */
-  refreshByButtonResult = (menuId, position, btn) => {
+  refreshByButtonResult = (menuId, position, btn, id) => {
     const { config, group } = this.state
 
     if (group.uuid !== menuId) return
@@ -205,10 +205,14 @@
     }
 
     if (config.wrap.datatype !== 'static' && config.setting) {
-      this.loadData(btn)
-    } else {
-      this.execSuccess(btn)
+      this.loadData()
     }
+
+    if (id) {
+      MKEmitter.emit('resetSelectLine', config.uuid, id, '')
+    }
+
+    this.execSuccess(btn, id)
   }
 
   resetParentParam = (MenuID, id) => {
@@ -221,7 +225,7 @@
     }
   }
 
-  execSuccess = (btn) => {
+  execSuccess = (btn, id) => {
     const { config, group } = this.state
 
     if (config.subcards.length > group.sort) {
@@ -243,8 +247,9 @@
       let newtab = {
         ...menu,
         selected: true,
-        param: {}
+        param: {$BID: id || ''}
       }
+
       let tabs = this.props.tabviews.filter((tab, i) => {
         tab.selected = false
         return tab.MenuID !== newtab.MenuID
@@ -261,7 +266,7 @@
     }
   }
 
-  async loadData (btn, type) {
+  async loadData (type) {
     const { mainSearch, menuType } = this.props
     const { config, arr_field, BID, group } = this.state
 
@@ -269,7 +274,6 @@
       this.setState({
         data: {}
       })
-      btn && this.execSuccess(btn)
       return
     }
 
@@ -277,7 +281,6 @@
 
     let requireFields = searches.filter(item => item.required && item.value === '')
     if (requireFields.length > 0) {
-      btn && this.execSuccess(btn)
       return
     }
 
@@ -291,34 +294,25 @@
     let result = await Api.genericInterface(param)
     if (result.status) {
       let _data = result.data && result.data[0] ? result.data[0] : {}
+      let _group = group
 
-      if (btn) {
-        this.setState({
-          data: _data || {},
-          loading: false
-        })
-        this.execSuccess(btn)
-      } else {
-        let _group = group
-
-        if (type === 'refresh') {
-          _group = config.subcards[0]
-        }
-
-        if (config.wrap.statusControl && _data[config.wrap.statusControl]) {
-          let _status = _data[config.wrap.statusControl]
-          let _groups = config.subcards.filter(item => item.setting.status === _status)[0]
-          _group = _groups || _group
-        }
-        this.setState({
-          group: null,
-          step: _group.sort - 1,
-          data: _data || {},
-          loading: false
-        }, () => {
-          this.setState({group: _group})
-        })
+      if (type === 'refresh') {
+        _group = config.subcards[0]
       }
+
+      if (config.wrap.statusControl && _data[config.wrap.statusControl]) {
+        let _status = _data[config.wrap.statusControl]
+        let _groups = config.subcards.filter(item => item.setting.status === _status)[0]
+        _group = _groups || _group
+      }
+      this.setState({
+        group: null,
+        step: _group.sort - 1,
+        data: _data || {},
+        loading: false
+      }, () => {
+        this.setState({group: _group})
+      })
     } else {
       this.setState({
         loading: false,
@@ -328,7 +322,6 @@
         message: result.message,
         duration: 10
       })
-      btn && this.execSuccess(btn)
     }
   }
 
@@ -388,7 +381,7 @@
           dict={dict}
           data={data}
           action={group}
-          inputSubmit={this.handleOk}
+          inputSubmit={() => this.mkFormSubmit(group.uuid)}
           wrappedComponentRef={(inst) => this.formRef = inst}
         /> : null}
         {group && data ? <div className={'mk-form-action ' + (group.$button || '')}>
diff --git a/src/tabviews/custom/components/form/normal-form/index.scss b/src/tabviews/custom/components/form/normal-form/index.scss
index 7547391..77767fb 100644
--- a/src/tabviews/custom/components/form/normal-form/index.scss
+++ b/src/tabviews/custom/components/form/normal-form/index.scss
@@ -4,7 +4,7 @@
   background-repeat: no-repeat;
   background-size: cover;
   position: relative;
-  min-height: 200px;
+  min-height: 50px;
 
   .mk-normal-form-title {
     display: flex;
@@ -65,17 +65,14 @@
     position: relative;
     text-align: center;
     padding-bottom: 10px;
-  
-    .prev {
-      margin-right: 15px;
-    }
+
     .submit {
       min-width: 70px;
       border: none;
     }
     .skip {
-      position: absolute;
-      right: 5px;
+      float: right;
+      height: auto;
     }
   }
   .mk-form-action.no-button {
@@ -105,7 +102,7 @@
   }
 }
 
-.custom-card-box::after {
+.custom-normal-form-box::after {
   content: ' ';
   display: block;
   clear: both;
diff --git a/src/tabviews/custom/components/form/tab-form/index.jsx b/src/tabviews/custom/components/form/tab-form/index.jsx
new file mode 100644
index 0000000..4082856
--- /dev/null
+++ b/src/tabviews/custom/components/form/tab-form/index.jsx
@@ -0,0 +1,362 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
+import { Spin, notification } 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 asyncSpinComponent from '@/utils/asyncSpinComponent'
+import MKEmitter from '@/utils/events.js'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import { modifyTabview } from '@/store/action'
+import './index.scss'
+
+const MutilForm = asyncSpinComponent(() => import('@/tabviews/zshare/mutilform'))
+const NormalButton = asyncComponent(() => import('@/tabviews/zshare/actionList/normalbutton'))
+
+class TabForm extends Component {
+  static propTpyes = {
+    BID: PropTypes.any,              // 鐖剁骇Id
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+    menuType: PropTypes.any,         // 鑿滃崟绫诲瀷
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: null,                  // 鏁版嵁
+    group: null,
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, BID } = this.props
+    let config = fromJS(this.props.config).toJS()
+
+    let _data = null
+    let _sync = false
+
+    if (config.wrap.datatype !== 'static') {
+      _sync = config.setting.sync === 'true'
+
+      if (_sync && data && data[config.dataName]) {
+        _data = data[config.dataName]
+        if (Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      }
+    } else {
+      _data = {}
+    }
+
+    if (!config.wrap.groupLabel) {
+      if (config.subcards.length > 1) {
+        config.wrap.groupLabel = 'show'
+      } else {
+        config.wrap.groupLabel = 'hidden'
+      }
+    }
+
+    let roleId = sessionStorage.getItem('role_id') || ''
+
+    config.subcards = config.subcards.map(group => {
+      group.subButton.uuid = group.uuid
+      group.subButton.$menuId = group.uuid
+      group.subButton.Ot = 'requiredSgl'
+      group.subButton.$forbid = true
+      group.subButton.OpenType = 'formSubmit'
+      group.subButton.execError = 'never'
+
+      if (group.subButton.enable === 'false') {
+        group.subButton.style.display = 'none'
+        group.$button = 'no-button'
+      }
+
+      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
+    })
+
+    this.setState({
+      sync: _sync,
+      data: _data,
+      group: config.subcards[0],
+      BID: BID || '',
+      config: config,
+      arr_field: config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (config.wrap.datatype !== 'static' && config.setting && config.setting.sync !== 'true' && config.setting.onload === 'true') {
+        this.loadData()
+      }
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('reloadData', this.reloadData)
+    MKEmitter.addListener('mkFormSubmit', this.mkFormSubmit)
+    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('mkFormSubmit', this.mkFormSubmit)
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  /**
+   * @description 鍥捐〃鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   */
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config, group } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = {}
+      let _group = group
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName]
+        if (Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+      }
+
+      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))) {
+      this.setState({}, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  reloadData = (menuId, id) => {
+    const { config } = this.state
+
+    if (config.uuid !== menuId) return
+
+    this.loadData()
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn, id) => {
+    const { config, group } = this.state
+
+    if (group.uuid !== menuId) return
+
+    if (btn.syncComponentId && btn.syncComponentId !== config.uuid && btn.syncComponentId !== config.setting.supModule) {
+      MKEmitter.emit('reloadData', btn.syncComponentId)                        // 鍚岀骇鏍囩鍒锋柊
+    }
+
+    if (id) {
+      MKEmitter.emit('resetSelectLine', config.uuid, id, '')
+    }
+
+    if (config.wrap.datatype !== 'static' && config.setting) {
+      this.loadData()
+    }
+
+    this.execSuccess(btn, id)
+  }
+
+  resetParentParam = (MenuID, id) => {
+    const { config } = this.state
+    if (config.wrap.datatype === 'static' || !config.setting.supModule || config.setting.supModule !== MenuID) return
+    if (id !== this.state.BID) {
+      this.setState({ BID: id }, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  execSuccess = (btn, id) => {
+    if (btn.linkmenu && btn.linkmenu.length > 0) {
+      let menu_id = btn.linkmenu[btn.linkmenu.length - 1]
+      let menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+
+      if (!menu) return
+
+      let newtab = {
+        ...menu,
+        selected: true,
+        param: {$BID: id || ''}
+      }
+
+      let tabs = this.props.tabviews.filter((tab, i) => {
+        tab.selected = false
+        return tab.MenuID !== newtab.MenuID
+      })
+
+      if (this.props.tabviews.length > tabs.length) {
+        this.props.modifyTabview(fromJS(tabs).toJS())
+      }
+
+      this.setState({}, () => {
+        tabs.push(newtab)
+        this.props.modifyTabview(tabs)
+      })
+    }
+  }
+
+  async loadData () {
+    const { mainSearch, menuType } = this.props
+    const { config, arr_field, BID, group } = this.state
+
+    if (config.wrap.datatype === 'static' || (config.setting.supModule && !BID)) {
+      this.setState({
+        data: {}
+      })
+      return
+    }
+
+    let searches = config.setting.useMSearch && mainSearch ? mainSearch : []
+
+    let requireFields = searches.filter(item => item.required && item.value === '')
+    if (requireFields.length > 0) {
+      return
+    }
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      let _data = result.data && result.data[0] ? result.data[0] : {}
+
+      let _group = group
+
+      this.setState({
+        group: null,
+        data: _data || {},
+        loading: false
+      }, () => {
+        this.setState({group: _group})
+      })
+    } else {
+      this.setState({
+        loading: false,
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  mkFormSubmit = (btnId) => {
+    const { group } = this.state
+
+    if (group.uuid !== btnId) return
+
+    this.formRef.handleConfirm().then(res => {
+      MKEmitter.emit('triggerFormSubmit', {menuId: btnId, form: res})
+    })
+  }
+
+  changeGroup = (group) => {
+    this.setState({
+      group: null
+    }, () => {
+      this.setState({group})
+    })
+  }
+
+  render() {
+    const { config, loading, BID, data, group, dict } = this.state
+
+    return (
+      <div className="custom-tab-form-box" style={{...config.style}}>
+        {loading ?
+          <div className="loading-mask">
+            <div className="ant-spin-blur"></div>
+            <Spin />
+          </div> : null
+        }
+        {config.wrap.groupLabel !== 'hidden' ? <div className={'mk-normal-form-title ' + config.wrap.tabtype}>
+          {config.subcards.map(card => (
+            <div key={card.uuid} onClick={() => this.changeGroup(card)} className={'form-title' + (group && group.uuid === card.uuid ? ' active' : '')}>
+              {card.setting.title}
+            </div>))
+          }
+        </div> : null}
+        {group && data ? <MutilForm
+          BID={BID}
+          dict={dict}
+          data={data}
+          action={group}
+          inputSubmit={() => this.mkFormSubmit(group.uuid)}
+          wrappedComponentRef={(inst) => this.formRef = inst}
+        /> : null}
+        {group && data ? <div className={'mk-form-action ' + (group.$button || '')}>
+          <NormalButton
+            BID={BID}
+            position="form"
+            btn={group.subButton}
+            setting={config.setting}
+            columns={config.columns}
+            selectedData={[data]}
+          />
+        </div> : null}
+      </div>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    permMenus: state.permMenus,
+    tabviews: state.tabviews,
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(TabForm)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/form/tab-form/index.scss b/src/tabviews/custom/components/form/tab-form/index.scss
new file mode 100644
index 0000000..f1fabf1
--- /dev/null
+++ b/src/tabviews/custom/components/form/tab-form/index.scss
@@ -0,0 +1,98 @@
+.custom-tab-form-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  position: relative;
+  min-height: 50px;
+
+  .mk-normal-form-title {
+    display: flex;
+    line-height: 36px;
+    min-height: 36px;
+    margin-bottom: 20px;
+    font-size: 16px;
+    .form-title {
+      position: relative;
+      flex: 1;
+      text-align: center;
+      cursor: pointer;
+    }
+  }
+  .mk-normal-form-title.mkbtn {
+    .form-title {
+      background: #ffffff;
+      color: #1890ff;
+      border: 1px solid #1890ff;
+      transition: all 0.3s;
+    }
+    .form-title.active {
+      background: #1890ff;
+      color: #ffffff;
+    }
+    .form-title:first-child {
+      border-radius: 4px 0px 0px 4px;
+    }
+    .form-title:last-child {
+      border-radius: 0px 4px 4px 0px;
+    }
+  }
+  .mk-normal-form-title.mktab {
+    border-bottom: 1px solid #d9d9d9;
+    .form-title {
+      color: rgba(0, 0, 0, 0.85);
+      border-bottom: 2px solid transparent;
+      transition: all 0.3s;
+    }
+    .form-title.active {
+      color: #1890ff;
+      border-bottom-color: #1890ff;
+    }
+  }
+
+  .mk-form-action {
+    position: relative;
+    text-align: center;
+    padding-bottom: 10px;
+
+    .submit {
+      min-width: 70px;
+      border: none;
+    }
+    .skip {
+      float: right;
+      height: auto;
+    }
+  }
+  .mk-form-action.no-button {
+    padding: 0;
+    height: 0;
+  }
+  
+  .loading-mask {
+    position: absolute;
+    left: 40px;
+    top: 0;
+    right: 40px;
+    bottom: 0px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    text-align: justify;
+    z-index: 1;
+
+    .ant-spin-blur {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      opacity: 0.5;
+      background: #ffffff;
+    }
+  }
+}
+
+.custom-tab-form-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
diff --git a/src/tabviews/custom/components/group/normal-group/index.jsx b/src/tabviews/custom/components/group/normal-group/index.jsx
index 0ffbce3..05c8ba6 100644
--- a/src/tabviews/custom/components/group/normal-group/index.jsx
+++ b/src/tabviews/custom/components/group/normal-group/index.jsx
@@ -25,9 +25,11 @@
 const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
 const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 const NormalForm = asyncComponent(() => import('@/tabviews/custom/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/tabviews/custom/components/form/tab-form'))
 const NormalTree = asyncComponent(() => import('@/tabviews/custom/components/tree/antd-tree'))
 const CarouselDataCard = asyncComponent(() => import('@/tabviews/custom/components/carousel/data-card'))
 const CarouselPropCard = asyncComponent(() => import('@/tabviews/custom/components/carousel/prop-card'))
+const Balcony = asyncComponent(() => import('@/tabviews/custom/components/card/balcony'))
 
 class TabTransfer extends Component {
   static propTpyes = {
@@ -90,7 +92,9 @@
 
   UNSAFE_componentWillReceiveProps(nextProps) {
     if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
-      this.setState({mainSearch: fromJS(nextProps.mainSearch).toJS()})
+      this.setState({mainSearch: null}, () => {
+        this.setState({mainSearch: fromJS(nextProps.mainSearch).toJS()})
+      })
     }
   }
 
@@ -130,11 +134,9 @@
     if (!config || !config.components || config.components.length === 0) return (<Empty description={false} />)
 
     return config.components.map(item => {
-      let _bid = ''
-      if (bids && item.setting && item.setting.supModule) {
-        _bid = bids[item.setting.supModule] || ''
-      } else if (!bids && BID && (!item.setting || !item.setting.supModule)) {
-        _bid = BID
+      let _bid = BID || ''
+      if (item.setting && item.setting.supModule) {
+        _bid = bids ? bids[item.setting.supModule] || '' : ''
       }
 
       if (item.type === 'bar' || item.type === 'line') {
@@ -155,10 +157,16 @@
             <AntvDashboard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
-      } else if (item.type === 'form') {
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
         return (
           <Col span={item.width} key={item.uuid}>
             <NormalForm config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <TabForm config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
       } else if (item.type === 'scatter') {
@@ -221,6 +229,12 @@
             <SandBox config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'balcony') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <Balcony config={item} data={data} BID={_bid} menuType={menuType} />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/tabviews/custom/components/share/normalTable/index.jsx b/src/tabviews/custom/components/share/normalTable/index.jsx
index 2d53f40..46eba22 100644
--- a/src/tabviews/custom/components/share/normalTable/index.jsx
+++ b/src/tabviews/custom/components/share/normalTable/index.jsx
@@ -3,7 +3,7 @@
 import md5 from 'md5'
 import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Table, Typography, Icon, Col, Switch } from 'antd'
+import { Table, Typography, Icon, Col, Switch, message } from 'antd'
 
 import { modifyTabview } from '@/store/action'
 import asyncComponent from '@/utils/asyncComponent'
@@ -275,6 +275,7 @@
 
       let cols = 24 / (col.picSort || 1)
       let paddingTop = '100%'
+      let scale = col.scale === 'true'
 
       if (PicRadio[col.lenWidRadio]) {
         paddingTop = PicRadio[col.lenWidRadio]
@@ -284,7 +285,10 @@
         <div>
           {photos.map((url, i) => (
             <Col key={i} span={cols}>
-              <div className="ant-mk-picture" style={{paddingTop, backgroundImage: `url('${url}')`}}></div>
+              <div className={'ant-mk-picture' + (scale ? ' scale' : '')} onClick={() => {
+                if (!scale) return
+                MKEmitter.emit('mkImageScale', url, photos)
+              }} style={{paddingTop, backgroundImage: `url('${url}')`}}></div>
             </Col>
           ))}
         </div>
@@ -496,6 +500,7 @@
   }
 
   componentDidMount () {
+    MKEmitter.addListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.addListener('resetTable', this.resetTable)
   }
 
@@ -506,7 +511,39 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('mkCheckAll', this.mkCheckAll)
     MKEmitter.removeListener('resetTable', this.resetTable)
+  }
+
+  mkCheckAll = (menuId, checked) => {
+    const { MenuID, data } = this.props
+
+    if (MenuID !== menuId) return
+
+    if (checked) {
+      this.setState({
+        activeIndex: '',
+        selectedRowKeys: data.map((item, index) => index)
+      })
+
+      this.props.chgSelectData(data)
+
+      MKEmitter.emit('resetSelectLine', MenuID, '', '')
+      MKEmitter.emit('syncBalconyData', MenuID, data, data.length > 0)
+      if (data.length === 0) {
+        message.warning('鏈幏鍙栧埌鏁版嵁锛�')
+      }
+    } else {
+      this.setState({
+        activeIndex: '',
+        selectedRowKeys: [],
+      })
+
+      this.props.chgSelectData([])
+  
+      MKEmitter.emit('resetSelectLine', MenuID, '', '')
+      MKEmitter.emit('syncBalconyData', MenuID, [], false)
+    }
   }
 
   // 瀛楁閫忚
@@ -585,7 +622,7 @@
    * 
    */
   onSelectChange = selectedRowKeys => {
-    const { setting } = this.props
+    const { setting, MenuID, data } = this.props
 
     let index = ''
     let _activeIndex = null
@@ -604,13 +641,16 @@
     let selects = this.props.data.filter((item, _index) => selectedRowKeys.includes(_index))
 
     this.props.chgSelectData(selects)
+    if (setting.$hasSyncModule) {
+      MKEmitter.emit('syncBalconyData', MenuID, selects, data.length === selects.length)
+    }
   }
 
   /**
    * @description 鐐瑰嚮鏁磋锛岃Е鍙戝垏鎹紝 鍒ゆ柇鏄惁鍙�夛紝鍗曢�夋垨澶氶�夛紝杩涜瀵瑰簲鎿嶄綔
    */
   changeRow = (record, index) => {
-    const { setting } = this.props
+    const { setting, MenuID, data } = this.props
 
     if (!setting.tableType || this.state.pickup) return
     
@@ -640,6 +680,10 @@
     let selects = this.props.data.filter((item, _index) => newkeys.includes(_index))
     
     this.props.chgSelectData(selects)
+
+    if (setting.$hasSyncModule) {
+      MKEmitter.emit('syncBalconyData', MenuID, selects, data.length === selects.length)
+    }
   }
 
   changeTable = (pagination, filters, sorter) => {
diff --git a/src/tabviews/custom/components/share/normalTable/index.scss b/src/tabviews/custom/components/share/normalTable/index.scss
index 489c533..9fadcde 100644
--- a/src/tabviews/custom/components/share/normalTable/index.scss
+++ b/src/tabviews/custom/components/share/normalTable/index.scss
@@ -68,6 +68,9 @@
           background-size: cover;
           margin: 2px;
         }
+        .ant-mk-picture.scale {
+          cursor: zoom-in;
+        }
         .action-col {
           .ant-btn > .anticon + span {
             margin-left: 3px;
diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx
index 62512b7..0fad518 100644
--- a/src/tabviews/custom/components/share/tabtransfer/index.jsx
+++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -28,9 +28,11 @@
 const BraftEditor = asyncComponent(() => import('@/tabviews/custom/components/editor/braft-editor'))
 const SandBox = asyncComponent(() => import('@/tabviews/custom/components/code/sand-box'))
 const NormalForm = asyncComponent(() => import('@/tabviews/custom/components/form/normal-form'))
+const TabForm = asyncComponent(() => import('@/tabviews/custom/components/form/tab-form'))
 const NormalTree = asyncComponent(() => import('@/tabviews/custom/components/tree/antd-tree'))
 const CarouselDataCard = asyncComponent(() => import('@/tabviews/custom/components/carousel/data-card'))
 const CarouselPropCard = asyncComponent(() => import('@/tabviews/custom/components/carousel/prop-card'))
+const Balcony = asyncComponent(() => import('@/tabviews/custom/components/card/balcony'))
 
 class TabTransfer extends Component {
   static propTpyes = {
@@ -106,7 +108,9 @@
     const { self } = this.state
     
     if (!self && nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
-      this.setState({mainSearch: fromJS(nextProps.mainSearch).toJS()})
+      this.setState({mainSearch: null}, () => {
+        this.setState({mainSearch: fromJS(nextProps.mainSearch).toJS()})
+      })
     }
   }
 
@@ -152,7 +156,7 @@
     if (!config || !config.components || config.components.length === 0) return (<Empty description={false} />)
 
     return config.components.map(item => {
-      let BID = ''
+      let BID = this.props.BID || ''
       if (item.setting && item.setting.supModule) {
         BID = bids[item.setting.supModule] || ''
       }
@@ -184,13 +188,13 @@
       } else if (item.type === 'search') {
         return (
           <Col span={item.width} key={item.uuid}>
-            <MainSearch config={item} menuType={menuType} refreshdata={this.resetSearch} />
+            <MainSearch config={item} BID={BID} menuType={menuType} refreshdata={this.resetSearch} />
           </Col>
         )
       } else if (item.type === 'tabs') {
         return (
           <Col span={item.width} key={item.uuid}>
-            <AntvTabs config={item} bids={bids} mainSearch={mainSearch} menuType={menuType} />
+            <AntvTabs config={item} BID={BID} bids={bids} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'datacard') {
@@ -232,13 +236,19 @@
       } else if (item.type === 'group' && item.subtype === 'normalgroup') {
         return (
           <Col span={item.width} key={item.uuid}>
-            <NormalGroup config={item} bids={bids} mainSearch={mainSearch} menuType={menuType} />
+            <NormalGroup config={item} BID={BID} bids={bids} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
-      } else if (item.type === 'form') {
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
         return (
           <Col span={item.width} key={item.uuid}>
             <NormalForm config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <TabForm config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
       } else if (item.type === 'tree') {
@@ -259,6 +269,12 @@
             <SandBox config={item} data={data} BID={BID} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'balcony') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <Balcony config={item} data={data} BID={BID} menuType={menuType} />
+          </Col>
+        )
       } else {
         return null
       }
diff --git a/src/tabviews/custom/components/table/normal-table/index.jsx b/src/tabviews/custom/components/table/normal-table/index.jsx
index 2c432a8..cd57cff 100644
--- a/src/tabviews/custom/components/table/normal-table/index.jsx
+++ b/src/tabviews/custom/components/table/normal-table/index.jsx
@@ -138,7 +138,7 @@
    */
   async loadmaindata (reset, repage) {
     const { mainSearch } = this.props
-    const { setting, config, arr_field, search, orderBy, BID, pageIndex, pageSize } = this.state
+    const { setting, config, arr_field, search, orderBy, BID, pageIndex, pageSize, BData } = this.state
 
     if (setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
       this.setState({
@@ -148,6 +148,9 @@
       })
       MKEmitter.emit('resetSelectLine', config.uuid, '', '') // 骞挎挱鏁版嵁鍒囨崲
       reset && MKEmitter.emit('resetTable', config.uuid, repage) // 鍒楄〃閲嶇疆
+      if (setting.$hasSyncModule) {
+        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+      }
       return
     }
 
@@ -173,10 +176,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = config.name || ''
-    }
-
     let result = await Api.genericInterface(param)
     if (result.status) {
       MKEmitter.emit('resetSelectLine', config.uuid, '', '') // 骞挎挱鏁版嵁鍒囨崲
@@ -192,6 +191,7 @@
           item.key = index
           item.$$uuid = item[setting.primaryKey] || ''
           item.$$BID = BID || ''
+          item.$$BData = BData || ''
           item.$Index = start + index + ''
           return item
         }),
@@ -199,6 +199,9 @@
         total: result.total,
         loading: false
       })
+      if (setting.$hasSyncModule) {
+        MKEmitter.emit('syncBalconyData', config.uuid, [], false)
+      }
     } else {
       this.setState({
         loading: false
@@ -216,7 +219,7 @@
    */ 
   async loadmainLinedata (id) {
     const { mainSearch } = this.props
-    const { setting, config, arr_field, search, orderBy, BID, pageIndex, pageSize } = this.state
+    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) { // 涓昏〃鎼滅储鏉′欢
@@ -235,10 +238,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType, id)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = config.name || ''
-    }
-
     let result = await Api.genericInterface(param)
     if (result.status) {
       let data = fromJS(this.state.data).toJS()
@@ -247,6 +246,7 @@
         let _data = result.data[0] || {}
         _data.$$uuid = _data[setting.primaryKey] || ''
         _data.$$BID = BID || ''
+        _data.$$BData = BData || ''
         try {
           data = data.map(item => {
             if (item.$$uuid === _data.$$uuid) {
@@ -317,10 +317,6 @@
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, config.statFields, searches, _orderBy, BID, this.props.menuType)
-
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = config.name || ''
-    }
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
@@ -501,14 +497,6 @@
     }
   }
 
-  getSyncData = (syncModule, btnId) => {
-    const { config, selectedData } = this.state
-
-    if (config.uuid !== syncModule) return
-
-    MKEmitter.emit('triggerBtnId', btnId, (selectedData || []))
-  }
-
   UNSAFE_componentWillReceiveProps(nextProps) {
     const { sync, config, BID } = this.state
 
@@ -539,7 +527,6 @@
 
   componentDidMount () {
     MKEmitter.addListener('reloadData', this.reloadData)
-    MKEmitter.addListener('getSyncData', this.getSyncData)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
     MKEmitter.addListener('queryModuleParam', this.queryModuleParam)
     MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
@@ -553,7 +540,6 @@
       return
     }
     MKEmitter.removeListener('reloadData', this.reloadData)
-    MKEmitter.removeListener('getSyncData', this.getSyncData)
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
     MKEmitter.removeListener('queryModuleParam', this.queryModuleParam)
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
diff --git a/src/tabviews/custom/components/tabs/antv-tabs/index.jsx b/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
index 5cff8f8..bbad0d0 100644
--- a/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
+++ b/src/tabviews/custom/components/tabs/antv-tabs/index.jsx
@@ -12,6 +12,7 @@
 
 class antvTabs extends Component {
   static propTpyes = {
+    BID: PropTypes.any,              // 椤甸潰BID
     bids: PropTypes.any,             // 鐖剁骇Id闆�
     config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
     mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
@@ -62,7 +63,7 @@
   }
 
   render() {
-    const { mainSearch } = this.props
+    const { mainSearch, BID } = this.props
     const { tabs, bids } = this.state
 
     return (
@@ -70,7 +71,7 @@
         <Tabs defaultActiveKey="1" tabPosition={tabs.setting.position} type={tabs.setting.tabStyle}>
           {tabs.subtabs.map(tab => (
             <TabPane tab={<span>{tab.icon ? <Icon type={tab.icon} /> : null}{tab.label}</span>} key={tab.uuid}>
-              <TabTransfer config={tab} bids={bids} mainSearch={mainSearch}/>
+              <TabTransfer BID={BID} config={tab} bids={bids} mainSearch={mainSearch}/>
             </TabPane>
           ))}
         </Tabs>
diff --git a/src/tabviews/custom/components/tree/antd-tree/index.scss b/src/tabviews/custom/components/tree/antd-tree/index.scss
index 13f499c..8e3a725 100644
--- a/src/tabviews/custom/components/tree/antd-tree/index.scss
+++ b/src/tabviews/custom/components/tree/antd-tree/index.scss
@@ -4,7 +4,7 @@
   background-position: center center;
   background-repeat: no-repeat;
   background-size: cover;
-  min-height: 100px;
+  min-height: 180px;
 
   .tree-header {
     position: relative;
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index 8cf4cc6..b23c7a5 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -24,6 +24,7 @@
 const DataCard = asyncComponent(() => import('./components/card/data-card'))
 const PropCard = asyncComponent(() => import('./components/card/prop-card'))
 const NormalForm = asyncComponent(() => import('./components/form/normal-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'))
@@ -130,7 +131,8 @@
 
       // 鏉冮檺杩囨护
       let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
-      config.components = this.filterComponent(config.components, roleId, permAction, permMenus)
+      let balMap = new Map()
+      config.components = this.filterComponent(config.components, roleId, permAction, permMenus, balMap)
       
       // 鑾峰彇涓绘悳绱㈡潯浠�
       let mainSearch = []
@@ -190,7 +192,11 @@
         })
       }
 
-      config.components = this.formatSetting(config.components, params, mainSearch, inherit, regs)
+      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,
@@ -280,6 +286,8 @@
         })
         return
       }
+
+      item.MenuName = config.MenuName || ''
 
       inters.push(item)
     })
@@ -437,8 +445,18 @@
     })
   }
 
-  filterComponent = (components, roleId, permAction, permMenus) => {
+  filterComponent = (components, roleId, permAction, permMenus, balMap) => {
     return components.filter(item => {
+      
+      if (item.style && item.style.boxShadow) {
+        delete item.style.hShadow
+        delete item.style.vShadow
+        delete item.style.shadowBlur
+        delete item.style.shadowColor
+      }
+
+      item.$menuname = this.props.MenuName + '-' + (item.name || '')
+
       if (item.type === 'tabs') {
         if (
           item.setting.blacklist && item.setting.blacklist.length > 0 &&
@@ -458,7 +476,7 @@
         })
 
         item.subtabs = item.subtabs.map(tab => {
-          tab.components = this.filterComponent(tab.components, roleId, permAction, permMenus)
+          tab.components = this.filterComponent(tab.components, roleId, permAction, permMenus, balMap)
           return tab
         })
 
@@ -481,7 +499,7 @@
           return false
         }
 
-        item.components = this.filterComponent(item.components, roleId, permAction, permMenus)
+        item.components = this.filterComponent(item.components, roleId, permAction, permMenus, balMap)
       } else if (['pie', 'bar', 'line', 'dashboard', 'scatter'].includes(item.type)) {
         if (
           item.plot.blacklist && item.plot.blacklist.length > 0 &&
@@ -548,7 +566,7 @@
       let tabId = this.props.Tab ? this.props.Tab.uuid : '' // 寮圭獥鏍囩鎸夐挳Id
       if (item.action && item.action.length > 0) {
         item.action = item.action.filter(cell => {
-          cell.logLabel = item.name + '-' + cell.label
+          cell.logLabel = item.$menuname + '-' + cell.label
           cell.ContainerId = this.state.ContainerId
           cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
           cell.$menuId = item.uuid
@@ -572,15 +590,17 @@
         item.subcards && item.subcards.forEach(card => {
           let _hasheight = card.style.height && card.style.height !== 'auto'
 
-          if (card.style.shadow) { // 鍗$墖闃村奖
-            card.style.boxShadow = '0 0 4px ' + card.style.shadow
-            delete card.style.shadow
+          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') {
-              cell.logLabel = item.name + '-' + cell.label
-              cell.Ot = 'requiredSgl'
+              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
@@ -602,8 +622,8 @@
           })
           card.backElements = card.backElements.filter(cell => {
             if (cell.eleType === 'button') {
-              cell.logLabel = item.name + '-' + cell.label
-              cell.Ot = 'requiredSgl'
+              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
@@ -624,9 +644,13 @@
           })
         })
       } else if (item.type === 'balcony') {
+        if (item.wrap.linkType === 'sync') {
+          item.wrap.syncModuleId = item.wrap.syncModule.pop()
+          balMap.set(item.wrap.syncModuleId, true)
+        }
         item.elements = item.elements.filter(cell => {
           if (cell.eleType === 'button') {
-            cell.logLabel = item.name + '-' + cell.label
+            cell.logLabel = item.$menuname + '-' + cell.label
             cell.ContainerId = this.state.ContainerId
             cell.syncComponentId = cell.syncComponent ? (cell.syncComponent.pop() || '') : ''
             cell.$menuId = item.uuid
@@ -647,8 +671,8 @@
           let _hasheight = card.style.height && card.style.height !== 'auto'
           card.elements = card.elements.filter(cell => {
             if (cell.eleType === 'button') {
-              cell.logLabel = item.name + '-' + cell.label
-              cell.Ot = 'requiredSgl'
+              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
@@ -673,8 +697,8 @@
         item.cols = item.cols.filter(col => {
           if (col.type !== 'action') return true
           col.elements = col.elements.filter(cell => {
-            cell.logLabel = item.name + '-' + cell.label
-            cell.Ot = 'requiredSgl'
+            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
@@ -715,6 +739,44 @@
     })
   }
 
+  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)
 
@@ -734,17 +796,17 @@
   }
 
   // 鏍煎紡鍖栭粯璁よ缃�
-  formatSetting = (components, params, mainSearch, inherit, regs) => {
+  formatSetting = (components, params, mainSearch, inherit, regs, balMap) => {
     return components.map(component => {
       if (component.type === 'tabs') {
         component.subtabs = component.subtabs.map(tab => {
-          tab.components = this.formatSetting(tab.components, [], [], inherit, regs)
+          tab.components = this.formatSetting(tab.components, [], [], inherit, regs, balMap)
           tab = {...tab, ...inherit}
           return tab
         })
         return component
       } else if (component.type === 'group') {
-        component.components = this.formatSetting(component.components, [], [], inherit, regs)
+        component.components = this.formatSetting(component.components, [], [], inherit, regs, balMap)
         component = {...component, ...inherit}
         return component
       }
@@ -777,7 +839,7 @@
         }
       })
       delete component.scripts
-      component.setting.$name = component.name || ''
+      component.setting.$name = component.$menuname || ''
       component.setting.execute = component.setting.execute !== 'false'  // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
       component.setting.laypage = component.setting.laypage === 'true'   // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
 
@@ -830,6 +892,11 @@
         }
       } else if (component.floor === 1) {
         component.setting.sync = 'false'
+      }
+
+      if (balMap.has(component.uuid)) {
+        component.setting.$hasSyncModule = true
+        balMap.set(component.uuid, component)
       }
 
       return component
@@ -917,7 +984,8 @@
       viewlost: false,      // 椤甸潰涓㈠け锛�1銆佹湭鑾峰彇鍒伴厤缃�-椤甸潰涓㈠け锛�2銆侀〉闈㈡湭鍚敤
       config: null,         // 椤甸潰閰嶇疆淇℃伅锛屽寘鎷粍浠剁瓑
       loading: false,       // 鍒楄〃鏁版嵁鍔犺浇涓�
-      shortcuts: null
+      shortcuts: null,
+      data: ''
     }, () => {
       this.loadconfig()
     })
@@ -965,10 +1033,16 @@
             <AntvDashboard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
-      } else if (item.type === 'form') {
+      } else if (item.type === 'form' && item.subtype === 'stepform') {
         return (
           <Col span={item.width} key={item.uuid}>
             <NormalForm config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'form' && item.subtype === 'tabform') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <TabForm config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
       } else if (item.type === 'search') {
@@ -980,7 +1054,7 @@
       } else if (item.type === 'tabs') {
         return (
           <Col span={item.width} key={item.uuid}>
-            <AntvTabs config={item} mainSearch={mainSearch} />
+            <AntvTabs config={item} BID={BID} mainSearch={mainSearch} />
           </Col>
         )
       } else if (item.type === 'card' && item.subtype === 'datacard') {
@@ -998,7 +1072,7 @@
       } else if (item.type === 'balcony') {
         return (
           <Col span={item.width} key={item.uuid}>
-            <Balcony menu={config} config={item} data={data} BID={_bid} menuType={menuType} />
+            <Balcony config={item} data={data} BID={_bid} menuType={menuType} />
           </Col>
         )
       } else if (item.type === 'carousel' && item.subtype === 'datacard') {
diff --git a/src/tabviews/formtab/actionList/index.jsx b/src/tabviews/formtab/actionList/index.jsx
index 698fcf1..ac97ecd 100644
--- a/src/tabviews/formtab/actionList/index.jsx
+++ b/src/tabviews/formtab/actionList/index.jsx
@@ -305,14 +305,14 @@
       notification.error({
         top: 92,
         message: res.message || res.ErrMesg,
-        duration: btn.errorTime || 10
+        duration: 10
       })
     } else if (res.ErrCode === 'F') {
       notification.error({
         className: 'notification-custom-error',
         top: 92,
         message: res.message || res.ErrMesg,
-        duration: btn.errorTime || 10
+        duration: 10
       })
     } else if (res.ErrCode === 'NM') {
       message.error(res.message || res.ErrMesg)
diff --git a/src/tabviews/home/index.jsx b/src/tabviews/home/index.jsx
index dfb3f7d..ae14a8f 100644
--- a/src/tabviews/home/index.jsx
+++ b/src/tabviews/home/index.jsx
@@ -78,7 +78,7 @@
     if (loading || waiting) {
       return (<div className="home-loading-view" style={{background: background}}><Spin className="home-box-spin" size="large" /></div>)
     } else if (view === 'custom') {
-      return (<CustomPage MenuID={this.props.MenuID}/>)
+      return (<CustomPage MenuID={this.props.MenuID} MenuName="棣栭〉"/>)
     } else {
       return (<DefaultHome />)
     }
diff --git a/src/tabviews/rolemanage/index.jsx b/src/tabviews/rolemanage/index.jsx
index 99b8022..9b138ec 100644
--- a/src/tabviews/rolemanage/index.jsx
+++ b/src/tabviews/rolemanage/index.jsx
@@ -1,9 +1,11 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty } from 'antd'
+import { Card, Col, Row, Icon, Menu, notification, Spin, Input, Tabs, Button, Tree, Empty, Select } from 'antd'
+import md5 from 'md5'
 
 import Api from '@/api'
+import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -20,14 +22,16 @@
 export default class RoleManage extends Component {
   static propTpyes = {
     MenuNo: PropTypes.string, // 鑿滃崟鍙傛暟
-    MenuID: PropTypes.string // 鑿滃崟Id
+    MenuID: PropTypes.string  // 鑿滃崟Id
   }
 
   state = {
     dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     loading: true,
     loadingTree: false,
+    loadingAppTree: false,
     roleList: null,
+    filterRoleList: [],
     selectRoleId: '',
     mainMenus: null,
     menuTrees: null,
@@ -36,9 +40,17 @@
     selectMenuTrees: null,
     selectMenuOpenKeys: [],
     primarykey: '',
-    tabKey: '',
     submitloading: false,
-    initCheckKeys: null
+    initCheckKeys: null,
+    activeKey: 'manage',
+    appTrees: null,
+    appCheckedKeys: [],
+    appOpenKeys: [],
+    applist: [],
+    selectApp: null,
+    selectSubApp: null,
+    selectAppTrees: null,
+    selectAppOpenKeys: [],
   }
 
   /**
@@ -52,7 +64,8 @@
 
     if (result.status) {
       this.setState({
-        roleList: result.data
+        roleList: result.data,
+        filterRoleList: result.data || []
       })
     } else {
       notification.warning({
@@ -61,6 +74,37 @@
         duration: 5
       })
     }
+  }
+
+  getAppList = () => {
+    if (options.sysType !== 'local') return
+
+    let param = {
+      func: 's_get_kei'
+    }
+
+    Api.getSystemConfig(param).then(result => {
+      if (result.status) {
+        let applist = result.data.map(item => {
+          item.sublist = item.data_detail || []
+          item.sublist = item.sublist.map(cell => {
+            cell.ID = cell.d_id
+            return cell
+          })
+
+          return item
+        })
+        let selectApp = applist[0] || null
+
+        this.setState({ applist, selectApp })
+      } else {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
   }
 
   /**
@@ -103,12 +147,12 @@
    * @description 鑾峰彇鎵�鏈夎彍鍗曡妭鐐癸紝褰㈡垚鏉冮檺鏍�
    */
   getAllMenuList = async () => {
-    const { selectRoleId, mainMenus, tabKey } = this.state
+    const { selectRoleId, mainMenus } = this.state
 
     let param = {
       func: 's_rolemenu_get_FunMenu',
       RoleID: selectRoleId,
-      SelectedType: tabKey
+      SelectedType: ''
     }
 
     let result = await Api.getSystemConfig(param)
@@ -153,61 +197,147 @@
    * @description 閫夋嫨瑙掕壊涓斿瓨鍦ㄦ潈闄愭爲鏃讹紝鑾峰彇宸插垎閰嶇粨鏋�
    */
   getSelectMenuList = async () => {
-    const { selectRoleId, menuTrees, tabKey } = this.state
-    if (!menuTrees) return
+    const { selectRoleId, menuTrees, activeKey } = this.state
+    if (!menuTrees || !selectRoleId || activeKey !== 'manage') return
 
     let param = {
       func: 's_rolemenu_get_Menulist',
       RoleID: selectRoleId
     }
 
+    this.setState({
+      loadingTree: true
+    })
+
     let result = await Api.getSystemConfig(param)
 
     if (result.status) {
       let _openKeys = []
 
-      if (tabKey === '') {
-        let _initKeys = result.data.map(item => item.MenuID)
-        _initKeys = Array.from(new Set(_initKeys))
+      let _initKeys = result.data.map(item => item.MenuID)
+      _initKeys = Array.from(new Set(_initKeys))
 
-        selectMap = new Map()
-        this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
+      selectMap = new Map()
+      this.getCheckedKeys(fromJS(menuTrees).toJS(), _initKeys)
 
-        if (menuTrees[0]) {
-          if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
-            this.getOpenNode(menuTrees[0].children[0], _openKeys)
-          } else {
-            this.getOpenNode(menuTrees[0], _openKeys)
-          }
+      if (menuTrees[0]) {
+        if (menuTrees[0].key === 'PC' && menuTrees[0].children) {
+          this.getOpenNode(menuTrees[0].children[0], _openKeys)
+        } else {
+          this.getOpenNode(menuTrees[0], _openKeys)
         }
-
-        this.setState({
-          loadingTree: false,
-          initCheckKeys: _initKeys,
-          checkedKeys: Array.from(selectMap.keys()),
-          menuOpenKeys: _openKeys
-        })
-      } else {
-        let Keys = result.data.map(item => item.MenuID)
-        let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
-
-        if (_tree[0]) {
-          if (_tree[0].key === 'PC' && _tree[0].children) {
-            this.getOpenNode(_tree[0].children[0], _openKeys)
-          } else {
-            this.getOpenNode(_tree[0], _openKeys)
-          }
-        }
-
-        this.setState({
-          loadingTree: false,
-          selectMenuTrees: _tree,
-          selectMenuOpenKeys: _openKeys
-        })
       }
+
+      let _openkeys = []
+      let Keys = result.data.map(item => item.MenuID)
+      let _tree = this.getSelectTree(fromJS(menuTrees).toJS(), Keys)
+
+      if (_tree[0]) {
+        if (_tree[0].key === 'PC' && _tree[0].children) {
+          this.getOpenNode(_tree[0].children[0], _openkeys)
+        } else {
+          this.getOpenNode(_tree[0], _openkeys)
+        }
+      }
+
+      this.setState({
+        loadingTree: false,
+        initCheckKeys: _initKeys,
+        checkedKeys: Array.from(selectMap.keys()),
+        menuOpenKeys: _openKeys,
+        selectMenuTrees: _tree,
+        selectMenuOpenKeys: _openkeys
+      })
     } else {
       this.setState({
         loadingTree: false
+      })
+      notification.warning({
+        top: 92,
+        message: result.message,
+        duration: 5
+      })
+    }
+  }
+
+  /**
+   * @description 閫夋嫨瑙掕壊涓斿瓨鍦ㄦ潈闄愭爲鏃讹紝鑾峰彇宸插垎閰嶇粨鏋�
+   */
+  getSelectAppNodeList = async () => {
+    const { selectRoleId, selectSubApp, selectApp, appTrees, activeKey } = this.state
+
+    if (!appTrees || !selectRoleId || activeKey !== 'app' || !selectSubApp || !selectApp) return
+
+    let param = {
+      func: 's_rolemenu_get_Menulist',
+      RoleID: selectRoleId,
+      TypeCharOne: selectApp.kei_no,
+      typename: selectSubApp.typename,
+      lang: selectSubApp.lang
+    }
+
+    this.setState({
+      loadingAppTree: true
+    })
+
+    let result = await Api.getSystemConfig(param)
+
+    if (result.status) {
+      let _initKeys = result.data.map(item => item.MenuID)
+      _initKeys = Array.from(new Set(_initKeys))
+      let _checkedKeys = []
+
+      let getCheckedKeys = (parents) => {
+        parents.forEach(item => {
+          if (_initKeys.includes(item.key)) {
+            if (item.children && item.children.length > 0) {
+              getCheckedKeys(item.children)
+            } else {
+              _checkedKeys.push(item.key)
+            }
+          }
+        })
+      }
+
+      getCheckedKeys(appTrees)
+
+      let _openKeys = []
+      let getOpenNode = (parentNode) => {
+        if (parentNode && parentNode.children && parentNode.children.length > 0) {
+          _openKeys.push(parentNode.key)
+          parentNode.children.forEach(node => {
+            getOpenNode(node)
+          })
+        }
+      }
+      getOpenNode(appTrees[0])
+
+      let Keys = result.data.map(item => item.MenuID)
+      let _tree = this.getSelectTree(fromJS(appTrees).toJS(), Keys)
+
+
+      let _openkeys = []
+      let _getOpenNode = (parentNode) => {
+        if (parentNode && parentNode.children && parentNode.children.length > 0) {
+          _openkeys.push(parentNode.key)
+          parentNode.children.forEach(node => {
+            _getOpenNode(node)
+          })
+        }
+      }
+      _getOpenNode(_tree[0])
+
+      this.setState({
+        loadingAppTree: false,
+        appInitCheckKeys: _initKeys,
+        appCheckedKeys: _checkedKeys,
+        appOpenKeys: _openKeys,
+        selectAppTrees: _tree,
+        selectAppOpenKeys: _openkeys
+      })
+    } else {
+      this.setState({
+        loadingAppTree: false
       })
       notification.warning({
         top: 92,
@@ -334,10 +464,10 @@
     if (selectRoleId === role.RoleID) return
 
     this.setState({
-      selectRoleId: role.RoleID,
-      loadingTree: true
+      selectRoleId: role.RoleID
     }, () => {
       this.getSelectMenuList()
+      this.getSelectAppNodeList()
     })
   }
 
@@ -369,23 +499,13 @@
   }
 
   /**
-   * @description 宸插垎閰嶄笌鏈垎閰嶅垏鎹�
+   * @description 鑺傜偣閫夋嫨浜嬩欢
    */
-  changeTab = (key) => {
-    const { selectRoleId } = this.state
-
+  onAppCheck = (checkedKeys, info) => {
     this.setState({
-      tabKey: key === 'selected' ? key : '',
-      loadingTree: true
-    }, () => {
-      if (selectRoleId) {
-        this.getSelectMenuList()
-      } else {
-        this.setState({
-          selectMenuTrees: [],
-          loadingTree: false
-        })
-      }
+      appCheckedKeys: checkedKeys,
+      appHalfCheckedKeys: info.halfCheckedKeys,
+      appInitCheckKeys: null
     })
   }
 
@@ -429,36 +549,88 @@
 
     Api.getSystemConfig(param).then(result => {
       if (result.status) {
-        if (!window.GLOB.mainSystemApi) {
-          notification.success({
-            top: 92,
-            message: '淇濆瓨鎴愬姛',
-            duration: 2
-          })
-          this.setState({
-            submitloading: false,
-            loadingTree: true
-          }, () => {
-            this.getSelectMenuList()
-          })
-        } else {
+        notification.success({
+          top: 92,
+          message: '淇濆瓨鎴愬姛',
+          duration: 2
+        })
+        this.setState({
+          submitloading: false
+        }, () => {
+          this.getSelectMenuList()
+        })
+        if (window.GLOB.mainSystemApi) {
           Api.getLocalConfig(localParam).then(res => {
-            if (res.status) {
-              notification.success({
+            if (!res.status) {
+              notification.warning({
                 top: 92,
-                message: '淇濆瓨鎴愬姛',
-                duration: 2
+                message: res.message,
+                duration: 5
               })
-              this.setState({
-                submitloading: false,
-                loadingTree: true
-              }, () => {
-                this.getSelectMenuList()
-              })
-            } else {
-              this.setState({
-                submitloading: false
-              })
+            }
+          })
+        }
+      } else {
+        this.setState({
+          submitloading: false
+        })
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  /**
+   * @description 鎻愪氦宸查�夌殑鏉冮檺
+   */
+  appRoleSubmit = () => {
+    const { selectApp, selectSubApp, appCheckedKeys, appHalfCheckedKeys, selectRoleId, appInitCheckKeys } = this.state
+
+    let _keys = []
+
+    if (appInitCheckKeys) {
+      _keys = appInitCheckKeys
+    } else {
+      _keys = appCheckedKeys.concat(appHalfCheckedKeys)
+    }
+
+    let param = {
+      func: 's_rolemenu_sub',
+      RoleID: selectRoleId,
+      TypeCharOne: selectApp.kei_no,
+      typename: selectSubApp.typename,
+      lang: selectSubApp.lang,
+      RoleMenu: _keys.map(key => {
+        return {MenuID: key}
+      })
+    }
+
+    let localParam = fromJS(param).toJS()
+    localParam.func = 's_rolemenu_sub_local'
+
+    this.setState({
+      submitloading: true
+    })
+
+    Api.getSystemConfig(param).then(result => {
+      if (result.status) {
+        notification.success({
+          top: 92,
+          message: '淇濆瓨鎴愬姛',
+          duration: 2
+        })
+        this.setState({
+          submitloading: false
+        }, () => {
+          this.getSelectAppNodeList()
+        })
+        
+        if (window.GLOB.mainSystemApi) {
+          Api.getLocalConfig(localParam).then(res => {
+            if (!res.status) {
               notification.warning({
                 top: 92,
                 message: res.message,
@@ -489,25 +661,168 @@
       loading: true,
       loadingTree: false,
       roleList: null,
+      filterRoleList: [],
       selectRoleId: '',
       mainMenus: null,
       menuTrees: null,
       checkedKeys: [],
+      appCheckedKeys: [],
       menuOpenKeys: [],
       selectMenuTrees: null,
       selectMenuOpenKeys: [],
       primarykey: '',
-      tabKey: '',
       submitloading: false,
-      initCheckKeys: null
+      initCheckKeys: null,
+      appInitCheckKeys: null,
+      activeKey: 'manage',
+      appTrees: null,
+      appOpenKeys: [],
+      applist: [],
+      selectApp: null,
+      selectSubApp: null,
+      selectAppTrees: null,
+      selectAppOpenKeys: []
     })
     this.getRoleList()
     this.getMainMenuList()
+    this.getAppList()
+  }
+
+  filterRole = () => {
+    const { primarykey, roleList } = this.state
+
+    let _roleList  = []
+
+    if (roleList && roleList.length > 0) {
+      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
+    }
+
+    this.setState({filterRoleList: _roleList})
+  }
+
+  getTreeList = () => {
+    const { selectApp, selectSubApp } = this.state
+    let param = {
+      func: 's_get_menus_roles_tree',
+      typecharone: selectApp.kei_no,
+      lang: selectSubApp.lang
+    }
+
+    param.upid = md5(window.GLOB.appkey + selectApp.kei_no + selectSubApp.typename + selectSubApp.lang)
+
+    this.setState({loadingAppTree: true})
+
+    Api.getSystemConfig(param).then(result => {
+      if (result.status) {
+        if (!result.data || result.data.length === 0) {
+          this.setState({loadingAppTree: false, appTrees: [], appInitCheckKeys: null, appCheckedKeys: [], appOpenKeys: [], selectAppTrees: [], selectAppOpenKeys: []})
+        } else {
+          this.initTrees(result.data)
+        }
+      } else {
+        this.setState({loadingAppTree: false, appTrees: [], appInitCheckKeys: null, appCheckedKeys: [], appOpenKeys: [], selectAppTrees: [], selectAppOpenKeys: []})
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  initTrees = (data) => {
+    let trees = []
+    let map = new Map()
+    let _data = data.sort((a, b) => {
+      return a.sort - b.sort
+    })
+
+    _data.forEach(menu => {
+      if (menu.ParentID === 'top') {
+        trees.push({
+          key: menu.MenuID,
+          title: menu.MenuName,
+          children: []
+        })
+      } else {
+        map.set(menu.MenuID, menu)
+      }
+    })
+
+    let reset = (m) => {
+      return m.map(n => {
+        [...map.keys()].forEach(key => {
+          if (map.get(key).ParentID === n.key) {
+            let c = map.get(key)
+            n.children.push({
+              key: c.MenuID,
+              title: c.MenuName,
+              children: []
+            })
+            map.delete(key)
+          }
+        })
+        if (n.children.length > 0) {
+          n.children = reset(n.children)
+        }
+        return n
+      })
+    }
+
+    trees = reset(trees)
+
+    let expandedKeys = this.getExpandedKeys(trees, 0, [])
+
+    this.setState({loadingAppTree: false, appInitCheckKeys: null, appCheckedKeys: [], selectAppTrees: [], selectAppOpenKeys: [], appTrees: trees, appOpenKeys: expandedKeys}, () => {
+      this.getSelectAppNodeList()
+    })
+  }
+
+  getExpandedKeys = (trees, i, keys) => {
+    if (i >= 3 || !trees[0]) return keys
+
+    keys.push(trees[0].key)
+
+    i++
+
+    if (trees[0].children && trees[0].children.length > 0) {
+      keys = this.getExpandedKeys(trees[0].children, i, keys)
+    }
+
+    return keys
+  }
+
+  changeType = (val) => {
+    this.setState({
+      activeKey: val
+    }, () => {
+      this.getSelectMenuList()
+      this.getSelectAppNodeList()
+    })
+  }
+
+  changeApp = (val) => {
+    const { applist } = this.state
+
+    let app = applist.filter(item => item.ID === val)[0]
+
+    this.setState({selectApp: app, selectSubApp: null})
+  }
+  
+  changeSubApp = (val) => {
+    const { selectApp } = this.state
+
+    let subapp = selectApp.sublist.filter(item => item.ID === val)[0]
+
+    this.setState({selectSubApp: subapp}, () => {
+      this.getTreeList()
+    })
   }
 
   UNSAFE_componentWillMount () {
     this.getRoleList()
     this.getMainMenuList()
+    this.getAppList()
   }
 
   componentDidMount () {
@@ -529,66 +844,136 @@
   }
 
   render() {
-    const { roleList, loading, loadingTree, primarykey, menuTrees, checkedKeys, menuOpenKeys, selectMenuTrees, tabKey, selectRoleId, selectMenuOpenKeys, submitloading } = this.state
+    const { activeKey, filterRoleList, applist, selectApp, selectSubApp, loading, loadingTree, loadingAppTree, primarykey, menuTrees, appTrees, checkedKeys, appCheckedKeys, menuOpenKeys, selectMenuTrees, selectAppTrees, selectRoleId, selectMenuOpenKeys, selectAppOpenKeys, submitloading, appOpenKeys } = this.state
 
-    let _roleList  = []
-
-    if (roleList && roleList.length > 0) {
-      _roleList = roleList.filter(role => role.RoleName.toLowerCase().indexOf(primarykey.toLowerCase()) >= 0)
-    }
-
+    let ismanage = options.sysType !== 'local'
+    
     return (
-      <div className="rolemanage">
+      <div className={'rolemanage' + (ismanage ? ' manager' : '')}>
         {loading && <Spin size="large" />}
-        <Row gutter={16}>
-          <Col span={5}>
-            <Card
-              className="role-list"
-              title={
-                <span className="role-title">
-                  <Icon type="bank" />
-                  <span className="title">{this.state.dict['main.role.title']}</span>
-                  <Search placeholder="" onSearch={value => this.setState({primarykey: value})} />
-                </span>
-              }
-              bordered={false}
-            >
-              <Menu
-                onClick={this.handleClick}
-                mode="inline"
-              >
-                {_roleList.map((role, index) =>
-                  <Menu.Item key={index} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
-                )}
-              </Menu>
-            </Card>
-          </Col>
-          <Col span={19}>
-            <Tabs defaultActiveKey="all" tabBarExtraContent={!tabKey && selectRoleId ? <Button type="primary" loading={submitloading} onClick={this.roleSubmit}>鎻愪氦</Button> : null} onChange={this.changeTab}>
-              <TabPane tab="鑿滃崟鍒楄〃" key="all">
-                {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
-                  checkable
-                  selectable={false}
-                  defaultExpandedKeys={menuOpenKeys}
-                  autoExpandParent={true}
-                  onCheck={this.onCheck}
-                  checkedKeys={checkedKeys}
+        <Tabs activeKey={activeKey} type="card" onChange={this.changeType}>
+          <TabPane tab="绠$悊绯荤粺" key="manage">
+            <Row gutter={16}>
+              <Col span={5}>
+                <Card
+                  className="role-list"
+                  title={
+                    <span className="role-title">
+                      <Icon type="bank" />
+                      <span className="title">瑙掕壊</span>
+                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
+                    </span>
+                  }
+                  bordered={false}
                 >
-                  {this.renderTreeNodes(menuTrees)}
-                </Tree> : null}
-                {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
-                {loadingTree ? <Spin className="load-tree" /> : null}
-              </TabPane>
-              <TabPane tab="宸叉巿鏉冭彍鍗�" key="selected">
-                {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
-                  {this.renderTreeNodes(selectMenuTrees)}
-                </DirectoryTree> : null}
-                {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
-                {loadingTree ? <Spin className="load-tree" /> : null}
-              </TabPane>
-            </Tabs>
-          </Col>
-        </Row>
+                  <Menu selectedKeys={[selectRoleId]} mode="inline">
+                    {filterRoleList.map((role) =>
+                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
+                    )}
+                  </Menu>
+                </Card>
+              </Col>
+              <Col span={19}>
+                <Tabs defaultActiveKey="all">
+                  <TabPane tab="鏉冮檺" key="all">
+                    {selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.roleSubmit}>鎻愪氦</Button> : null}
+                    {!loadingTree && menuTrees && menuTrees.length > 0 ? <Tree
+                      checkable
+                      selectable={false}
+                      defaultExpandedKeys={menuOpenKeys}
+                      autoExpandParent={true}
+                      onCheck={this.onCheck}
+                      checkedKeys={checkedKeys}
+                    >
+                      {this.renderTreeNodes(menuTrees)}
+                    </Tree> : null}
+                    {!loadingTree && (!menuTrees || menuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                    {loadingTree ? <Spin className="load-tree" /> : null}
+                  </TabPane>
+                  <TabPane tab="宸叉巿鏉�" key="selected">
+                    {!loadingTree && selectMenuTrees && selectMenuTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectMenuOpenKeys}>
+                      {this.renderTreeNodes(selectMenuTrees)}
+                    </DirectoryTree> : null}
+                    {!loadingTree && (!selectMenuTrees || selectMenuTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                    {loadingTree ? <Spin className="load-tree" /> : null}
+                  </TabPane>
+                </Tabs>
+              </Col>
+            </Row>
+          </TabPane>
+          <TabPane tab="搴旂敤" key="app">
+            <div className="app-select">
+              <div className="mk-form-item">
+                <label>搴旂敤:</label>
+                <Select value={selectApp ? selectApp.ID : ''} onChange={this.changeApp}>
+                  {applist.map(option =>
+                    <Select.Option key={option.ID} value={option.ID}>{option.remark}</Select.Option>
+                  )}
+                </Select>
+              </div>
+              <div className="mk-form-item">
+                <label>瀛愬簲鐢�:</label>
+                <Select value={selectSubApp ? selectSubApp.ID : ''} onChange={this.changeSubApp}>
+                  {selectApp && selectApp.sublist.map(option =>
+                    <Select.Option key={option.ID} value={option.ID}>{`${option.typename}锛�${option.lang}锛塦}</Select.Option>
+                  )}
+                </Select>
+              </div>
+            </div>
+            <Row gutter={16}>
+              <Col span={5}>
+                <Card
+                  className="role-list"
+                  title={
+                    <span className="role-title">
+                      <Icon type="bank" />
+                      <span className="title">瑙掕壊</span>
+                      <Search placeholder="" value={primarykey} onChange={e => this.setState({primarykey: e.target.value})} onSearch={this.filterRole} />
+                    </span>
+                  }
+                  bordered={false}
+                >
+                  <Menu selectedKeys={[selectRoleId]} mode="inline">
+                    {filterRoleList.map((role) =>
+                      <Menu.Item key={role.RoleID} onClick={() => this.changeRole(role)}>{role.RoleName}</Menu.Item>
+                    )}
+                  </Menu>
+                </Card>
+              </Col>
+              <Col span={19}>
+                <Tabs defaultActiveKey="all">
+                  <TabPane tab="鏉冮檺" key="all">
+                    {selectSubApp && selectRoleId ? <Button className="submitBtn" type="primary" loading={submitloading} onClick={this.appRoleSubmit}>鎻愪氦</Button> : null}
+                    {selectSubApp ? <div>
+                      {!loadingAppTree && appTrees && appTrees.length > 0 ? <Tree
+                        checkable
+                        selectable={false}
+                        defaultExpandedKeys={appOpenKeys}
+                        autoExpandParent={true}
+                        onCheck={this.onAppCheck}
+                        checkedKeys={appCheckedKeys}
+                      >
+                        {this.renderTreeNodes(appTrees)}
+                      </Tree> : null}
+                      {!loadingAppTree && (!appTrees || appTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                      {loadingAppTree ? <Spin className="load-tree" /> : null}
+                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'璇烽�夋嫨瀛愬簲鐢�'} />}
+                  </TabPane>
+                  <TabPane tab="宸叉巿鏉�" key="selected">
+                    {selectSubApp ? <div>
+                      {!loadingAppTree && selectAppTrees && selectAppTrees.length > 0 ? <DirectoryTree multiple defaultExpandedKeys={selectAppOpenKeys}>
+                        {this.renderTreeNodes(selectAppTrees)}
+                      </DirectoryTree> : null}
+                      {!loadingAppTree && (!selectAppTrees || selectAppTrees.length === 0) ? <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} /> : null}
+                      {loadingAppTree ? <Spin className="load-tree" /> : null}
+                    </div> : <Empty image={Empty.PRESENTED_IMAGE_SIMPLE} description={'璇烽�夋嫨瀛愬簲鐢�'} />}
+                  </TabPane>
+                </Tabs>
+              </Col>
+            </Row>
+          </TabPane>
+        </Tabs>
+        
       </div>
     )
   }
diff --git a/src/tabviews/rolemanage/index.scss b/src/tabviews/rolemanage/index.scss
index 7140d77..32a7261 100644
--- a/src/tabviews/rolemanage/index.scss
+++ b/src/tabviews/rolemanage/index.scss
@@ -14,7 +14,7 @@
         .role-title {
           display: inline-block;
           width: 100%;
-          color: #1890ff;
+          color: rgba(0, 0, 0, 0.65);
           .anticon-bank {
             margin-right: 5px;
           }
@@ -45,8 +45,14 @@
     left: calc(50vw - 22px);
     top: calc(50vh - 70px);
     z-index: 1;
-  } 
-  .ant-tabs {
+  }
+  .ant-tabs-bar.ant-tabs-card-bar {
+    .ant-tabs-tab {
+      min-width: 120px;
+      text-align: center;
+    }
+  }
+  .ant-tabs.ant-tabs-line {
     background: #fff;
     min-height: calc(100vh - 125px);
     box-shadow: 0px 0px 2px #eeeeee;
@@ -74,6 +80,35 @@
         text-align: center;
         color: #bcbcbc;
       }
+      .submitBtn {
+        position: absolute;
+        right: 10px;
+        top: 15px;
+      }
     }
   }
+  .app-select {
+    position: absolute;
+    top: 0px;
+    right: 0px;
+    .mk-form-item {
+      display: inline-block;
+      width: 230px;
+      label {
+        width: 60px;
+        display: inline-block;
+        text-align: right;
+        padding-right: 5px;
+        white-space: nowrap;
+      }
+      .ant-select {
+        width: 150px;
+      }
+    }
+  }
+}
+.rolemanage.manager {
+  .ant-tabs-bar.ant-tabs-card-bar {
+    display: none;
+  }
 }
\ No newline at end of file
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index f917266..0a0b845 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -102,6 +102,7 @@
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
         config.setting.MenuName = Tab.label
+        config.setting.$name = Tab.label
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -595,10 +596,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.Tab.label || ''
-    }
-
     let result = await Api.genericInterface(param)
 
     this.getStatFieldsValue(searches)
@@ -662,10 +659,6 @@
 
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType, id)
-
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.Tab.label || ''
-    }
 
     let result = await Api.genericInterface(param)
     if (result.status) {
@@ -736,10 +729,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, statFields, searches, _orderBy, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.Tab.label || ''
-    }
-    
     Api.genericInterface(param).then(res => {
       if (res.status) {
         let _data = res.data[0]
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index fdc900d..de85084 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -79,6 +79,7 @@
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
         config.setting.MenuName = Tab.label
+        config.setting.$name = Tab.label
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
@@ -557,10 +558,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getQueryDataParams(setting, arr_field, searches, _orderBy, pageIndex, pageSize, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.Tab.label || ''
-    }
-
     let result = await Api.genericInterface(param)
 
     this.getStatFieldsValue(searches)
@@ -615,10 +612,6 @@
     let _orderBy = orderBy || setting.order
     let param = UtilsDM.getStatQueryDataParams(setting, statFields, searches, _orderBy, BID, this.props.menuType)
 
-    if (param.func === 'sPC_Get_TableData') {
-      param.menuname = this.props.Tab.label || ''
-    }
-    
     Api.genericInterface(param).then(res => {
       if (res.status) {
         let _data = res.data[0]
diff --git a/src/tabviews/treepage/index.jsx b/src/tabviews/treepage/index.jsx
index 835e4a3..6c49ffd 100644
--- a/src/tabviews/treepage/index.jsx
+++ b/src/tabviews/treepage/index.jsx
@@ -56,7 +56,7 @@
    * @description 鑾峰彇椤甸潰閰嶇疆淇℃伅
    */
   async loadconfig () {
-    const { permAction, param } = this.props
+    const { permAction, param, MenuName } = this.props
 
     let _param = {
       func: 'sPC_Get_LongParam',
@@ -71,7 +71,8 @@
       try { // 閰嶇疆淇℃伅瑙f瀽
         config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
         config.MenuID = this.props.MenuID
-        config.MenuName = this.props.MenuName
+        config.MenuName = MenuName
+        config.setting.$name = MenuName
       } catch (e) {
         console.warn('Parse Failure')
         config = ''
diff --git a/src/tabviews/zshare/actionList/changeuserbutton/index.jsx b/src/tabviews/zshare/actionList/changeuserbutton/index.jsx
index 12ee0f4..d750c2c 100644
--- a/src/tabviews/zshare/actionList/changeuserbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/changeuserbutton/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Button, notification, Modal } from 'antd'
+import { Button, notification, Modal, Icon } from 'antd'
 
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -162,6 +162,8 @@
               localStorage.setItem('localDataM', res.dataM ? 'true' : '')
               localStorage.setItem('debug', res.debug || '')
               localStorage.setItem('role_id', res.role_id || '')
+              localStorage.setItem('departmentcode', res.departmentcode || '')
+              localStorage.setItem('organization', res.organization || '')
               localStorage.setItem('localRole_id', res.role_id || '')
               
               sessionStorage.removeItem('CloudAvatar')
@@ -207,6 +209,22 @@
         >{btn.label}</Button>
       )
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || ''
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return (
         <Button
           type="link"
@@ -214,9 +232,9 @@
           loading={loading}
           disabled={disabled}
           style={btn.style}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
+        >{label}</Button>
       )
     }
   }
diff --git a/src/tabviews/zshare/actionList/excelInbutton/index.jsx b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
index 2fc88ba..e943949 100644
--- a/src/tabviews/zshare/actionList/excelInbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -3,7 +3,7 @@
 import moment from 'moment'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, Modal, notification, message } from 'antd'
+import { Button, Modal, notification, message, Icon } from 'antd'
 
 import ExcelIn from './excelin'
 import Utils, { getExcelInSql } from '@/utils/utils.js'
@@ -456,6 +456,22 @@
         <ExcelIn btn={btn} triggerExcelIn={() => this.updateStatus('start')} returndata={this.getexceldata} ref="excelIn" />
       </div>
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || 'upload'
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
         <Button
           type="link"
@@ -463,9 +479,9 @@
           loading={loading}
           disabled={disabled}
           style={btn.style}
-          icon={show === 'text' ? '' : (show === 'icon' ? (btn.icon || 'upload') : (btn.icon || ''))}
+          icon={icon}
           onClick={() => {this.actionTrigger()}}
-        >{show === 'icon' ? '' : btn.label}</Button>
+        >{label}</Button>
         <ExcelIn btn={btn} triggerExcelIn={() => this.updateStatus('start')} returndata={this.getexceldata} ref="excelIn" />
       </div>
     }
diff --git a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
index 2bd9686..c151133 100644
--- a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -3,7 +3,7 @@
 import moment from 'moment'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, Modal, notification, message } from 'antd'
+import { Button, Modal, notification, message, Icon } from 'antd'
 import * as XLSX from 'xlsx'
 
 import Utils from '@/utils/utils.js'
@@ -685,6 +685,9 @@
 
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -742,7 +745,7 @@
     }
 
     if (param.custom_script) {
-      param.custom_script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      param.custom_script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${param.custom_script}
       `
       regoptions.forEach(item => {
@@ -870,15 +873,31 @@
         >{btn.label}</Button>
       )
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || 'download'
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return (
         <Button
           type="link"
           title={show === 'icon' ? btn.label : ''}
           loading={loading}
           style={btn.style}
-          icon={show === 'text' ? '' : (show === 'icon' ? (btn.icon || 'download') : (btn.icon || ''))}
+          icon={icon}
           onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
-        >{show === 'icon' ? '' : btn.label}</Button>
+        >{label}</Button>
       )
     }
   }
diff --git a/src/tabviews/zshare/actionList/newpagebutton/index.jsx b/src/tabviews/zshare/actionList/newpagebutton/index.jsx
index 2ff034c..6ce5e9b 100644
--- a/src/tabviews/zshare/actionList/newpagebutton/index.jsx
+++ b/src/tabviews/zshare/actionList/newpagebutton/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Button, notification, Modal } from 'antd'
+import { Button, notification, Modal, Icon } from 'antd'
 
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
@@ -202,15 +202,31 @@
         >{btn.label}</Button>
       )
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || ''
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return (
         <Button
           type="link"
           title={show === 'icon' ? btn.label : ''}
           style={btn.style}
           disabled={disabled}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
+        >{label}</Button>
       )
     }
   }
diff --git a/src/tabviews/zshare/actionList/normalbutton/index.jsx b/src/tabviews/zshare/actionList/normalbutton/index.jsx
index 952fd74..737abd4 100644
--- a/src/tabviews/zshare/actionList/normalbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -3,7 +3,7 @@
 import moment from 'moment'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, Modal, notification, message, Drawer } from 'antd'
+import { Button, Modal, notification, message, Drawer, Icon } from 'antd'
 
 import Api from '@/api'
 import Utils, { getSysDefaultSql } from '@/utils/utils.js'
@@ -155,11 +155,6 @@
         message: '闇�瑕佷笂绾т富閿�硷紒',
         duration: 5
       })
-      return
-    }
-
-    if (btn.$syncModule && !triggerId) {
-      MKEmitter.emit('getSyncData', btn.$syncModule, btn.uuid)
       return
     }
 
@@ -357,6 +352,9 @@
             param.$callbacksql = callbacksql
           } else {
             param.LText = getSysDefaultSql(btn, setting, formdata, param, data[0], columns, this.props.Tab, false, this.moduleParams, Utils.getAllSearchOptions) // 鏁版嵁婧�
+            if (btn.OpenType === 'formSubmit' && btn.output) {
+              param.key_back_type = 'Y'
+            }
           }
           
           if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
@@ -1297,8 +1295,13 @@
       this.sendMessage()
     }
 
+    let id = ''
+    if (btn.output) {
+      id = res.mk_b_id || res[btn.output] || ''
+    }
+
     if (btn.execSuccess !== 'never') {
-      MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execSuccess || '', btn)
+      MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execSuccess, btn, id)
     }
   }
 
@@ -1804,6 +1807,22 @@
         {this.getModels()}
       </div>
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || ''
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
         <Button
           type="link"
@@ -1811,9 +1830,9 @@
           loading={loading}
           disabled={disabled}
           style={btn.style || style}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={() => {this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
+        >{label}</Button>
         {this.getModels()}
       </div>
     }
diff --git a/src/tabviews/zshare/actionList/popupbutton/index.jsx b/src/tabviews/zshare/actionList/popupbutton/index.jsx
index c519a94..a0429e7 100644
--- a/src/tabviews/zshare/actionList/popupbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/popupbutton/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, Modal, notification } from 'antd'
+import { Button, Modal, notification, Icon, Drawer } from 'antd'
 
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -192,9 +192,108 @@
     }
   }
 
+  getPop = () => {
+    const { btn } = this.props
+    const { popData, primaryId, visible } = this.state
+
+    let ratio = btn.ratio || 85
+
+    if (btn.display !== 'drawer') {
+      if (ratio > 100) {
+        ratio = ratio + 'px'
+      } else {
+        ratio = ratio + 'vw'
+      }
+      return <Modal
+        className={'popview-modal ' + (btn.$view === 'CustomPage' ? 'custom-popview' : '')}
+        title={btn.label}
+        width={ratio}
+        maskClosable={false}
+        visible={visible}
+        onCancel={this.popclose}
+        footer={[
+          <Button key="close" onClick={this.popclose}>{this.state.dict['main.close']}</Button>
+        ]}
+        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 === 'CustomPage' ? <CustomPage Tab={btn} MenuID={btn.uuid} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
+      </Modal>
+    } else {
+      let height = '100vh'
+      let width = '100vw'
+      if (btn.placement === 'top' || btn.placement === 'bottom') {
+        if (ratio > 100) {
+          ratio = ratio + 'px'
+        } else {
+          ratio = ratio + 'vh'
+        }
+        
+        height = ratio
+      } else {
+        if (ratio > 100) {
+          ratio = ratio + 'px'
+        } else {
+          ratio = ratio + 'vw'
+        }
+
+        width = ratio
+      }
+
+      return (
+        <Drawer
+          title={btn.label}
+          className={btn.$view === 'CustomPage' ? 'custom-drawer-popview' : ''}
+          width={width}
+          height={height}
+          maskClosable={false}
+          onClose={this.popclose}
+          visible={visible}
+          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 === 'CustomPage' ? <CustomPage Tab={btn} MenuID={btn.uuid} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
+          <div className="close-drawer">
+            <Button onClick={this.popclose}>
+              鍏抽棴
+            </Button>
+          </div>
+        </Drawer>
+      )
+    }
+  }
+
   render() {
     const { btn, show } = this.props
-    const { loading, popData, primaryId, disabled } = this.state
+    const { loading, disabled } = this.state
+
+    let label = ''
+    let icon = ''
+
+    if (show === 'button') {
+      label = btn.label
+      icon = btn.icon || ''
+    } else if (show === 'link') {
+      label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+      icon = ''
+    } else if (show === 'icon') {
+      icon = btn.icon || ''
+    } else {
+      label = btn.label
+    }
 
     return (
       <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
@@ -211,30 +310,10 @@
           loading={loading}
           disabled={disabled}
           style={btn.style}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={() => {this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button> : null}
-        <Modal
-          className={'popview-modal ' + (btn.$view === 'CustomPage' ? 'custom-popview' : '')}
-          title={btn.label}
-          width={'85vw'}
-          maskClosable={false}
-          visible={this.state.visible}
-          onCancel={this.popclose}
-          footer={[
-            <Button key="close" onClick={this.popclose}>{this.state.dict['main.close']}</Button>
-          ]}
-          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 === 'CustomPage' ? <CustomPage Tab={btn} MenuID={btn.uuid} param={{$BID: (popData ? primaryId : this.props.BID), ...(popData || this.props.BData || {})}} /> : null}
-        </Modal>
+        >{label}</Button> : null}
+        {this.getPop()}
       </div>
     )
   }
diff --git a/src/tabviews/zshare/actionList/popupbutton/index.scss b/src/tabviews/zshare/actionList/popupbutton/index.scss
index bd9cd43..e0a65c3 100644
--- a/src/tabviews/zshare/actionList/popupbutton/index.scss
+++ b/src/tabviews/zshare/actionList/popupbutton/index.scss
@@ -3,3 +3,27 @@
     padding: 0;
   }
 }
+.custom-drawer-popview {
+  .ant-drawer-content {
+    .ant-drawer-header {
+      position: absolute;
+      width: 100%;
+      background: #ffffff;
+      z-index: 3;
+    }
+    .ant-drawer-body {
+      padding: 55px 0 50px 0;
+      .close-drawer {
+        position: absolute;
+        z-index: 3;
+        right: 0;
+        bottom: 0;
+        width: 100%;
+        padding: 8px 16px;
+        background: #fff;
+        text-align: right;
+        box-shadow: 0 0 3px #cbcbcb;
+      }
+    }
+  }
+}
diff --git a/src/tabviews/zshare/actionList/printbutton/index.jsx b/src/tabviews/zshare/actionList/printbutton/index.jsx
index fb45054..ac9fa04 100644
--- a/src/tabviews/zshare/actionList/printbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -3,7 +3,7 @@
 import moment from 'moment'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, Modal, notification, message } from 'antd'
+import { Button, Modal, notification, message, Icon } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -380,6 +380,177 @@
       // eslint-disable-next-line
       let func = new Function('data', 'form', 'printer', 'notification', btn.verify.printFunc)
       func(printlist, formdata, btn.verify, notification)
+
+      // 鑷畾涔夋墦鍗扮ず渚�
+      // let defaultPrinter = printer.defaultPrinter || 'lackprinter'
+      // let printers = {}
+      // if (printer.printerTypeList && printer.printerTypeList.length > 0) {
+      //   printer.printerTypeList.forEach(cell => {
+      //     if (cell.printer) {
+      //       printers[cell.Value] = cell.printer
+      //     }
+      //   })
+      // }
+
+      // let jdList = []
+      // let otherList = []
+      // data.forEach(item => {
+      //   if (item.CustomData) {
+      //     item.CustomData = JSON.parse(item.CustomData.replace(/\n/g,"\\n").replace(/\r/g,"\\r"))
+      //   }
+      //   if (item.PrintData) {
+      //     item.PrintData = JSON.parse(item.PrintData.replace(/\n/g,"\\n").replace(/\r/g,"\\r"))
+      //     item.PrintData.data = {...form, ...item.PrintData.data}
+      //   }
+        
+      //   if (!item.PrintData) {
+      //     return
+      //   }
+
+      //   if (item.PrintData.ectype === 'jdpop') {
+      //     jdList.push(item)
+      //   } else {
+      //     otherList.push(item)
+      //   }
+      // })
+
+      // if (jdList.length === 0 && otherList.length === 0) {
+      //   notification.warning({
+      //     top: 92,
+      //     message: '鏃犳墦鍗版暟鎹紒',
+      //     duration: 5
+      //   })
+      //   return
+      // }
+
+      // let execPrint = (list, linkUrl) => {
+      //   let printdata = {}
+
+      //   list.forEach(res => {
+      //     let _printer = defaultPrinter
+
+      //     if (res.printType && printers[res.printType]) {
+      //       _printer = printers[res.printType]
+      //     }
+
+      //     printdata[_printer] = printdata[_printer] || []
+
+      //     printdata[_printer].push(res)
+      //   })
+
+      //   let printerList = []
+
+      //   Object.keys(printdata).forEach(printer => {
+      //     let _documents = []
+      //     printdata[printer].forEach(item => {
+      //       let _cell = {
+      //         documentID: new Date().getTime().toString(),
+      //         contents: []
+      //       }
+
+      //       if (item.PrintData) {
+      //         _cell.contents.push(item.PrintData)
+      //       }
+      //       if (item.CustomData) {
+      //         _cell.contents.push(item.CustomData)
+      //       }
+
+      //       for (let i = 0; i < item.printCount; i++) {
+      //         _documents.push(_cell)
+      //       }
+      //     })
+      //     printerList.push({
+      //       cmd: 'print',
+      //       requestID: '',
+      //       version: '',
+      //       task: {
+      //         taskID: new Date().getTime().toString(),
+      //         preview: false,
+      //         printer: printer,
+      //         documents: _documents
+      //       }
+      //     })
+      //   })
+
+      //   let lackItems = printerList.filter(cell => cell.task.printer === 'lackprinter')[0]
+
+      //   let socket = new WebSocket('ws://' + linkUrl)
+
+      //   // 鎵撳紑Socket
+      //   socket.onopen = () =>{
+      //     if (lackItems) {
+      //       let request  = {
+      //         requestID: '',
+      //         version: '',
+      //         cmd: 'getPrinters'
+      //       }
+      //       socket.send(JSON.stringify(request))
+      //     } else {
+      //       printerList.forEach(cell => {
+      //         socket.send(JSON.stringify(cell).replace(/\\r/g,"\r").replace(/\\n/g,"\n"))
+      //       })
+
+      //       notification.success({
+      //         top: 92,
+      //         message: '鎵撳嵃璇锋眰宸插彂鍑恒��',
+      //         duration: 2
+      //       })
+      //     }
+      //   }
+      //   // 鐩戝惉娑堟伅
+      //   socket.onmessage = (event) => {
+      //     let data = ''
+
+      //     if (event.data) {
+      //       try {
+      //         data = JSON.parse(event.data)
+      //       } catch {
+      //         notification.warning({
+      //           top: 92,
+      //           message: event.data,
+      //           duration: 10
+      //         })
+      //         data = ''
+      //       }
+      //     }
+
+      //     if (data && data.cmd === 'getPrinters' && data.status) {
+      //       printerList.forEach(cell => {
+      //         if (cell.task.printer === 'lackprinter') {
+      //           cell.task.printer = data.defaultPrinter
+      //         }
+      //         socket.send(JSON.stringify(cell).replace(/\\r/g,"\r").replace(/\\n/g,"\n"))
+      //       })
+
+      //       notification.success({
+      //         top: 92,
+      //         message: '鎵撳嵃璇锋眰宸插彂鍑恒��',
+      //         duration: 2
+      //       })
+      //     } else if (data && data.message && !data.status) {
+      //       notification.warning({
+      //         top: 92,
+      //         message: data.message,
+      //         duration: 10
+      //       })
+      //     }
+      //   }
+
+      //   socket.onerror = () => {
+      //     notification.warning({
+      //       top: 92,
+      //       message: '鏃犳硶杩炴帴鍒�:' + linkUrl,
+      //       duration: 10
+      //     })
+      //   }
+      // }
+
+      // if (jdList.length > 0) {
+      //   execPrint(jdList, '127.0.0.1:13529')
+      // }
+      // if (otherList.length > 0) {
+      //   execPrint(otherList, '127.0.0.1:13528')
+      // }
     } catch (e) {
       console.warn(e)
 
@@ -1338,6 +1509,22 @@
         {this.getModels()}
       </div>
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || ''
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return <div style={{display: 'inline-block'}} onClick={(e) => e.stopPropagation()}>
         <Button
           type="link"
@@ -1345,9 +1532,9 @@
           loading={loading}
           disabled={disabled}
           style={btn.style}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={() => {this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
+        >{label}</Button>
         {this.getModels()}
       </div>
     }
diff --git a/src/tabviews/zshare/actionList/tabbutton/index.jsx b/src/tabviews/zshare/actionList/tabbutton/index.jsx
index fdb9d78..db9b04b 100644
--- a/src/tabviews/zshare/actionList/tabbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -2,7 +2,7 @@
 import PropTypes from 'prop-types'
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Button, notification } from 'antd'
+import { Button, notification, Icon } from 'antd'
 
 import { modifyTabview } from '@/store/action'
 import zhCN from '@/locales/zh-CN/main.js'
@@ -157,7 +157,7 @@
       return tab.MenuID !== newtab.MenuID
     })
 
-    if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+    if (['linkage_navigation', 'linkage', 'menu_board'].includes(window.GLOB.navBar)) {
       this.props.modifyTabview([newtab])
     } else {
       if (tabviews.length !== tabs.length) {
@@ -194,15 +194,31 @@
         >{btn.label}</Button>
       )
     } else { // icon銆乼ext銆� all 鍗$墖
+      let label = ''
+      let icon = ''
+
+      if (show === 'button') {
+        label = btn.label
+        icon = btn.icon || ''
+      } else if (show === 'link') {
+        label = <span>{btn.label}{btn.icon ? <Icon style={{marginLeft: '8px'}} type={btn.icon}/> : ''}</span>
+        icon = ''
+      } else if (show === 'icon') {
+        icon = btn.icon || ''
+      // } else if (show === 'text') {
+      } else {
+        label = btn.label
+      }
+
       return (
         <Button
           type="link"
           title={show === 'icon' ? btn.label : ''}
           style={btn.style}
           disabled={disabled}
-          icon={show === 'text' ? '' : (btn.icon || '')}
+          icon={icon}
           onClick={(e) => {e.stopPropagation(); this.actionTrigger()}}
-        >{show === 'icon' && btn.icon ? '' : btn.label}</Button>
+        >{label}</Button>
       )
     }
   }
diff --git a/src/tabviews/zshare/imgScale/index.jsx b/src/tabviews/zshare/imgScale/index.jsx
deleted file mode 100644
index dc4c542..0000000
--- a/src/tabviews/zshare/imgScale/index.jsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon } from 'antd'
-
-import './index.scss'
-
-class ImgScale extends Component {
-  static propTpyes = {
-    data: PropTypes.object
-  }
-
-  state = {
-    list: [],
-    index: 0
-  }
-
-  UNSAFE_componentWillMount() {
-    const { data } = this.props
-
-    this.setState({
-      list: data.list || [],
-      index: data.index || 0
-    })
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  /**
-   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
-   */
-  componentWillUnmount () {
-    this.setState = () => {
-      return
-    }
-  }
-
-  reduce = () => {
-    const { index } = this.state
-
-    this.setState({index: index - 1})
-  }
-
-  plus = () => {
-    const { index } = this.state
-
-    this.setState({index: index + 1})
-  }
-
-  render() {
-    const { list, index } = this.state
-
-    return (
-      <div className="img-scale-wrap">
-        <img src={list[index]} alt="" />
-        {index > 0 ? <Icon type="left" onClick={this.reduce} /> : null}
-        {index < list.length -1 ? <Icon type="right" onClick={this.plus} /> : null}
-      </div>
-    )
-  }
-}
-
-export default ImgScale
\ No newline at end of file
diff --git a/src/tabviews/zshare/imgScale/index.scss b/src/tabviews/zshare/imgScale/index.scss
deleted file mode 100644
index db4944e..0000000
--- a/src/tabviews/zshare/imgScale/index.scss
+++ /dev/null
@@ -1,20 +0,0 @@
-.img-scale-wrap {
-  position: relative;
-  img {
-    max-width: 100%;
-  }
-
-  .anticon {
-    position: absolute;
-    top: calc(50% - 25px);
-    font-size: 30px;
-    padding: 10px;
-  }
-
-  .anticon-left {
-    left: 20px;
-  }
-  .anticon-right {
-    right: 20px;
-  }
-}
diff --git a/src/tabviews/zshare/mutilform/index.jsx b/src/tabviews/zshare/mutilform/index.jsx
index 28affdd..7006e7b 100644
--- a/src/tabviews/zshare/mutilform/index.jsx
+++ b/src/tabviews/zshare/mutilform/index.jsx
@@ -160,6 +160,9 @@
       }
 
       if (item.type === 'text') {
+        if (typeof(item.initval) === 'number') {
+          item.initval = item.initval + ''
+        }
         let _rules = [{
           pattern: /^[^']*$/ig,
           message: formRule.input.quotemsg
@@ -186,6 +189,11 @@
             _rules.push({
               pattern: /^[a-zA-Z0-9]*$/ig,
               message: formRule.input.letternummsg
+            })
+          } else if (item.regular === 'phone') {
+            _rules.push({
+              pattern: /^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/ig,
+              message: '璇锋纭緭鍏ユ墜鏈哄彿'
             })
           } else if (item.regular === 'funcname') {
             _rules.push({
@@ -656,9 +664,9 @@
         let label = item.tooltip ? <Tooltip placement="topLeft" title={item.tooltip}><Icon type="question-circle" />{item.label}</Tooltip> : item.label
       
         if (item.type === 'text' || item.type === 'linkMain') {
-          content = (<MKInput config={item} onChange={(val) => this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
+          content = (<MKInput config={item} onChange={(val, defer) => !defer && this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
         } else if (item.type === 'number') {
-          content = (<MKNumberInput config={item} onChange={(val) => this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
+          content = (<MKNumberInput config={item} onChange={(val, defer) => !defer && this.recordChange({[item.field]: val})} onSubmit={this.props.inputSubmit} />)
         } else if (item.type === 'select' || item.type === 'link' || item.type === 'multiselect') {
           content = (<MKSelect config={item} onChange={(val, other) => this.recordChange({[item.field]: val, ...other}, item)} onSubmit={this.props.inputSubmit} />)
         } else if (item.type === 'color') {
@@ -677,7 +685,7 @@
         } else if (item.type === 'fileupload') {
           content = (<MKFileUpload config={item} onChange={(val) => this.recordChange({[item.field]: val})} />)
         } else if (item.type === 'textarea') {
-          content = (<MKTextArea config={item} onChange={(val) => this.recordChange({[item.field]: val})}/>)
+          content = (<MKTextArea config={item} onChange={(val, defer) => !defer && this.recordChange({[item.field]: val})}/>)
         } else if (item.type === 'brafteditor') {
           content = (<MKEditor config={item} onChange={(val) => this.recordChange({[item.field]: val})}/>)
           label = item.hidelabel !== 'true' ? label : ''
diff --git a/src/tabviews/zshare/mutilform/mkInput/index.jsx b/src/tabviews/zshare/mutilform/mkInput/index.jsx
index 9b090dd..1907f06 100644
--- a/src/tabviews/zshare/mutilform/mkInput/index.jsx
+++ b/src/tabviews/zshare/mutilform/mkInput/index.jsx
@@ -43,6 +43,7 @@
       this.inputRef.current.select()
     } else if (type === 'input') {
       this.setState({value})
+      this.props.onChange(value, true)
     }
   }
 
@@ -77,7 +78,7 @@
     const { config } = this.props
     const { value } = this.state
 
-    return <Input ref={this.inputRef} placeholder="" value={value} autoComplete="off" disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleInputSubmit} />
+    return <Input ref={this.inputRef} placeholder={config.placeholder || ''} value={value} autoComplete="off" disabled={config.readonly} onChange={this.handleChange} onPressEnter={this.handleInputSubmit} />
   }
 }
 
diff --git a/src/tabviews/zshare/mutilform/mkNumberInput/index.jsx b/src/tabviews/zshare/mutilform/mkNumberInput/index.jsx
index 7dde469..5cad869 100644
--- a/src/tabviews/zshare/mutilform/mkNumberInput/index.jsx
+++ b/src/tabviews/zshare/mutilform/mkNumberInput/index.jsx
@@ -20,8 +20,6 @@
     }
   }
 
-  inputNumberRef = React.createRef()
-
   shouldComponentUpdate (nextProps, nextState) {
     return !is(fromJS(this.state), fromJS(nextState))
   }
@@ -40,16 +38,17 @@
   mkFormHandle = (type, uuid, value) => {
     if (uuid !== this.props.config.uuid) return
     if (type === 'focus') {
-      this.inputNumberRef.current.focus()
+      let node = document.getElementById(uuid)
+      node.select()
     } else if (type === 'input') {
       this.setState({value})
+      this.props.onChange(value, true)
     }
   }
 
   handleChange = (val) => {
-
-    this.props.onChange(val)
     this.setState({value: val})
+    this.props.onChange(val)
   }
 
   render() {
@@ -57,9 +56,9 @@
     const { value, precision } = this.state
 
     if (precision === null) {
-      return (<InputNumber ref={this.inputNumberRef} value={value} disabled={config.readonly} onChange={this.handleChange} onPressEnter={onSubmit}/>)
+      return (<InputNumber id={config.uuid} value={value} disabled={config.readonly} onChange={this.handleChange} onPressEnter={onSubmit}/>)
     } else {
-      return (<InputNumber ref={this.inputNumberRef} value={value} precision={precision} disabled={config.readonly} onChange={this.handleChange} onPressEnter={onSubmit} />)
+      return (<InputNumber id={config.uuid} value={value} precision={precision} disabled={config.readonly} onChange={this.handleChange} onPressEnter={onSubmit} />)
     }
   }
 }
diff --git a/src/tabviews/zshare/mutilform/mkSelect/index.jsx b/src/tabviews/zshare/mutilform/mkSelect/index.jsx
index 24da440..d754520 100644
--- a/src/tabviews/zshare/mutilform/mkSelect/index.jsx
+++ b/src/tabviews/zshare/mutilform/mkSelect/index.jsx
@@ -150,7 +150,7 @@
           value={value}
           filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
           onSelect={this.selectChange}
-          onClear={() => this.selectChange('')}
+          onChange={(val) => val === undefined && this.selectChange('')}
           disabled={config.readonly}
         >
           {options.map(option =>
diff --git a/src/tabviews/zshare/mutilform/mkTextArea/index.jsx b/src/tabviews/zshare/mutilform/mkTextArea/index.jsx
index 531aa49..48429fc 100644
--- a/src/tabviews/zshare/mutilform/mkTextArea/index.jsx
+++ b/src/tabviews/zshare/mutilform/mkTextArea/index.jsx
@@ -59,6 +59,7 @@
       this.inputRef.current.focus()
     } else if (type === 'input') {
       this.setState({value})
+      this.props.onChange(value, true)
     }
   }
 
@@ -84,7 +85,7 @@
     const { value } = this.state
 
     return (
-      <TextArea ref={this.inputRef} value={value} autoSize={{ minRows: 2, maxRows: config.maxRows || 6 }} onChange={this.onChange} disabled={config.readonly} />
+      <TextArea ref={this.inputRef} placeholder={config.placeholder || ''} value={value} autoSize={{ minRows: 2, maxRows: config.maxRows || 6 }} onChange={this.onChange} disabled={config.readonly} />
     )
   }
 }
diff --git a/src/tabviews/zshare/normalTable/index.jsx b/src/tabviews/zshare/normalTable/index.jsx
index aa989fb..21feaa5 100644
--- a/src/tabviews/zshare/normalTable/index.jsx
+++ b/src/tabviews/zshare/normalTable/index.jsx
@@ -3,7 +3,7 @@
 import md5 from 'md5'
 import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Table, Affix, Typography, Modal, Icon } from 'antd'
+import { Table, Affix, Typography, Icon } from 'antd'
 
 import { modifyTabview } from '@/store/action'
 import asyncComponent from '@/utils/asyncComponent'
@@ -17,7 +17,6 @@
 const PopupButton = asyncComponent(() => import('@/tabviews/zshare/actionList/popupbutton'))
 const TabButton = asyncComponent(() => import('@/tabviews/zshare/actionList/tabbutton'))
 const NewPageButton = asyncComponent(() => import('@/tabviews/zshare/actionList/newpagebutton'))
-const ImgScale = asyncComponent(() => import('@/tabviews/zshare/imgScale'))
 
 class NormalTable extends Component {
   static propTpyes = {
@@ -45,8 +44,6 @@
     pageIndex: 1,         // 鍒濆椤甸潰绱㈠紩
     pageSize: 10,         // 姣忛〉鏁版嵁鏉℃暟
     columns: null,        // 鏄剧ず鍒�
-    imgShow: false,       // 鍥剧墖鏀惧ぇ妯℃�佹
-    imgData: {},          // 鍥剧墖闆�
     lineMarks: null,      // 琛屾爣璁�
     activeIndex: null,    // 鏍囪褰撳墠閫変腑琛�
     rowspans: null        // 琛屽悎骞跺瓧娈典俊鎭�
@@ -558,7 +555,10 @@
         <div className="picture-col">
           {photos && photos.map((url, i) => {
             if (item.scale === 'true') {
-              return <img style={{maxHeight: maxHeight}} className="image-scale" onClick={() => this.imgScale(photos, i)} key={`${i}`} src={url} alt=""/>
+              return <img style={{maxHeight: maxHeight}} className="image-scale" onClick={(e) => {
+                e.stopPropagation()
+                MKEmitter.emit('mkImageScale', url, photos)
+              }} key={`${i}`} src={url} alt=""/>
             } else {
               return <img style={{maxHeight: maxHeight}} key={`${i}`} src={url} alt=""/>
             }
@@ -871,7 +871,10 @@
           <div className="content-fence-top" style={images[0] ? {textAlign: images[0].align} : null}>
             {images.map((_img, index) => {
               if (_img.scale) {
-                return <img style={{maxHeight: _img.maxHeight}} className="image-scale" onClick={() => this.imgScale(images, index)} key={`${index}`} src={_img.url} alt=""/>
+                return <img style={{maxHeight: _img.maxHeight}} className="image-scale" onClick={(e) => {
+                  e.stopPropagation()
+                  MKEmitter.emit('mkImageScale', _img.url, images.map(g => g.url))
+                }} key={`${index}`} src={_img.url} alt=""/>
               } else {
                 return (<img style={{maxHeight: _img.maxHeight}} key={`${index}`} src={_img.url} alt=""/>)
               }
@@ -890,7 +893,9 @@
           <div className="content-fence-left" style={images[0] ? {textAlign: images[0].align} : null}>
             {images.map((_img, index) => {
               if (_img.scale) {
-                return <img style={{maxHeight: _img.maxHeight}} className="image-scale" onClick={() => this.imgScale(images, index)} key={`${index}`} src={_img.url} alt=""/>
+                return <img style={{maxHeight: _img.maxHeight}} className="image-scale" onClick={() => {
+                  MKEmitter.emit('mkImageScale', _img.url, images.map(g => g.url))
+                }} key={`${index}`} src={_img.url} alt=""/>
               } else {
                 return (<img style={{maxHeight: _img.maxHeight}} key={`${index}`} src={_img.url} alt=""/>)
               }
@@ -904,24 +909,6 @@
         </div>
       )
     }
-  }
-
-  /**
-   * @description 鍥剧墖缂╂斁
-   */
-  imgScale = (images, index) => {
-    this.setState({
-      imgShow: true,
-      imgData: {
-        list: images.map(item => {
-          if (typeof(item) === 'string') {
-            return item
-          }
-          return item.url
-        }),
-        index
-      }
-    })
   }
 
   /**
@@ -1232,18 +1219,6 @@
           pagination={_pagination}
         />
         {_footer ? <div className={'normal-table-footer ' + (_pagination ? 'pagination' : '')}>{_footer}</div> : null}
-        <Modal
-          className="image-scale-modal"
-          visible={this.state.imgShow}
-          width="70vw"
-          maskClosable={true}
-          onCancel={() => {this.setState({ imgShow: false })}}
-          title={this.props.dict['main.form.picture.check']}
-          footer={[<span key="close" onClick={() => {this.setState({ imgShow: false })}}>{this.props.dict['main.close']}</span>]}
-          destroyOnClose
-        >
-          <ImgScale data={this.state.imgData}/>
-        </Modal>
       </div>
     )
   }
diff --git a/src/tabviews/zshare/normalTable/index.scss b/src/tabviews/zshare/normalTable/index.scss
index 5c27e47..9c97a66 100644
--- a/src/tabviews/zshare/normalTable/index.scss
+++ b/src/tabviews/zshare/normalTable/index.scss
@@ -137,7 +137,7 @@
         .picture-col {
           img {
             max-width: 100%;
-            display: block;
+            display: inline-block;
           }
           img + img {
             margin-top: 10px;
diff --git a/src/tabviews/zshare/topSearch/advanceform/index.jsx b/src/tabviews/zshare/topSearch/advanceform/index.jsx
index dd07f57..9a9f6d9 100644
--- a/src/tabviews/zshare/topSearch/advanceform/index.jsx
+++ b/src/tabviews/zshare/topSearch/advanceform/index.jsx
@@ -43,9 +43,9 @@
 
       if (item.type === 'text') {
         if (item.inputType === 'search') {
-          content = <Search placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onSearch={this.handleSubmit} enterButton/>
+          content = <Search allowClear placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onSearch={this.handleSubmit} enterButton/>
         } else {
-          content = <Input placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onPressEnter={this.handleSubmit} />
+          content = <Input allowClear placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onPressEnter={this.handleSubmit} />
         }
       } else if (item.type === 'select' || item.type === 'link' || item.type === 'multiselect') {
         content = (<MKSelect config={item}/>)
@@ -72,7 +72,8 @@
     return fields
   }
 
-  handleSubmit = () => {
+  handleSubmit = (e) => {
+    e.stopPropagation()
     // 鍥炶溅鎴栫偣鍑绘悳绱�
     this.props.form.validateFields((err, values) => {
       if (err) return
diff --git a/src/tabviews/zshare/topSearch/advanceform/index.scss b/src/tabviews/zshare/topSearch/advanceform/index.scss
index 74b0bca..21216b0 100644
--- a/src/tabviews/zshare/topSearch/advanceform/index.scss
+++ b/src/tabviews/zshare/topSearch/advanceform/index.scss
@@ -38,4 +38,15 @@
     padding: 10px;
     border-top: 1px solid #f0f0f0;
   }
+  .ant-input-affix-wrapper {
+    .ant-input-suffix {
+      opacity: 1;
+      transition: opacity 0.3s;
+    }
+  }
+  .ant-input-affix-wrapper:not(:hover) {
+    .ant-input-suffix {
+      opacity: 0;
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/tabviews/zshare/topSearch/index.jsx b/src/tabviews/zshare/topSearch/index.jsx
index c16a881..90209bf 100644
--- a/src/tabviews/zshare/topSearch/index.jsx
+++ b/src/tabviews/zshare/topSearch/index.jsx
@@ -362,7 +362,8 @@
   resetSearch = (result) => {
     let _searchlist = this.state.searchlist.map(item => {
       if (['select', 'link', 'multiselect', 'checkcard'].includes(item.type) && result[item.field] && result[item.field].length > 0) {
-        let options = result[item.field].map(cell => {
+        let options = []
+        result[item.field].forEach(cell => {
           let _item = {
             key: Utils.getuuid()
           }
@@ -370,6 +371,9 @@
           if (item.type !== 'checkcard') {
             _item.Value = cell[item.valueField]
             _item.Text = cell[item.valueText]
+            if (!_item.Text && _item.Text !== 0) {
+              return
+            }
           } else {
             _item.$value = cell[item.cardValField]
             _item = {..._item, ...cell}
@@ -379,7 +383,7 @@
             _item.ParentID = cell[item.linkField]
           }
 
-          return _item
+          options.push(_item)
         })
 
         item.oriOptions = [...item.oriOptions, ...options]
@@ -427,7 +431,7 @@
 
   getFields() {
     const { getFieldDecorator } = this.props.form
-    const { dict, showButton, showAdvanced, float } = this.state
+    const { dict, showButton, showAdvanced, float, visible } = this.state
     const fields = []
 
     this.state.searchlist.forEach((item, index) => {
@@ -445,9 +449,9 @@
 
       if (item.type === 'text') {
         if (item.inputType === 'search') {
-          content = <Search placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onSearch={this.handleSubmit} enterButton/>
+          content = <Search allowClear placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onSearch={this.handleSubmit} enterButton/>
         } else {
-          content = <Input placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onPressEnter={this.handleSubmit} />
+          content = <Input allowClear placeholder={item.labelShow === 'false' ? item.label : ''} autoComplete="off" onPressEnter={this.handleSubmit} />
         }
       } else if (item.type === 'select' || item.type === 'link' || item.type === 'multiselect') {
         content = (<MKSelect config={item} onChange={(val, defer) => this.recordChange(val, defer, item)} />)
@@ -484,7 +488,7 @@
             <Button style={{ marginLeft: 8 }} onClick={this.handleReset}>
               {dict['main.reset']}
             </Button>
-            {showAdvanced ? <Button type="link" onClick={this.handleAdvance}>
+            {showAdvanced && !visible ? <Button type="link" onClick={this.handleAdvance}>
               楂樼骇
             </Button> : null}
           </Form.Item>
diff --git a/src/tabviews/zshare/topSearch/index.scss b/src/tabviews/zshare/topSearch/index.scss
index f5378b0..90da412 100644
--- a/src/tabviews/zshare/topSearch/index.scss
+++ b/src/tabviews/zshare/topSearch/index.scss
@@ -37,6 +37,7 @@
     }
   }
   .search-button {
+    min-height: 60px;
     .ant-btn-link, .ant-btn-link:hover, .ant-btn-link:active{
       border-color: transparent;
       span {
@@ -67,6 +68,20 @@
   .check-card-form-box {
     margin-top: 5px;
   }
+  .ant-calendar-picker-clear, .ant-calendar-picker-icon {
+    right: 8px;
+  }
+  .ant-input-affix-wrapper {
+    .ant-input-suffix {
+      opacity: 1;
+      transition: opacity 0.3s;
+    }
+  }
+  .ant-input-affix-wrapper:not(:hover) {
+    .ant-input-suffix {
+      opacity: 0;
+    }
+  }
 }
 .top-search.right {
   >.ant-row {
diff --git a/src/tabviews/zshare/topSearch/mkSelect/index.jsx b/src/tabviews/zshare/topSearch/mkSelect/index.jsx
index 725c805..53d6385 100644
--- a/src/tabviews/zshare/topSearch/mkSelect/index.jsx
+++ b/src/tabviews/zshare/topSearch/mkSelect/index.jsx
@@ -111,7 +111,7 @@
         onChange={this.selectChange}
       >
         {options.map(option =>
-          <Select.Option id={option.key} key={option.key} value={option.Value}>{option.Text}</Select.Option>
+          <Select.Option id={option.key} key={option.key} title={option.Text} value={option.Value}>{option.Text}</Select.Option>
         )}
       </Select>
     )
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index c7b4f43..cdb8a60 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -402,7 +402,7 @@
       EasyCode: _config.easyCode || '',
       Template: _config.Template || '',
       MenuName: _config.MenuName,
-      PageParam: JSON.stringify({...menu.PageParam, Template: _config.Template, OpenType: _config.OpenType}),
+      PageParam: JSON.stringify({...menu.PageParam, Template: _config.Template, OpenType: _config.OpenType, hidden: _config.hidden || 'false'}),
       LongParam: _LongParam,
       LText: _vals.func.map(item => `select '${menu.MenuID}' as MenuID,'${item.func}' as ProcName,'${item.label}' as MenuName`),
       LTexttb: _vals.table.map(item => `select '${menu.MenuID}' as MenuID,'${item}' as tbName`)
diff --git a/src/templates/comtableconfig/menuform/index.jsx b/src/templates/comtableconfig/menuform/index.jsx
index e8a2261..dd128de 100644
--- a/src/templates/comtableconfig/menuform/index.jsx
+++ b/src/templates/comtableconfig/menuform/index.jsx
@@ -1,7 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import { Form, Row, Col, Input, Select } from 'antd'
+import { Form, Row, Col, Input, Select, Switch } from 'antd'
 import { formRule } from '@/utils/option.js'
 import './index.scss'
 
@@ -50,6 +50,10 @@
       this.setState({}, () => {
         this.props.updatemenu({...config, OpenType: value})
       })
+    } else if (key === 'hidden') {
+      this.setState({}, () => {
+        this.props.updatemenu({...config, hidden: value})
+      })
     }
   }
 
@@ -96,7 +100,7 @@
     }
 
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form" id="subqazxcvbn">
+      <Form {...formItemLayout} className="comtable-menu-form" id="subqazxcvbn">
         <Row gutter={24}>
           <Col span={24}>
             <Form.Item label={dict['model.menu.level1']}>
@@ -206,6 +210,13 @@
               })(<Input placeholder="" autoComplete="off" onChange={this.changeEasyCode}/>)}
             </Form.Item>
           </Col>
+          <Col span={24}>
+            <Form.Item className="hidden-menu" labelCol={{span: 8}} wrapperCol={{span: 16}} label={'闅愯棌鑿滃崟'}>
+              <Switch checkedChildren={'鏄�'} defaultChecked={menu.PageParam ? (menu.PageParam.hidden === 'true') : false} unCheckedChildren={'鍚�'} onChange={(value) => {
+                this.selectChange('hidden', value + '')
+              }} />
+            </Form.Item>
+          </Col>
         </Row>
       </Form>
     )
diff --git a/src/templates/comtableconfig/menuform/index.scss b/src/templates/comtableconfig/menuform/index.scss
index e69de29..ef2ddb2 100644
--- a/src/templates/comtableconfig/menuform/index.scss
+++ b/src/templates/comtableconfig/menuform/index.scss
@@ -0,0 +1,7 @@
+.comtable-menu-form {
+  .hidden-menu {
+    .ant-form-item-control {
+      line-height: 25px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/templates/comtableconfig/source.jsx b/src/templates/comtableconfig/source.jsx
index d5297f9..6743863 100644
--- a/src/templates/comtableconfig/source.jsx
+++ b/src/templates/comtableconfig/source.jsx
@@ -79,7 +79,6 @@
         position: 'toolbar',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'pop',
         icon: 'plus',
         class: 'green',
@@ -93,7 +92,6 @@
         position: 'toolbar',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'pop',
         icon: 'form',
         class: 'purple',
@@ -107,7 +105,6 @@
         position: 'toolbar',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'prompt',
         icon: 'delete',
         class: 'red',
diff --git a/src/templates/formtabconfig/dragelement/index.jsx b/src/templates/formtabconfig/dragelement/index.jsx
index cbd7e79..c75052f 100644
--- a/src/templates/formtabconfig/dragelement/index.jsx
+++ b/src/templates/formtabconfig/dragelement/index.jsx
@@ -131,7 +131,6 @@
         newcard.execSuccess = 'grid'
         newcard.execError = 'never'
         newcard.popClose = 'never'
-        newcard.errorTime = 15
         newcard.verify = null
       }
       
diff --git a/src/templates/formtabconfig/source.jsx b/src/templates/formtabconfig/source.jsx
index 148e0ab..94de283 100644
--- a/src/templates/formtabconfig/source.jsx
+++ b/src/templates/formtabconfig/source.jsx
@@ -73,7 +73,6 @@
         Ot: 'notRequired',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'exec',
         icon: '',
         class: 'border-primary',
@@ -84,7 +83,6 @@
         label: '鍏抽棴',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'exec',
         pageTemplate: '',
         url: '',
diff --git a/src/templates/modalconfig/dragelement/card.jsx b/src/templates/modalconfig/dragelement/card.jsx
index f5ad414..a8ef271 100644
--- a/src/templates/modalconfig/dragelement/card.jsx
+++ b/src/templates/modalconfig/dragelement/card.jsx
@@ -65,7 +65,7 @@
 
   let formItem = null
   if (card.type === 'text') {
-    formItem = (<Input style={{marginTop: '4px'}} value={card.initval} />)
+    formItem = (<Input style={{marginTop: '4px'}} placeholder={card.placeholder || ''} value={card.initval} />)
   } else if (card.type === 'number') {
     formItem = (<InputNumber value={card.initval} precision={card.decimal} />)
   } else if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
@@ -79,7 +79,7 @@
   } else if (card.type === 'datetime') {
     formItem = (<DatePicker showTime value={card.initval ? moment().subtract(card.initval, 'days') : null} />)
   } else if (card.type === 'textarea') {
-    formItem = (<TextArea value={card.initval} autoSize={{ minRows: 2, maxRows: 6 }} />)
+    formItem = (<TextArea value={card.initval} placeholder={card.placeholder || ''} autoSize={{ minRows: 2, maxRows: 6 }} />)
   } else if (card.type === 'brafteditor') {
     formItem = (<Editor />)
   } else if (card.type === 'fileupload') {
@@ -134,7 +134,7 @@
       </div>
     } trigger="hover">
       <div className="page-card" style={{ opacity: opacity}}>
-        <div ref={node => drag(drop(node))}>
+        <div ref={node => drag(drop(node))} onDoubleClick={edit}>
           {card.type === 'split' ? formItem : <Form.Item
             className="ant-form-item"
             colon={!!_label}
diff --git a/src/templates/modalconfig/dragelement/index.scss b/src/templates/modalconfig/dragelement/index.scss
index ad41b4d..c6bc0a5 100644
--- a/src/templates/modalconfig/dragelement/index.scss
+++ b/src/templates/modalconfig/dragelement/index.scss
@@ -63,6 +63,9 @@
         line-height: 1.3;
         float: left;
       }
+      .ant-btn + .field-name {
+        float: none;
+      }
       .ant-checkbox-group {
         line-height: 40px;
         .ant-checkbox-wrapper {
diff --git a/src/templates/modalconfig/index.jsx b/src/templates/modalconfig/index.jsx
index 3ab3c92..97a26bb 100644
--- a/src/templates/modalconfig/index.jsx
+++ b/src/templates/modalconfig/index.jsx
@@ -97,22 +97,12 @@
 
     // 涓昏彍鍗曞凡鏈夐�夋嫨鐨勮〃鍚嶏紝妯℃�佹娌℃湁琛ㄥ悕鏃讹紝澶嶅埗涓昏彍鍗曡〃鍚�
     _config.tables = _config.tables.length === 0 ? _menu.tables : _config.tables
-
-    let _source = fromJS(SearchItems).toJS()
-    if (!!this.props.editTab) {
-      _source.push({
-        type: 'form',
-        label: this.state.dict['header.form.linkMain'],
-        subType: 'linkMain',
-        url: ''
-      })
-    }
+    
     _config = updateForm(_config)
 
     this.setState({
       openEdition: editAction.open_edition || '',
       menu: _menu,
-      source: _source,
       config: _config,
       originConfig: fromJS(_config).toJS(),
       modalformlist: [
@@ -611,7 +601,7 @@
   }
 
   render () {
-    const { config, source, dict } = this.state
+    const { config, dict } = this.state
 
     return (
       <div className="modal-form-board">
@@ -632,7 +622,7 @@
               </Panel>
               <Panel header={dict['header.menu.form']} key="1">
                 <div className="search-element">
-                  {source.map((item, index) => {
+                  {SearchItems.map((item, index) => {
                     return (<SourceElement key={index} content={item}/>)
                   })}
                 </div>
diff --git a/src/templates/modalconfig/source.jsx b/src/templates/modalconfig/source.jsx
index 6e831de..4c57d66 100644
--- a/src/templates/modalconfig/source.jsx
+++ b/src/templates/modalconfig/source.jsx
@@ -81,115 +81,101 @@
     type: 'form',
     label: CommonDict['model.form.text'],
     subType: 'text',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.number'],
     subType: 'number',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.select'],
     subType: 'select',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.multiselect'],
     subType: 'multiselect',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.link'],
     subType: 'link',
-    url: ''
   },
   {
     type: 'form',
     label: '寮�鍏�',
     subType: 'switch',
-    url: ''
   },
   {
     type: 'form',
     label: '澶氶�夋',
     subType: 'checkbox',
-    url: ''
   },
   {
     type: 'form',
     label: '鍗曢�夋',
     subType: 'radio',
-    url: ''
   },
   {
     type: 'form',
     label: '閫夐」鍗�',
     subType: 'checkcard',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['header.form.fileupload'],
     subType: 'fileupload',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.dateday'],
     subType: 'date',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.datemonth'],
     subType: 'datemonth',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.datetime'],
     subType: 'datetime',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.textarea'],
     subType: 'textarea',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['model.form.color'],
     subType: 'color',
-    url: ''
   },
   {
     type: 'form',
     label: '瀵屾枃鏈�',
     subType: 'brafteditor',
-    url: ''
   },
   {
     type: 'form',
     label: CommonDict['header.form.funcvar'],
     subType: 'funcvar',
-    url: ''
   },
   {
     type: 'form',
     label: '鎻愮ず',
     subType: 'hint',
-    url: ''
   },
   {
     type: 'form',
     label: '鍒嗗壊绾�',
     subType: 'split',
-    url: ''
+  },
+  {
+    type: 'form',
+    label: CommonDict['header.form.linkMain'],
+    subType: 'linkMain',
   }
 ]
 
diff --git a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
index 53a733a..587d091 100644
--- a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
@@ -14,7 +14,7 @@
   exec: ['label', 'position', 'OpenType', 'intertype', 'Ot', 'icon', 'class', 'execSuccess', 'execError'],
   excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'icon', 'class', 'sheet', 'execSuccess', 'execError'],
   excelOut: ['label', 'OpenType', 'intertype', 'icon', 'class', 'execSuccess', 'execError', 'pagination', 'search'],
-  popview: ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'tabType', 'linkTab', 'popClose'],
+  popview: ['label', 'Ot', 'OpenType', 'icon', 'class', 'position', 'tabType', 'linkTab', 'popClose', 'display', 'ratio', 'placement'],
   tab: ['label', 'Ot', 'OpenType', 'tabTemplate', 'icon', 'class', 'position'],
   innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'icon', 'class', 'position'],
   funcbutton: ['label', 'OpenType', 'funcType', 'icon', 'class']
diff --git a/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx b/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
index e640a0e..df81400 100644
--- a/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/dragaction/index.jsx
@@ -110,7 +110,6 @@
       newcard.execSuccess = 'grid'
       newcard.execError = 'never'
       newcard.popClose = 'never'
-      newcard.errorTime = 10
       newcard.verify = null
 
       if (item.subType === 'excelIn') {
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
index dd5f292..fc73196 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
@@ -49,7 +49,7 @@
     }
 
     let _sql = `Declare @${btn.sheet} table (${columns.map(item => item.Column + ' ' + item.type).join(',')},jskey nvarchar(50) )
-      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50), @retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50), @retmsg nvarchar(4000),@tbid Nvarchar(512)
       Select @ErrorCode='', @retmsg=''
     `
     
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
index f937f81..b5fadc0 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
@@ -475,6 +475,7 @@
                     </Radio.Group>
                   </Form.Item>
                 </Col>
+                <div style={{color: '#959595', fontSize: '13px', paddingTop: '30px', float: 'right'}}>鎵ц鎴愬姛鍚庣殑鍥炶皟鍑芥暟銆�</div>
                 <Col span={24} className="sql">
                   <Form.Item label={'sql'}>
                     {getFieldDecorator('sql', {
diff --git a/src/templates/sharecomponent/columncomponent/colspanform/index.jsx b/src/templates/sharecomponent/columncomponent/colspanform/index.jsx
index 8474416..653a136 100644
--- a/src/templates/sharecomponent/columncomponent/colspanform/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/colspanform/index.jsx
@@ -187,9 +187,7 @@
                   }
                 ]
               })(
-                <Select
-                  getPopupContainer={() => document.getElementById('columncolspan')}
-                >
+                <Select getPopupContainer={() => document.getElementById('columncolspan')} >
                   <Select.Option value="vertical">{this.props.dict['header.form.vertical']}</Select.Option>
                   <Select.Option value="horizontal">{this.props.dict['header.form.horizontal']}</Select.Option>
                   <Select.Option value="vertical2">{this.props.dict['header.form.vertical2']}</Select.Option>
diff --git a/src/templates/sharecomponent/columncomponent/dragcolumn/card.jsx b/src/templates/sharecomponent/columncomponent/dragcolumn/card.jsx
index f98d56e..9d6cb95 100644
--- a/src/templates/sharecomponent/columncomponent/dragcolumn/card.jsx
+++ b/src/templates/sharecomponent/columncomponent/dragcolumn/card.jsx
@@ -51,6 +51,7 @@
             {showfield ?
               <div className="ant-table-column-fields">
                 <span className="ant-table-column-title">{card.type === 'colspan' ? card.subfield : card.field}</span>
+                {card.Hide === 'true' ? <Icon style={{marginLeft: '5px', color: 'orange', fontSize: '12px'}} type="close-circle" /> : null}
               </div> : null
             }
           </span>
diff --git a/src/templates/sharecomponent/columncomponent/index.jsx b/src/templates/sharecomponent/columncomponent/index.jsx
index a37af66..50d7df2 100644
--- a/src/templates/sharecomponent/columncomponent/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/index.jsx
@@ -26,7 +26,7 @@
   state = {
     dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     columnlist: null,    // 鏄剧ず鍒�
-    showField: false,    // 鏄剧ず鍒楀瓧娈�
+    showField: true,     // 鏄剧ず鍒楀瓧娈�
     modaltype: '',       // 妯℃�佹鎺у埗
     card: null           // 缂栬緫涓厓绱�
   }
diff --git a/src/templates/sharecomponent/fieldscomponent/index.scss b/src/templates/sharecomponent/fieldscomponent/index.scss
index eb8645e..1497d32 100644
--- a/src/templates/sharecomponent/fieldscomponent/index.scss
+++ b/src/templates/sharecomponent/fieldscomponent/index.scss
@@ -8,6 +8,9 @@
       .ant-empty {
         margin: 15vh 8px;
       }
+      .ant-card-bordered {
+        border: 1px solid #e8e8e8;
+      }
     }
     .ant-modal-body::-webkit-scrollbar {
       width: 7px;
diff --git a/src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx b/src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx
index 1981f06..c0d3aa2 100644
--- a/src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx
+++ b/src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx
@@ -92,7 +92,7 @@
       </div>
     } trigger="hover">
       <div className={'page-card ' + (card.labelShow || '')} style={{ opacity: opacity}}>
-        <div ref={node => drag(drop(node))}>
+        <div ref={node => drag(drop(node))} onDoubleClick={() => editCard(id)}>
           <Form.Item
             labelCol={{xs: { span: 24 }, sm: { span: 8 }}}
             wrapperCol = {{xs: { span: 24 }, sm: { span: 16 }}}
diff --git a/src/templates/sharecomponent/searchcomponent/index.scss b/src/templates/sharecomponent/searchcomponent/index.scss
index 2b8857c..8c171b4 100644
--- a/src/templates/sharecomponent/searchcomponent/index.scss
+++ b/src/templates/sharecomponent/searchcomponent/index.scss
@@ -14,7 +14,7 @@
     position: absolute;
     z-index: 1;
     right: 20px;
-    bottom: 10px;
+    bottom: 5px;
   }
   > .ant-row {
     min-height: 65px;
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
index fa3d829..8f0f2d1 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
@@ -22,7 +22,7 @@
     }
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
index f27e974..2e3bcf9 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
@@ -431,12 +431,12 @@
             </Col>
             {usefulFields ? <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
+                id, bid, loginuid, sessionuid, userid, username, fullname, RoleID, departmentcode, organization, login_city, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
               </Form.Item>
             </Col> : null}
             {!usefulFields ? <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id
+                id, bid, loginuid, sessionuid, userid, username, fullname, RoleID, departmentcode, organization, login_city, appkey, time_id
               </Form.Item>
             </Col> : null}
             {!usefulFields ? <Col span={8} style={{whiteSpace: 'nowrap'}}>
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
index 003b030..3b8c1e9 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/utils.jsx
@@ -18,7 +18,7 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
@@ -142,7 +142,7 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
index 332934c..5fdbc99 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
@@ -18,7 +18,7 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/templates/subtableconfig/source.jsx b/src/templates/subtableconfig/source.jsx
index 4ccd3bf..d8c8aa3 100644
--- a/src/templates/subtableconfig/source.jsx
+++ b/src/templates/subtableconfig/source.jsx
@@ -78,7 +78,6 @@
         position: 'toolbar',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'pop',
         icon: 'plus',
         class: 'green',
@@ -92,7 +91,6 @@
         position: 'grid',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'pop',
         icon: 'form',
         class: 'purple',
@@ -106,7 +104,6 @@
         position: 'toolbar',
         execSuccess: 'grid',
         execError: 'never',
-        errorTime: 15,
         OpenType: 'prompt',
         icon: 'delete',
         class: 'red',
diff --git a/src/templates/zshare/customscript/index.jsx b/src/templates/zshare/customscript/index.jsx
index e2db585..aaa8368 100644
--- a/src/templates/zshare/customscript/index.jsx
+++ b/src/templates/zshare/customscript/index.jsx
@@ -377,7 +377,7 @@
             </Col>
             <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id, orderBy{setting.laypage === 'true' ? ', pageSize, pageIndex': ''}{usefulFields ? ', ' + usefulFields : ''}
+                id, bid, loginuid, sessionuid, userid, username, fullname, RoleID, departmentcode, organization, login_city, appkey, time_id, orderBy{setting.laypage === 'true' ? ', pageSize, pageIndex': ''}{usefulFields ? ', ' + usefulFields : ''}
               </Form.Item>
             </Col>
             <Col span={10} className="quick-add">
diff --git a/src/templates/zshare/editTable/index.jsx b/src/templates/zshare/editTable/index.jsx
index 5e1d00b..b431aca 100644
--- a/src/templates/zshare/editTable/index.jsx
+++ b/src/templates/zshare/editTable/index.jsx
@@ -14,6 +14,7 @@
 
 let eTDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 const EditableContext = React.createContext()
+const { confirm } = Modal
 let dragingIndex = -1
 const { Paragraph } = Typography
 
@@ -185,12 +186,11 @@
       operation = {
         title: (<div>
           {eTDict['model.operation']}
-          {actions.includes('copy') ? (
-            <span className="copy-control">
-              <Icon type="copy" onClick={() => this.copy()} />
-              <Icon type="snippets" onClick={this.paste} />
-            </span>
-          ) : null}
+          <span className="copy-control">
+            {actions.includes('copy') ? <Icon type="copy" title="澶嶅埗" onClick={() => this.copy()} /> : null}
+            {actions.includes('copy') ? <Icon type="snippets" title="绮樿创" onClick={this.paste} /> : null}
+            {actions.includes('clear') ? <Icon type="delete" title="娓呯┖" onClick={this.clear} /> : null}
+          </span>
         </div>),
         dataIndex: 'operation',
         width: '140px',
@@ -271,6 +271,21 @@
     this.setState({ editingKey: '' })
   }
 
+  clear = () => {
+    const _this = this
+    
+    confirm({
+      title: '纭畾娓呯┖鍒楄〃鍚楋紵',
+      content: '',
+      onOk() {
+        _this.setState({ data: [], editingKey: '' }, () => {
+          _this.props.onChange([])
+        })
+      },
+      onCancel() {}
+    })
+  }
+
   copy = (item) => {
     const { type } = this.props
     const { data } = this.state
diff --git a/src/templates/zshare/editTable/index.scss b/src/templates/zshare/editTable/index.scss
index 8713c7f..954f3ab 100644
--- a/src/templates/zshare/editTable/index.scss
+++ b/src/templates/zshare/editTable/index.scss
@@ -87,6 +87,10 @@
     .anticon-snippets {
       color: purple;
     }
+    .anticon-delete {
+      margin-left: 7px;
+      color: #ff4d4f;
+    }
   }
 }
 
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 2b1c010..b9d30a0 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -1323,7 +1323,53 @@
       tooltip: '褰撻�夋嫨鎺у埗瀛楁锛屼笖瀛楁鍊间笌鎺у埗鍊肩浉绛夋椂锛屾寜閽細绂佺敤锛屽涓�肩敤閫楀彿鍒嗛殧銆�',
       initVal: card.controlVal || '',
       required: false
-    }
+    },
+    {
+      type: 'radio',
+      key: 'display',
+      label: '鏄剧ず鏂瑰紡',
+      initVal: card.display || 'modal',
+      required: true,
+      options: [{
+        value: 'modal',
+        text: '妯℃�佹'
+      }, {
+        value: 'drawer',
+        text: '鎶藉眽'
+      }]
+    },
+    {
+      type: 'number',
+      key: 'ratio',
+      min: 1,
+      max: 24,
+      precision: 0,
+      label: '姣斾緥',
+      initVal: card.ratio || 85,
+      tooltip: '灏忎簬100涓哄搴︼紙鎴栭珮搴︼級鐧惧垎姣旓紝澶т簬100涓哄儚绱犲�笺��',
+      required: true
+    },
+    {
+      type: 'radio',
+      key: 'placement',
+      label: '鎶藉眽鏂瑰悜',
+      initVal: card.placement || 'right',
+      tooltip: '浣跨敤鎶藉眽鏃舵湁鏁堛��',
+      required: false,
+      options: [{
+        value: 'right',
+        text: '鍙充晶'
+      }, {
+        value: 'left',
+        text: '宸︿晶'
+      }, {
+        value: 'top',
+        text: '涓婁晶'
+      }, {
+        value: 'bottom',
+        text: '涓嬩晶'
+      }]
+    },
   ]
 }
 
@@ -2182,6 +2228,9 @@
   }, {
     value: 'split',
     text: '鍒嗛殧绾�'
+  }, {
+    value: 'linkMain',
+    text: Formdict['header.form.linkMain']
   }]
 
   let _fieldlength = 50
@@ -2236,11 +2285,6 @@
       value: 'split',
       text: '鍒嗛殧绾�'
     }]
-  } else if (subtable) {
-    _openType.push({
-      value: 'linkMain',
-      text: Formdict['header.form.linkMain']
-    })
   }
 
   if (['fileupload', 'multiselect', 'checkbox'].includes(card.type)) {
@@ -2555,6 +2599,9 @@
       }, {
         value: 'letter&number',
         text: Formdict['header.form.letter&number']
+      }, {
+        value: 'phone',
+        text: '鎵嬫満鍙�'
       }]
     },
     {
@@ -2938,6 +2985,14 @@
     },
     {
       type: 'text',
+      key: 'placeholder',
+      label: '鎻愮ず淇℃伅',
+      tooltip: '瀛楁棰勬湡鍊肩殑鎻愮ず淇℃伅銆�',
+      initVal: card.placeholder || '',
+      required: false
+    },
+    {
+      type: 'text',
       key: 'emptyText',
       label: '绌哄�兼枃鏈�',
       tooltip: '绌哄�肩殑鎻愮ず鏂囨湰锛岄�夋嫨璁剧疆绌哄�兼椂鏈夋晥锛岄粯璁ゅ�间负銆婄┖銆嬨��',
diff --git a/src/templates/zshare/modalform/index.jsx b/src/templates/zshare/modalform/index.jsx
index a8bb337..c9b59d6 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -16,7 +16,7 @@
 const DataTable = asyncComponent(() => import('./datatable'))
 
 const modalTypeOptions = {
-  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'scan', 'splitline'],
+  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'scan', 'splitline', 'placeholder'],
   number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter', 'cursor', 'splitline'],
   select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter', 'splitline'],
   checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'tooltip', 'extra', 'splitline', 'arrange'],
@@ -29,7 +29,7 @@
   date: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'mode', 'splitline'],
   datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'splitline'],
   datetime: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'declareType', 'mode', 'splitline'],
-  textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'span', 'labelwidth', 'maxRows', 'encryption', 'interception', 'tooltip', 'extra', 'count'],
+  textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'span', 'labelwidth', 'maxRows', 'encryption', 'interception', 'tooltip', 'extra', 'count', 'placeholder'],
   color: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
   hint: ['label', 'type', 'blacklist', 'message', 'span', 'labelwidth', 'splitline'],
   split: ['label', 'type'],
diff --git a/src/templates/zshare/verifycard/billcodeform/index.jsx b/src/templates/zshare/verifycard/billcodeform/index.jsx
index bdf18fc..a885185 100644
--- a/src/templates/zshare/verifycard/billcodeform/index.jsx
+++ b/src/templates/zshare/verifycard/billcodeform/index.jsx
@@ -30,23 +30,24 @@
   UNSAFE_componentWillMount() {
     let _modularDetail = []
     let _billFields = []
+
     if (this.props.modular.length > 0) {
       _modularDetail = this.props.modularDetail.filter(item => item.BID === this.props.modular[0].ID)
     }
 
     let fieldMap = new Map()
     this.props.fields.forEach(_field => {
-      if (_field.type === 'text' && !fieldMap.has(_field.field)) {
+      if (_field.type === 'text' && !fieldMap.has(_field.field.toLowerCase())) {
         _billFields.push(_field)
-        fieldMap.set(_field.field, true)
+        fieldMap.set(_field.field.toLowerCase(), true)
       }
     })
 
     if (this.props.btn.Ot !== 'notRequired') {
       this.props.columns.forEach(_field => {
-        if (_field.type === 'text' && !fieldMap.has(_field.field)) {
+        if (_field.type === 'text' && !fieldMap.has(_field.field.toLowerCase())) {
           _billFields.push(_field)
-          fieldMap.set(_field.field, true)
+          fieldMap.set(_field.field.toLowerCase(), true)
         }
       })
     }
diff --git a/src/templates/zshare/verifycard/index.jsx b/src/templates/zshare/verifycard/index.jsx
index 5e448f2..9346ec4 100644
--- a/src/templates/zshare/verifycard/index.jsx
+++ b/src/templates/zshare/verifycard/index.jsx
@@ -624,9 +624,9 @@
         resolve(_fields)
       }
     }).then(_fields => {
-      let _usefulfields = ['BID', 'ID', 'LoginUID', 'SessionUid', 'UserID', 'Appkey', 'UserName', 'FullName', 'login_city', 'BillCode', 'BVoucher', 'FIBVoucherDate', 'FiYear', 'ModularDetailCode']
-      let _declare = ['@UserName nvarchar(50)', '@FullName nvarchar(50)', '@login_city nvarchar(50)', '@ErrorCode nvarchar(50)', '@retmsg nvarchar(4000)', '@BillCode nvarchar(50)', '@BVoucher nvarchar(50)', '@FIBVoucherDate nvarchar(50)', '@FiYear nvarchar(50)', '@ModularDetailCode nvarchar(50)']
-      let _select = ['@UserName=\'\'', '@FullName=\'\'', '@login_city=\'\'', '@ErrorCode=\'\'', '@retmsg=\'\'', '@BillCode=\'\'', '@BVoucher=\'\'', '@FIBVoucherDate=\'\'', '@FiYear=\'\'', '@ModularDetailCode=\'\'']
+      let _usefulfields = ['BID', 'ID', 'LoginUID', 'SessionUid', 'UserID', 'Appkey', 'UserName', 'FullName', 'RoleID', 'departmentcode', 'organization', 'login_city', 'BillCode', 'BVoucher', 'FIBVoucherDate', 'FiYear', 'ModularDetailCode']
+      let _declare = ['@UserName nvarchar(50)', '@FullName nvarchar(50)', '@RoleID nvarchar(512)', '@departmentcode nvarchar(50)', '@organization nvarchar(50)', '@login_city nvarchar(50)', '@ErrorCode nvarchar(50)', '@retmsg nvarchar(4000)', '@BillCode nvarchar(50)', '@BVoucher nvarchar(50)', '@FIBVoucherDate nvarchar(50)', '@FiYear nvarchar(50)', '@ModularDetailCode nvarchar(50)']
+      let _select = ['@UserName=\'\'', '@FullName=\'\'', '@RoleID=\'\'', '@departmentcode=\'\'', '@organization=\'\'', '@login_city=\'\'', '@ErrorCode=\'\'', '@retmsg=\'\'', '@BillCode=\'\'', '@BVoucher=\'\'', '@FIBVoucherDate=\'\'', '@FiYear=\'\'', '@ModularDetailCode=\'\'']
       let fieldArr = _usefulfields.map(_f => _f.toLowerCase())
       let hasBid = false
 
@@ -671,9 +671,6 @@
 
       if (!hasBid) { // 鍞竴鎬ч獙璇佹坊鍔燘ID
         uniqueFields.unshift({ uuid: 'BID', field: 'BID', label: 'BID', type: 'text' })
-      }
-
-      if (!hasBid && (card.sqlType === 'insert' || card.sqlType === 'insertOrUpdate')) { // 琛ㄥ崟涓鍔燘ID
         _fields.unshift({ uuid: 'BID', field: 'BID', label: 'BID', type: 'text' })
         fieldArr.push('bid')
         _declare.push(`@bid nvarchar(50)`)
@@ -831,7 +828,7 @@
         }
 
         _form = _form.join(', ')
-        _updatesql = `update ${card.sql} set ${_form} where ${config.setting.primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
+        _updatesql = `update ${card.sql} set ${_form} where ${config.setting.primaryKey || 'id'}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
       }
 
       if (card.sqlType === 'insert') {
@@ -840,7 +837,7 @@
         _defaultsql = _updatesql
       } else if (card.sqlType === 'insertOrUpdate') {
         _defaultsql += `select @tbid=''
-          select @tbid='X' from ${card.sql} where ${config.setting.primaryKey}=@ID@
+          select @tbid='X' from ${card.sql} where ${config.setting.primaryKey || 'id'}=@ID@
           if @tbid=''
             begin
             ${_insertsql}
@@ -855,7 +852,7 @@
         if (_verify.voucher && _verify.voucher.enabled) {
           _voucher = ',BVoucher=@BVoucher,FIBVoucherDate=@FIBVoucherDate,FiYear=@FiYear'
         }
-        _defaultsql = `update ${card.sql} set deleted=1,modifydate=getdate(),modifyuserid=@userid@${_voucher} where ${config.setting.primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
+        _defaultsql = `update ${card.sql} set deleted=1,modifydate=getdate(),modifyuserid=@userid@${_voucher} where ${config.setting.primaryKey || 'id'}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
       } else if (card.sqlType === 'delete') {
         let _msg = ''
         if (columns && columns.length > 0 && card.Ot !== 'notRequired' && card.Ot !== 'requiredOnce') {
@@ -867,7 +864,7 @@
             _index++
           })
         }
-        _defaultsql += `insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${card.sql} 鏁版嵁: ${_msg}${config.setting.primaryKey}='+@ID@,200),@userid@,@username,@fullname delete ${card.sql} where ${config.setting.primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
+        _defaultsql += `insert into snote (remark,createuserid,CreateUser,CreateStaff) select left('鍒犻櫎琛�:${card.sql} 鏁版嵁: ${_msg}${config.setting.primaryKey || 'id'}='+@ID@,200),@userid@,@username,@fullname delete ${card.sql} where ${config.setting.primaryKey}${card.Ot !== 'requiredOnce' ? '=@ID@' : ' in (select ID  from dbo.SplitComma(@ID@))'};`
       }
 
       let _columns = []
diff --git a/src/utils/utils-custom.js b/src/utils/utils-custom.js
index 058d369..601ab7f 100644
--- a/src/utils/utils-custom.js
+++ b/src/utils/utils-custom.js
@@ -97,6 +97,11 @@
           value: item.uuid,
           label: item.name
         }
+      } else if (item.type === 'form') { // 鏁版嵁鏍煎紡锛屽瓨鍦ㄦ暟鎹簮
+        return {
+          value: item.uuid,
+          label: item.name
+        }
       } else if (item.type === 'tabs') {
         let _item = {
           value: item.uuid,
@@ -372,6 +377,13 @@
             })
           }
         })
+      } else if (item.type === 'balcony') {
+        if (item.elements) {
+          item.elements = item.elements.map(cell => {
+            cell.uuid = this.getuuid()
+            return cell
+          })
+        }
       } else if (item.type === 'table' && item.subtype === 'normaltable' && item.cols) {
         let loopCol = (col) => {
           col.subcols = col.subcols.map(c => {
@@ -426,7 +438,24 @@
           return cell
         })
       }
-      if (item.search) {
+      if (item.type === 'topbar') {
+        if (item.search && item.search.fields) {
+          item.search.fields = item.search.fields.map(cell => {
+            cell.uuid = this.getuuid()
+            return cell
+          })
+        }
+        if (item.search && item.search.groups) {
+          item.search.groups = item.search.groups.map(cell => {
+            cell.uuid = this.getuuid()
+            cell.fields = cell.fields.map(m => {
+              m.uuid = this.getuuid()
+              return m
+            })
+            return cell
+          })
+        }
+      } else if (item.search) {
         item.search = item.search.map(cell => {
           cell.uuid = this.getuuid()
           return cell
diff --git a/src/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index 2584cd9..b1bab66 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -92,6 +92,9 @@
 
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -103,8 +106,8 @@
     let _customScript = ''
     
     if (setting.customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${setting.customScript}
       `
     }
@@ -189,8 +192,12 @@
 
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
-      _customScript &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鑷畾涔夎剼鏈�*/\n` : ''}${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
-      LText &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鏁版嵁婧�*/\n` : ''}` + LText)
+      _customScript &&  console.info(`${setting.$name ? `/*${setting.$name} 鑷畾涔夎剼鏈�*/\n` : ''}${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
+      LText &&  console.info(`${setting.$name ? `/*${setting.$name} 鏁版嵁婧�*/\n` : ''}` + LText)
+    }
+
+    if (setting.$name) {
+      param.menuname = setting.$name
     }
 
     param.custom_script = Utils.formatOptions(_customScript)
@@ -225,6 +232,9 @@
     let _customScript = ''
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -233,8 +243,8 @@
     }
     
     if (setting.customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${setting.customScript}
       `
     }
@@ -298,8 +308,12 @@
 
     // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
     if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
-      _customScript &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鑷畾涔夎剼鏈� 缁熻鏌ヨ*/\n` : ''}${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
-      LText &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鏁版嵁婧� 缁熻鏌ヨ*/\n` : ''}` + LText)
+      _customScript &&  console.info(`${setting.$name ? `/*${setting.$name} 鑷畾涔夎剼鏈� 缁熻鏌ヨ*/\n` : ''}${LText ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
+      LText &&  console.info(`${setting.$name ? `/*${setting.$name} 鏁版嵁婧� 缁熻鏌ヨ*/\n` : ''}` + LText)
+    }
+
+    if (setting.$name) {
+      param.menuname = setting.$name
     }
     
     param.custom_script = Utils.formatOptions(_customScript)
@@ -367,6 +381,9 @@
     let sql = ''
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -382,8 +399,8 @@
 
     if (sql) {
       sql = `/*鍓嶇疆鑴氭湰*/
-        declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+        declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${sql}
         aaa:
           if @ErrorCode!=''
@@ -449,6 +466,9 @@
 
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let RoleID = sessionStorage.getItem('role_id') || ''
+    let departmentcode = sessionStorage.getItem('departmentcode') || ''
+    let organization = sessionStorage.getItem('organization') || ''
     let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
@@ -456,8 +476,8 @@
       fullName = sessionStorage.getItem('CloudFullName') || ''
     }
 
-    let _prevCustomScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        Select @ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+    let _prevCustomScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        Select @ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${errSql}
     `
     let _backCustomScript = `
@@ -626,8 +646,8 @@
 
   // 娴嬭瘯绯荤粺鎵撳嵃鏌ヨ璇彞
   if ((options.sysType === 'local' && !window.GLOB.systemType) || window.debugger === true) {
-    _customScript &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鑷畾涔夎剼鏈�*/\n` : ''}${_dataresource ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
-    _dataresource &&  console.info(`${setting.$name ? `/*缁勪欢-${setting.$name} 鏁版嵁婧�*/\n` : ''}` + _dataresource)
+    _customScript &&  console.info(`${setting.$name ? `/*${setting.$name} 鑷畾涔夎剼鏈�*/\n` : ''}${_dataresource ? '' : '/*涓嶆墽琛岄粯璁ql*/\n'}${_customScript}`)
+    _dataresource &&  console.info(`${setting.$name ? `/*${setting.$name} 鏁版嵁婧�*/\n` : ''}` + _dataresource)
   }
 
   return {
@@ -651,6 +671,9 @@
   let diffUser = false
   let userName = sessionStorage.getItem('User_Name') || ''
   let fullName = sessionStorage.getItem('Full_Name') || ''
+  let RoleID = sessionStorage.getItem('role_id') || ''
+  let departmentcode = sessionStorage.getItem('departmentcode') || ''
+  let organization = sessionStorage.getItem('organization') || ''
   let city = sessionStorage.getItem('city') || ''
 
   if (sessionStorage.getItem('isEditState') === 'true') {
@@ -662,8 +685,8 @@
     let _script = item.script
 
     if (index === 0) {
-      _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50)
-        select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50)
+        select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
         ${_script}
       `
     }
diff --git a/src/utils/utils.js b/src/utils/utils.js
index 93d0120..d3948e4 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -765,6 +765,9 @@
   let keys = ['delete', 'drop', 'insert', 'truncate', 'update']
   let userName = sessionStorage.getItem('User_Name') || ''
   let fullName = sessionStorage.getItem('Full_Name') || ''
+  let RoleID = sessionStorage.getItem('role_id') || ''
+  let departmentcode = sessionStorage.getItem('departmentcode') || ''
+  let organization = sessionStorage.getItem('organization') || ''
   let city = sessionStorage.getItem('city') || ''
   let _sheet = item.sheet
 
@@ -1012,9 +1015,9 @@
     _sql = `
       /* 绯荤粺鐢熸垚 */
       declare @${sheet} table (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
-      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
       
-      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
       ${_initCustomScript}
       `
     _sqlInsert = `Insert into @${sheet} (${fields},jskey,BID)`
@@ -1047,9 +1050,9 @@
     _sql = `
       /* 绯荤粺鐢熸垚 */
       declare @${sheet} table (jskey nvarchar(50))
-      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
       
-      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}'
       `
   }
 
@@ -1188,6 +1191,10 @@
       if (!_initvars.includes(_key)) {
         let _val = datavars.hasOwnProperty(_key) ? datavars[_key] : ''
 
+        if (col.datatype && /^date/ig.test(col.datatype) && !_val) {
+          _val = '1900-01-01'
+        }
+
         _initvars.push(_key)
         _initColfields.push(`@${_key}='${_val}'`)
       }
@@ -1235,7 +1242,7 @@
     _declarefields = ',' + _declarefields
   }
   _sql = `/* 绯荤粺鐢熸垚 */
-      Declare @tbid nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50), @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ModularDetailCode nvarchar(50)${_declarefields}
+      Declare @tbid nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@BillCode nvarchar(50),@BVoucher nvarchar(50),@FIBVoucherDate nvarchar(50), @FiYear nvarchar(50), @UserName nvarchar(50),@FullName nvarchar(50),@RoleID nvarchar(512),@departmentcode nvarchar(50),@organization nvarchar(50),@login_city nvarchar(50),@ModularDetailCode nvarchar(50)${_declarefields}
     `
 
   // 琛ㄥ崟鍙橀噺璧嬪��
@@ -1269,6 +1276,9 @@
 
   let userName = sessionStorage.getItem('User_Name') || ''
   let fullName = sessionStorage.getItem('Full_Name') || ''
+  let RoleID = sessionStorage.getItem('role_id') || ''
+  let departmentcode = sessionStorage.getItem('departmentcode') || ''
+  let organization = sessionStorage.getItem('organization') || ''
   let city = sessionStorage.getItem('city') || ''
 
   if (sessionStorage.getItem('isEditState') === 'true') {
@@ -1279,7 +1289,7 @@
   // 鍒濆鍖栧嚟璇佸強鐢ㄦ埛淇℃伅瀛楁
   _sql += `
       /* 鍑瘉鍙婄敤鎴蜂俊鎭垵濮嬪寲璧嬪�� */
-      select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}', @BillCode='', @ModularDetailCode=''
+      select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @RoleID='${RoleID}', @departmentcode='${departmentcode}', @organization='${organization}', @login_city='${city}', @BillCode='', @ModularDetailCode=''
       `
 
   if (retmsg) {
@@ -1754,6 +1764,9 @@
     _sql += `
       aaa: if @ErrorCode!=''
       insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@`
+  } else if (btn.output) {
+    _sql += `
+      aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg,${btn.output} as mk_b_id`
   } else {
     _sql += `
       aaa: select @ErrorCode as ErrorCode,@retmsg as retmsg`
diff --git a/src/views/appmanage/index.jsx b/src/views/appmanage/index.jsx
index c238f65..8deb437 100644
--- a/src/views/appmanage/index.jsx
+++ b/src/views/appmanage/index.jsx
@@ -334,6 +334,8 @@
       } else if (res.VType === 'role') {
         param.VType = 'mob_roletree'
         param.upid = md5(window.GLOB.appkey + kei_no + kei_no_detail + lang)
+      } else if (res.VType === 'app') {
+        param.VType = 'Vkei'
       }
 
       Api.getCloudConfig(param).then(result => {
@@ -1057,7 +1059,7 @@
                       <Col span={12}>
                         <div className="app-item">
                           <div className="label">缃戠珯鍥炬爣:</div>
-                          <div className="content">{item.favicon ? <img style={{width: '30px', height: '30px'}} src={item.favicon} alt="" /> : '鏃�'}</div>
+                          <div className="content">{item.favicon ? <img style={{width: '18px', height: '18px'}} src={item.favicon} alt="" /> : '鏃�'}</div>
                         </div>
                       </Col>
                     </Row>
diff --git a/src/views/appmanage/scriptform/index.jsx b/src/views/appmanage/scriptform/index.jsx
index 999599d..50b392e 100644
--- a/src/views/appmanage/scriptform/index.jsx
+++ b/src/views/appmanage/scriptform/index.jsx
@@ -164,6 +164,7 @@
                 }]
               })(
                 <Select onChange={this.changeType}>
+                  <Select.Option value="app">搴旂敤</Select.Option>
                   <Select.Option value="subapp">瀛愬簲鐢�</Select.Option>
                   <Select.Option value="view">椤甸潰</Select.Option>
                   <Select.Option value="role">鏉冮檺鏍�</Select.Option>
diff --git a/src/views/appmanage/submutilform/index.jsx b/src/views/appmanage/submutilform/index.jsx
index 137c862..3d1a936 100644
--- a/src/views/appmanage/submutilform/index.jsx
+++ b/src/views/appmanage/submutilform/index.jsx
@@ -5,7 +5,7 @@
 import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
 
-const FileUpload = asyncComponent(() => import('@/tabviews/zshare/fileupload'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
 
 class MainSearch extends Component {
   static propTpyes = {
@@ -73,12 +73,6 @@
         xs: { span: 24 },
         sm: { span: 16 }
       }
-    }
-
-    let file = ''
-
-    if (type === 'edit' && card && card.favicon) {
-      file = card.favicon
     }
 
     return (
@@ -202,13 +196,10 @@
           <Col span={12}>
             <Form.Item label="鍥炬爣">
               {getFieldDecorator('favicon', {
-                initialValue: file
-              })(<FileUpload config={{
-                initval: file,
-                suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico',
-                maxfile: 1,
-                fileType: 'text'
-              }}/>)}
+                initialValue: card ? card.favicon : ''
+              })(
+                <SourceComponent type="picture" placement="right"/>
+              )}
             </Form.Item>
           </Col>
         </Row>
diff --git a/src/views/appmanage/submutilform/index.scss b/src/views/appmanage/submutilform/index.scss
index b2364d9..f6ceb5b 100644
--- a/src/views/appmanage/submutilform/index.scss
+++ b/src/views/appmanage/submutilform/index.scss
@@ -5,4 +5,12 @@
     color: #c49f47;
     margin-right: 3px;
   }
+  .mk-source-wrap {
+    .ant-radio-button-wrapper:last-child {
+      display: none;
+    }
+    .ant-radio-button-wrapper:not(:first-child) {
+      border-radius: 0 4px 4px 0;
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/views/design/sidemenu/config.jsx b/src/views/design/sidemenu/config.jsx
index 9cca512..804b697 100644
--- a/src/views/design/sidemenu/config.jsx
+++ b/src/views/design/sidemenu/config.jsx
@@ -97,6 +97,14 @@
   //   MenuID: '1606794243739c5ihs58lucpskp3r4s2',
   //   MenuNo: 's_custom_componentsM',
   //   MenuName: '鑷畾涔夌粍浠�',
+  }, {
+    src: '',
+    systems: ['production', 'local'],
+    PageParam: {OpenType: 'newtab', Template: 'ManageTable'},
+    type: 'ManageTable',
+    MenuID: '15827879285193g85m3i2uprektpgmpf',
+    MenuNo: 'bd_mes_techM',
+    MenuName: '宸ヨ壓涓绘暟鎹�',
   }]
 }, {
   MenuID: 'systemManageViewInterface',
diff --git a/src/views/login/index.jsx b/src/views/login/index.jsx
index 3598767..5315abd 100644
--- a/src/views/login/index.jsx
+++ b/src/views/login/index.jsx
@@ -29,6 +29,7 @@
     dict: iszhCN ? zhCN : enUS,
     isDisabled: false,
     auth: false,
+    authError: '',
     loginlogo: window.GLOB.loginlogo || '',
     bgImage: window.GLOB.bgImage || '',
     platName: window.GLOB.platName || '',
@@ -40,9 +41,7 @@
     syncApp: false,
     loginWays: null,
     touristLogin: false,
-    syncing: false,
-    ipAddress: '',
-    city: ''
+    syncing: false
   }
 
   UNSAFE_componentWillMount() {
@@ -86,13 +85,15 @@
    * @param {Object} param 鐢ㄦ埛鍚嶅瘑鐮佺瓑淇℃伅
    */
   async loginsubmit (param) {
-    const { ipAddress, city } = this.state
     if (options.sysType === 'local' && !window.GLOB.mainSystemApi) { // 涓氬姟绯荤粺蹇呴』璁剧疆鍗曠偣鍦板潃
       Modal.warning({
         title: '鏈缃崟鐐规湇鍔″櫒鍦板潃锛岃鑱旂郴绠$悊鍛橈紒'
       })
       return
     }
+
+    let city = sessionStorage.getItem('city') || ''
+    let ipAddress = sessionStorage.getItem('ipAddress') || ''
 
     // 鐧诲綍鎻愪氦
     let res = await Api.getusermsg(param.username, param.password, false, ipAddress, city)
@@ -106,6 +107,8 @@
       localStorage.setItem('localDataM', res.dataM ? 'true' : '')
       localStorage.setItem('debug', res.debug || '')
       localStorage.setItem('role_id', res.role_id || '')
+      localStorage.setItem('departmentcode', res.departmentcode || '')
+      localStorage.setItem('organization', res.organization || '')
       localStorage.setItem('localRole_id', res.role_id || '')
 
       localStorage.setItem('lang', 'zh-CN')
@@ -180,13 +183,15 @@
   }
 
   async phoneloginsubmit (param) {
-    const { ipAddress, city } = this.state
     if (options.sysType === 'local' && !window.GLOB.mainSystemApi) { // 涓氬姟绯荤粺蹇呴』璁剧疆鍗曠偣鍦板潃
       Modal.warning({
         title: '鏈缃崟鐐规湇鍔″櫒鍦板潃锛岃鑱旂郴绠$悊鍛橈紒'
       })
       return
     }
+
+    let city = sessionStorage.getItem('city') || ''
+    let ipAddress = sessionStorage.getItem('ipAddress') || ''
 
     // 鐧诲綍鎻愪氦
     let res = await Api.getphoneusermsg(param.phone, param.vercode, false, ipAddress, city)
@@ -200,6 +205,8 @@
       sessionStorage.setItem('localDataM', res.dataM ? 'true' : '')
       sessionStorage.setItem('debug', res.debug || '')
       sessionStorage.setItem('role_id', res.role_id || '')
+      sessionStorage.setItem('departmentcode', res.departmentcode || '')
+      sessionStorage.setItem('organization', res.organization || '')
       sessionStorage.setItem('localRole_id', res.role_id || '')
 
       let _url = window.location.href.split('#')[0]
@@ -233,17 +240,106 @@
     }
   }
 
-  componentDidMount () {
-    // 鑾峰彇ip鍙婂煄甯備俊鎭�
-    let ipurl = window.atob('aHR0cHM6Ly9lcGMubWs5a$mkC5jbi93ZWJhcGkvaXBsb2M='.replace(/\$mk/ig, ''))
-    Api.directRequest(ipurl, 'get', null, 'true').then(res => {
-      if (!res || !res.location) return
-      sessionStorage.setItem('city', res.location)
-      this.setState({
-        ipAddress: res.ip || '',
-        city: res.location
+  authLogin = (appid, openid, memberid, scanId) => {
+    if (options.sysType === 'local' && !window.GLOB.mainSystemApi) { // 涓氬姟绯荤粺蹇呴』璁剧疆鍗曠偣鍦板潃
+      Modal.warning({
+        title: '鏈缃崟鐐规湇鍔″櫒鍦板潃锛岃鑱旂郴绠$悊鍛橈紒'
       })
+      return
+    }
+    Api.getTouristMsg(appid, openid, memberid, scanId).then(res => {
+      if (res.status) {
+        sessionStorage.setItem('UserID', res.UserID)
+        sessionStorage.setItem('LoginUID', res.LoginUID)
+        sessionStorage.setItem('User_Name', res.UserName)
+        sessionStorage.setItem('Full_Name', res.FullName)
+        sessionStorage.setItem('avatar', res.icon || '')
+        sessionStorage.setItem('dataM', res.dataM ? 'true' : '')
+        sessionStorage.setItem('localDataM', res.dataM ? 'true' : '')
+        sessionStorage.setItem('debug', res.debug || '')
+        sessionStorage.setItem('role_id', res.role_id || '')
+        sessionStorage.setItem('departmentcode', res.departmentcode || '')
+        sessionStorage.setItem('organization', res.organization || '')
+        sessionStorage.setItem('localRole_id', res.role_id || '')
+  
+        sessionStorage.removeItem('visitorUserID')
+        sessionStorage.removeItem('visitorLoginUID')
+  
+        let _history = sessionStorage.getItem('history')
+        if (_history) {
+          sessionStorage.removeItem('history')
+          // 鏌ョ湅鏄惁涓哄叾浠栭〉闈㈣烦杞紝璺緞瀛樺湪鏃讹紝璺冲洖鍘熼〉闈�
+          this.props.history.replace(_history)
+        } else {
+          this.props.history.replace('/main')
+        }
+      } else if (res.ErrCode === 'Need_Get_Appkey' && options.sysType === 'SSO') {
+        message.warning('搴旂敤灏氭湭鍒涘缓锛岃鍚戜簯绔悓姝ュ簲鐢紒')
+  
+        this.setState({
+          isDisabled: false,
+          syncApp: true
+        })
+      } else {
+        message.warning(res.message)
+        this.setState({
+          isDisabled: false
+        })
+      }
     })
+  }
+
+  jsonp(url) {
+    return new Promise((resolve, reject) => {
+      window.jsonCallBack = (result) => {
+        resolve(result)
+      }
+
+      const JSONP = document.createElement('script')
+      JSONP.type = 'text/javascript'
+      JSONP.src = url
+
+      document.getElementsByTagName('head')[0].appendChild(JSONP)
+
+      setTimeout(() => {
+        document.getElementsByTagName('head')[0].removeChild(JSONP)
+      },500)
+    })
+}
+
+  componentDidMount () {
+    // md5("/ws/location/v1/ip?callback=callbackFunction&key=key&output=jsonp secret key")
+    // md5("/ws/location/v1/ip?callback=callbackFunction&key=BA7BZ-4QB65-LFCIA-QPDA6-4G6O7-MJB4Q&output=jsonpuThL4ZM3XOj642ksEQh76tyHFjh4")
+
+    // 鑾峰彇ip鍙婂煄甯備俊鎭�
+    // let ipurl = window.atob('aHR0cHM6Ly9lcGMubWs5a$mkC5jbi93ZWJhcGkvaXBsb2M='.replace(/\$mk/ig, ''))
+    // Api.directRequest(ipurl, 'get', null, 'true').then(res => {
+    //   if (!res || !res.ip) return
+    //   sessionStorage.setItem('ipAddress', res.ip)
+    // })
+    const _addressUrl = window.location.href.split('#')[0] + 'queryAddress'
+
+    if (_addressUrl !== 'true') {
+      sessionStorage.setItem('city', '')
+      sessionStorage.setItem('ipAddress', '')
+    } else {
+      window.callbackFunction = (res) => {
+        if (res.result && res.result.ad_info) {
+          sessionStorage.setItem('city', res.result.ad_info.city)
+          sessionStorage.setItem('ipAddress', res.result.ip)
+        }
+      }
+  
+      const JSONP = document.createElement('script')
+      JSONP.type = 'text/javascript'
+      JSONP.src = 'https://apis.map.qq.com/ws/location/v1/ip?callback=callbackFunction&key=BA7BZ-4QB65-LFCIA-QPDA6-4G6O7-MJB4Q&output=jsonp&sig=3e5ebecb324ba266bf80014dcc8380db'
+      document.getElementsByTagName('head')[0].appendChild(JSONP)
+  
+      setTimeout(() => {
+        document.getElementsByTagName('head')[0].removeChild(JSONP)
+      },500)
+    }
+
     const timeStamp = new Date().getTime()
     const _authUrl = window.location.href.split('#')[0] + 'AuthCode'
 
@@ -252,13 +348,15 @@
     authCode = authCode ? authCode.split(',') : []
     let index = authCode.findIndex(key => key === _s)
 
-    if (index > -1) {
+    let key = md5(window.GLOB.appId + 'minke_software' + window.GLOB.appkey).toUpperCase().substr(-6)
+
+    if (index > -1 || window.GLOB.licenseKey === key) {
       this.setState({
         auth: true
       })
     }
 
-    if (index === -1 || index > 5) {
+    if (window.GLOB.licenseKey !== key && (index === -1 || index > 5)) {
       let _appId = window.GLOB.appId
   
       if (options.sysType === 'cloud') { // 浜戠浣跨敤绯荤粺閰嶇疆appid
@@ -280,6 +378,15 @@
         nonc: Utils.getuuid()
       }
 
+      // param = {
+      //   func: _func,
+      //   VerificationCode: '鎺堟潈鐮�',
+      //   TimeStamp: timeStamp,
+      //   userid: _id,
+      //   LoginUID: _id,
+      //   nonc: Utils.getuuid()
+      // }
+
       let keys = Object.keys(param).sort()
       let values = ''
       keys.forEach(key => {
@@ -288,7 +395,7 @@
       param.sign = md5(values)
       param.t = new Date().getTime()
   
-      Api.directRequest(_rduri + '/sEmpowerCloud_Get_LinkUrl', 'post', param, 'true').then(res => {
+      Api.directRequest(_rduri + '/' + _func, 'post', param, 'true').then(res => {
         if (res.status) {
           if (res.EPC === str) {
             let box = []
@@ -308,10 +415,17 @@
               auth: false
             })
           }
+
+          if (res.query_address !== 'true') {
+            localStorage.setItem(_addressUrl, 'false')
+          } else {
+            localStorage.setItem(_addressUrl, 'true')
+          }
         } else if (res.ErrCode === 'N') {
           localStorage.removeItem(_authUrl)
           this.setState({
-            auth: false
+            auth: false,
+            authError: res.message
           })
         }
       })
@@ -478,14 +592,12 @@
       try {
         loginWays = JSON.parse(window.decodeURIComponent(window.atob(loginWays)))
       } catch {
-        localStorage.removeItem(window.location.href.split('#')[0] + 'loginways')
+        loginWays = null
       }
 
-      if (loginWays) {
-        this.setState({
-          loginWays: loginWays
-        })
-      }
+      this.setState({
+        loginWays: loginWays
+      })
     }
   }
 
@@ -576,6 +688,7 @@
             platName={this.state.platName}
             dict={this.state.dict}
             auth={this.state.auth}
+            authError={this.state.authError}
             touristLogin={touristLogin}
             loginWays={loginWays}
             lang={this.state.selectedlang}
@@ -583,6 +696,7 @@
             isDisabled={this.state.isDisabled}
             changelang={(value) => this.changelang(value)}
             handleSubmit={() => this.handleSubmit()}
+            authLogin={this.authLogin}
             wrappedComponentRef={(inst) => this.loginformRef = inst}
           /> : null}
         </div>
@@ -596,8 +710,8 @@
         {/* 缂栬緫鐘舵�佺櫥褰� */}
         <Modal
           title={this.state.dict['login.sync.cloud']}
-          okText={this.state.dict['login.auth.ok']}
-          cancelText={this.state.dict['login.auth.cancel']}
+          okText={this.state.dict['login.ok']}
+          cancelText={this.state.dict['login.cancel']}
           visible={this.state.syncApp}
           onOk={this.syncSubmit}
           maskClosable={false}
diff --git a/src/views/login/index.scss b/src/views/login/index.scss
index 236e2ad..431ea30 100644
--- a/src/views/login/index.scss
+++ b/src/views/login/index.scss
@@ -226,6 +226,47 @@
   .ant-btn-primary[disabled] {
     color: #fff;
   }
+
+  .form-scan-wrap {
+    text-align: center;
+    line-height: 45px;
+    .qr-wrap {
+      position: relative;
+      width: 60%;
+      padding-top: 60%;
+      margin: 0 auto;
+  
+      .qrcode-box {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        canvas {
+          width: 100%!important;
+          height: 100%!important;
+        }
+      }
+      .qrcode-out {
+        position: absolute;
+        top: 0;
+        left: 0;
+        right: 0;
+        bottom: 0;
+        background: rgba(0, 0, 0, 0.7);
+        padding-top: 30%;
+        color: rgba(255, 255, 255, 0.7);
+        line-height: 1.5;
+        text-align: center;
+        .anticon-redo {
+          display: block;
+          font-size: 24px;
+          cursor: pointer;
+          color: #ffffff;
+        }
+      }
+    }
+  }
 }
 .sync-cloud-application {
   .ant-modal-body {
diff --git a/src/views/login/loginform.jsx b/src/views/login/loginform.jsx
index 8518aae..7f207ed 100644
--- a/src/views/login/loginform.jsx
+++ b/src/views/login/loginform.jsx
@@ -1,24 +1,28 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
 import { Form, Icon, Input, Button, Modal, message } from 'antd'
 import md5 from 'md5'
 import moment from 'moment'
 
 import Api from '@/api'
+import Utils from '@/utils/utils.js'
 import options from '@/store/options.js'
+import asyncLoadComponent from '@/utils/asyncLoadComponent'
 import './index.scss'
 
 const { warning } = Modal
 let LoginVerCodeTimer = null
+const QrCode = asyncLoadComponent(() => import('@/components/qrcode'))
 
 class LoginTabForm extends Component {
   static propTpyes = {
     isDisabled: PropTypes.bool,
     changelang: PropTypes.func,
     handleSubmit: PropTypes.func,
+    authLogin: PropTypes.func,
     dict: PropTypes.object,
     auth: PropTypes.bool,
+    authError: PropTypes.string,
     touristLogin: PropTypes.bool,
     lang: PropTypes.string,
     langList: PropTypes.array,
@@ -28,14 +32,19 @@
 
   state = {
     activeKey: 'uname_pwd',
+    scanId: '',
     username: '',
     password: '',
     remember: true,
     delay: null,
     loginWays: [],
     smsId: '',
-    verdisabled: false
+    verdisabled: false,
+    timeout: false
   }
+
+  timer = null
+  splitTime = 0
 
   UNSAFE_componentWillMount () {
     const { loginWays } = this.props
@@ -54,36 +63,64 @@
         _loginWays.push(item)
       } else if (item.type === 'uname_pwd') {
         _loginWays.push(item)
+      } else if (item.type === 'app_scan') {
+        _loginWays.push(item)
       }
     })
+
+    let activeKey = _loginWays[0].type
 
     this.setState({
       smsId: smsId,
       loginWays: _loginWays,
-      activeKey: _loginWays[0].type,
+      activeKey,
+      scanId: activeKey === 'app_scan' ? Utils.getuuid() : '',
+      timeout: false,
       remember
     })
+
+    if (activeKey === 'app_scan') {
+      this.splitTime = 0
+      this.timer = setTimeout(() => {
+        this.checkResult()
+      }, 10000)
+    }
   }
 
-  UNSAFE_componentWillReceiveProps (nextProps) {
-    if (!is(fromJS(this.props.loginWays), fromJS(nextProps.loginWays))) {
-      let smsId = ''
-      let _loginWays = []
-      nextProps.loginWays.forEach(item => {
-        if (item.type === 'sms_vcode') {
-          smsId = item.smsId
-          _loginWays.push(item)
-        } else if (item.type === 'uname_pwd') {
-          _loginWays.push(item)
-        }
-      })
+  checkResult = () => {
+    const { scanId } = this.state
 
-      this.setState({
-        smsId: smsId,
-        loginWays: _loginWays,
-        activeKey: _loginWays[0].type
-      })
+    this.splitTime += 10000
+
+    let _param = {
+      func: 'webapi_get_binding_key',
+      scan_type: 'pc',
+      id: scanId,
+      UserName: ''
     }
+
+    _param.userid = sessionStorage.getItem('visitorUserID')
+    _param.LoginUID = sessionStorage.getItem('visitorLoginUID')
+
+    if (this.splitTime >= 180000) {
+      this.setState({
+        timeout: true
+      })
+      return
+    }
+
+    Api.getSystemConfig(_param).then(res => {
+      if (!res.status) {
+        message.warning(res.message)
+        return
+      } else if (res.thd_party_appid && res.thd_party_member_id && res.thd_party_openid) {
+        this.props.authLogin(res.thd_party_appid, res.thd_party_openid, res.thd_party_member_id, scanId)
+      } else {
+        this.timer = setTimeout(() => {
+          this.checkResult()
+        }, 10000)
+      }
+    })
   }
 
   handleConfirm = () => {
@@ -110,9 +147,9 @@
     e.preventDefault()
     if (!this.props.auth) {
       warning({
-        title: this.props.dict['login.auth.tip'],
-        okText: this.props.dict['login.auth.ok'],
-        cancelText: this.props.dict['login.auth.cancel'],
+        title: this.props.authError || this.props.dict['login.auth.tip'],
+        okText: this.props.dict['login.ok'],
+        cancelText: this.props.dict['login.cancel'],
         onOk() {},
         onCancel() {}
       })
@@ -182,7 +219,29 @@
   }
 
   onChangeTab = (activeKey) => {
-    this.setState({activeKey})
+    this.setState({activeKey, scanId: activeKey === 'app_scan' ? Utils.getuuid() : ''})
+
+    if (this.state.activeKey === 'app_scan') {
+      this.timer && clearTimeout(this.timer)
+    }
+
+    if (activeKey === 'app_scan') {
+      this.splitTime = 0
+      this.setState({timeout: false})
+      this.timer = setTimeout(() => {
+        this.checkResult()
+      }, 10000)
+    }
+  }
+
+  reCode = () => {
+    this.splitTime = 0
+
+    this.setState({timeout: false, scanId: Utils.getuuid()})
+
+    this.timer = setTimeout(() => {
+      this.checkResult()
+    }, 10000)
   }
 
   getvercode = () => {
@@ -296,7 +355,7 @@
 
   render() {
     const { getFieldDecorator } = this.props.form
-    const { activeKey, verdisabled, delay, loginWays } = this.state
+    const { activeKey, verdisabled, delay, loginWays, scanId, timeout } = this.state
 
     return (
       <Form className={`login-form login-form-${loginWays.length}`} id="login-form" onSubmit={this.handleSubmit}>
@@ -357,17 +416,21 @@
               />
             )}
           </Form.Item> : null}
-          {/* {activeKey === 'uname_pwd' ? <Form.Item className="minline">
-            {getFieldDecorator('remember', {
-              valuePropName: 'checked',
-              initialValue: remember,
-            })(<Checkbox onChange={this.rememberChange}>{this.props.dict['login.remember']}</Checkbox>)}
-          </Form.Item> : null} */}
           {['uname_pwd', 'sms_vcode'].includes(activeKey) ? <Form.Item className="btn-login">
             <Button type="primary" htmlType="submit" className="login-form-button" disabled={this.props.isDisabled} loading={this.props.isDisabled}>
               {this.props.dict['login.submit']}
             </Button>
           </Form.Item> : null}
+          {activeKey === 'app_scan' ? <div className="form-scan-wrap">
+            <div className="qr-wrap">
+              {scanId ? <QrCode card={{qrWidth: 500, color: '#000000'}} value={`mkpcscan,${window.GLOB.appkey},${scanId}`}/> : null}
+              {timeout ? <div className="qrcode-out">
+                <Icon onClick={this.reCode} type="redo" />
+                浜岀淮鐮佸凡澶辨晥銆�
+              </div> : null}
+            </div>
+            璇蜂娇鐢ㄥ鎴风鎵竴鎵櫥褰�
+          </div> : null}
           {options.sysType === 'cloud' && options.cdomain.indexOf('mk9h') > -1 ? <Form.Item className="register-line">
             <a href="http://minkesoft.com/#/signup" target="_blank" rel="noopener noreferrer" className="register">娉ㄥ唽</a>
             <a href="http://minkesoft.com/#/forgotPwd" target="_blank" rel="noopener noreferrer" className="forgot">蹇樿瀵嗙爜锛�</a>
diff --git a/src/views/main/index.jsx b/src/views/main/index.jsx
index 0abf7b6..926696f 100644
--- a/src/views/main/index.jsx
+++ b/src/views/main/index.jsx
@@ -7,6 +7,7 @@
 import Header from '@/components/header'
 import Sidemenu from '@/components/sidemenu'
 import QueryLog from '@/components/querylog'
+import ImgScale from '@/components/imgScale'
 
 import './index.scss'
 
@@ -21,17 +22,18 @@
 
   render () {
     const { navBar } = this.state
-    const isSideMenu = !['linkage_navigation', 'linkage'].includes(navBar)
+    const isSideMenu = !['linkage_navigation', 'linkage', 'menu_board'].includes(navBar)
 
     return (
       <div className="mk-main-view">
         <ConfigProvider locale={_locale}>
           <Header key="header"/>
-          {isSideMenu ? <Sidemenu key="sidemenu"/> : null}
+          {isSideMenu && navBar !== 'menu_board_navigation' ? <Sidemenu key="sidemenu"/> : null}
           {isSideMenu ? <Tabview key="tabview"/> : null}
           {!isSideMenu ? <Breadview key="breadview"/> : null}
           <QueryLog />
         </ConfigProvider>
+        <ImgScale />
       </div>
     )
   }
diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index 85e7fe4..937c911 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -48,6 +48,7 @@
 sessionStorage.setItem('appType', '')          // 搴旂敤绫诲瀷
 document.body.className = ''
 window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.TabsMap = new Map()          // 缂撳瓨鐢ㄦ埛鎿嶄綔鐨勬爣绛鹃〉
 window.GLOB.urlFields = []               // url鍙橀噺
 window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
 
@@ -103,6 +104,7 @@
     MKEmitter.addListener('thawButtons', this.thawButtons)
     MKEmitter.addListener('copyButtons', this.copyButtons)
     MKEmitter.addListener('changePopview', this.initPopview)
+    MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     setTimeout(() => {
@@ -123,8 +125,15 @@
     MKEmitter.removeListener('thawButtons', this.thawButtons)
     MKEmitter.removeListener('copyButtons', this.copyButtons)
     MKEmitter.removeListener('changePopview', this.initPopview)
+    MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
+  }
+
+  triggerMenuSave = () => {
+    if (this.state.visible) return
+
+    this.submitConfig()
   }
 
   getAppPictures = () => {
@@ -214,12 +223,7 @@
   }
 
   delButtons = (items) => {
-    const { copyButtons } = this.state
-
-    this.setState({
-      delButtons: [...this.state.delButtons, ...items],
-      copyButtons: copyButtons.filter(item => !items.includes(item.uuid))
-    })
+    this.setState({ delButtons: [...this.state.delButtons, ...items] })
   }
 
   copyButtons = (items) => {
@@ -521,7 +525,7 @@
         EasyCode: config.easyCode || '',
         Template: 'CustomPage',
         MenuName: config.MenuName || '',
-        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: config.OpenType || 'newtab'}),
+        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: config.OpenType || 'newtab', hidden: config.hidden || 'false'}),
         LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
         open_edition: config.open_edition,
         LText: '',
@@ -776,12 +780,7 @@
             delButtons: [],
             copyButtons: [],
             thawButtons: [],
-            menuloading: false,
-            comloading: true
-          }, () => {
-            this.setState({
-              comloading: false
-            })
+            menuloading: false
           })
           notification.success({
             top: 92,
@@ -793,6 +792,7 @@
             menuloading: false
           })
         }
+        MKEmitter.emit('completeSave')
       })
     }, 300)
   }
@@ -856,7 +856,7 @@
           check(item.components)
           return
         }
-        if (['propcard', 'brafteditor', 'sandbox', 'stepform'].includes(item.subtype) && item.wrap.datatype === 'static') return
+        if (['propcard', 'brafteditor', 'sandbox', 'stepform', 'tabform'].includes(item.subtype) && item.wrap.datatype === 'static') return
         if (['balcony'].includes(item.type) && item.wrap.datatype === 'static') return
   
         if (item.setting) {
diff --git a/src/views/menudesign/menuform/index.jsx b/src/views/menudesign/menuform/index.jsx
index 92d185d..c2f987e 100644
--- a/src/views/menudesign/menuform/index.jsx
+++ b/src/views/menudesign/menuform/index.jsx
@@ -1,6 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Select, notification, Radio, Icon, Tooltip, InputNumber } from 'antd'
+import { Form, Row, Col, Input, Select, notification, Radio, Icon, Tooltip, InputNumber, Switch } from 'antd'
 
 import Api from '@/api'
 import options from '@/store/options.js'
@@ -135,6 +135,8 @@
       this.props.updateConfig({...config, timeUnit: value})
     } else if (key === 'OpenType') {
       this.props.updateConfig({...config, OpenType: value})
+    } else if (key === 'hidden') {
+      this.props.updateConfig({...config, hidden: value})
     }
   }
 
@@ -257,10 +259,10 @@
                   }
                 ]
               })(
-                <Select onChange={(value) => {this.selectChange('OpenType', value)}}>
-                  <Select.Option value="newtab">鏍囩椤�</Select.Option>
-                  <Select.Option value="newpage">鏂伴〉闈�</Select.Option>
-                </Select>
+                <Radio.Group onChange={(e) => {this.selectChange('OpenType', e.target.value)}}>
+                  <Radio value="newtab">鏍囩椤�</Radio>
+                  <Radio value="newpage">鏂伴〉闈�</Radio>
+                </Radio.Group>
               )}
             </Form.Item>
           </Col>
@@ -315,6 +317,13 @@
               })(<Input placeholder="" autoComplete="off" onChange={this.changeEasyCode}/>)}
             </Form.Item>
           </Col>
+          <Col span={24}>
+            <Form.Item label={'闅愯棌鑿滃崟'}>
+              <Switch checkedChildren={'鏄�'} checked={config.hidden === 'true'} unCheckedChildren={'鍚�'} onChange={(value) => {
+                this.selectChange('hidden', value + '')
+              }} />
+            </Form.Item>
+          </Col>
         </Row>
       </Form>
     )
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index e983907..762e998 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -47,6 +47,7 @@
 sessionStorage.setItem('appType', 'mob')       // 搴旂敤绫诲瀷
 document.body.className = ''
 window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.TabsMap = new Map()          // 缂撳瓨鐢ㄦ埛鎿嶄綔鐨勬爣绛鹃〉
 window.GLOB.CacheIndependent = new Map()
 window.GLOB.urlFields = []               // url鍙橀噺
 window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
@@ -132,6 +133,7 @@
       return
     }
     MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.addListener('triggerMenuSave', this.submitConfig)
     MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     setTimeout(() => {
@@ -150,6 +152,7 @@
       return
     }
     MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.removeListener('triggerMenuSave', this.submitConfig)
     MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
   }
@@ -500,6 +503,11 @@
       appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
 
       let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
+      menus = menus.map(item => {
+        item.value = item.MenuID
+        item.label = item.MenuName
+        return item
+      })
       sessionStorage.setItem('appMenus', JSON.stringify(menus))
     })
   }
@@ -672,7 +680,10 @@
           title: item.name,
           children: []
         }
-        if (item.type === 'tabs') {
+
+        if (item.type === 'topbar' || item.type === 'login') {
+          return null
+        } else if (item.type === 'tabs') {
           let tabs = []
           item.subtabs.forEach(tab => {
             let s = traversal(tab.components)
@@ -748,6 +759,13 @@
               title: menu.setting.name
             }
           })
+        } else if (item.type === 'form') {
+          m.children = item.subcards.map(m => {
+            return {
+              key: m.uuid,
+              title: m.setting.title
+            }
+          })
         } else if (item.type === 'table' && item.subtype === 'normaltable') {
           item.action && item.action.forEach(btn => {
             this.checkBtn(btn)
@@ -767,8 +785,6 @@
             })
           })
         }
-
-        if (m.children.length === 0) return null
 
         return m
       })
@@ -1032,12 +1048,7 @@
           this.setState({
             config,
             oriConfig: fromJS(config).toJS(),
-            menuloading: false,
-            comloading: true
-          }, () => {
-            this.setState({
-              comloading: false
-            })
+            menuloading: false
           })
 
           notification.success({
@@ -1055,6 +1066,7 @@
             menuloading: false
           })
         }
+        MKEmitter.emit('completeSave')
       })
     }, 300)
   }
@@ -1122,7 +1134,7 @@
           error = `瀵艰埅鏍忋��${item.name}銆嬫湭璁剧疆鑿滃崟鍙傛暟锛乣
         }
 
-        if (['propcard', 'brafteditor', 'sandbox', 'tabbar', 'stepform'].includes(item.subtype) && item.wrap.datatype === 'static') return
+        if (['propcard', 'brafteditor', 'sandbox', 'tabbar', 'stepform', 'tabform'].includes(item.subtype) && item.wrap.datatype === 'static') return
         if (['balcony'].includes(item.type) && item.wrap.datatype === 'static') return
 
         if (item.setting) {
@@ -1338,19 +1350,21 @@
                 {controlshow ? <Icon onClick={() => {this.setState({controlshow: false})}} type="double-right" /> : null}
                 {!controlshow ? <Icon onClick={() => {this.setState({controlshow: true})}} type="double-left" /> : null}
               </div>
-              <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
-              <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
-              <CreateView resetmenu={this.getAppMenus} />
-              <PasteController type="menu" Tab={null} insert={this.insert} />
-              <StyleCombControlButton menu={config} />
-              <SysInterface config={config} updateConfig={this.updateConfig}/>
-              <PictureController/>
-              <Quotecomponent config={config} updateConfig={this.updateConfig}/>
-              <Button className="mk-border-green" icon="home" onClick={this.setHomeView}>璁句负棣栭〉</Button>
-              <Button className="mk-border-danger" icon="redo" onClick={this.refreshView}>寮哄埗鍒锋柊</Button>
-              <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
-              <Transfer MenuID={MenuId} />
-              <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
+              <div className="wrap">
+                <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
+                <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
+                <CreateView resetmenu={this.getAppMenus} />
+                <PasteController type="menu" Tab={null} insert={this.insert} />
+                <StyleCombControlButton menu={config} />
+                <SysInterface config={config} updateConfig={this.updateConfig}/>
+                <PictureController/>
+                <Quotecomponent config={config} updateConfig={this.updateConfig}/>
+                <Button className="mk-border-green" icon="home" onClick={this.setHomeView}>璁句负棣栭〉</Button>
+                <Button className="mk-border-danger" icon="redo" onClick={this.refreshView}>寮哄埗鍒锋柊</Button>
+                <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
+                <Transfer MenuID={MenuId} />
+                <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
+              </div>
             </div>
             <div className={'menu-body menu-view' + (menuloading ? 'saving' : '')}>
               {config && !comloading ? <div className="mob-shell" style={{width: window.GLOB.shellWidth, height: window.GLOB.shellHeight}}>
diff --git a/src/views/mobdesign/index.scss b/src/views/mobdesign/index.scss
index 82ae4e6..ca8fe66 100644
--- a/src/views/mobdesign/index.scss
+++ b/src/views/mobdesign/index.scss
@@ -37,6 +37,7 @@
       background: #ffffff;
       overflow-y: auto;
       overflow-x: hidden;
+      padding-bottom: 50px;
 
       > .ant-collapse {
         background-color: #ffffff;
@@ -149,8 +150,7 @@
     position: fixed;
     right: 0;
     top: 48px;
-    height: 100vh;
-    padding: 20px 10px;
+    height: calc(100vh - 48px);
     background: #ffffff;
     z-index: 10;
     transition: right 0.3s;
@@ -187,7 +187,27 @@
       width: 22px;
       height: 22px;
     }
+    .wrap {
+      height: 100%;
+      padding: 20px 10px;
+      overflow-y: auto;
+    }
+    .wrap::-webkit-scrollbar {
+      width: 2px;
+    }
+    .wrap::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
+      background: rgba(0, 0, 0, 0.08);
+    }
+    .wrap::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
   }
+  
   .menu-control.hidden {
     right: -130px;
   }
diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx
index bde0d56..b54bdbc 100644
--- a/src/views/pcdesign/index.jsx
+++ b/src/views/pcdesign/index.jsx
@@ -47,6 +47,7 @@
 sessionStorage.setItem('typename', 'pc')
 document.body.className = ''
 window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.TabsMap = new Map()          // 缂撳瓨鐢ㄦ埛鎿嶄綔鐨勬爣绛鹃〉
 window.GLOB.CacheIndependent = new Map()
 window.GLOB.urlFields = []               // url鍙橀噺
 window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
@@ -127,6 +128,7 @@
     MKEmitter.addListener('copyButtons', this.copyButtons)
     MKEmitter.addListener('changePopview', this.initPopview)
     MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.addListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
     setTimeout(() => {
@@ -149,8 +151,15 @@
     MKEmitter.removeListener('copyButtons', this.copyButtons)
     MKEmitter.removeListener('changePopview', this.initPopview)
     MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.removeListener('triggerMenuSave', this.triggerMenuSave)
     MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
     MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
+  }
+
+  triggerMenuSave = () => {
+    if (this.state.visible) return
+
+    this.submitConfig()
   }
 
   changeEditMenu = (menu) => {
@@ -402,12 +411,7 @@
   }
 
   delButtons = (items) => {
-    const { copyButtons, delButtons } = this.state
-
-    this.setState({
-      delButtons: [...delButtons, ...items],
-      copyButtons: copyButtons.filter(item => !items.includes(item.uuid))
-    })
+    this.setState({ delButtons: [...this.state.delButtons, ...items] })
   }
 
   copyButtons = (items) => {
@@ -565,6 +569,11 @@
       appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
 
       let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
+      menus = menus.map(item => {
+        item.value = item.MenuID
+        item.label = item.MenuName
+        return item
+      })
       sessionStorage.setItem('appMenus', JSON.stringify(menus))
     })
   }
@@ -738,7 +747,9 @@
           title: item.name,
           children: []
         }
-        if (item.type === 'tabs') {
+        if (item.type === 'login') {
+          return null
+        } else if (item.type === 'tabs') {
           let tabs = []
           item.subtabs.forEach(tab => {
             let s = traversal(tab.components)
@@ -829,6 +840,13 @@
               title: menu.setting.name
             }
           })
+        } else if (item.type === 'form') {
+          m.children = item.subcards.map(m => {
+            return {
+              key: m.uuid,
+              title: m.setting.title
+            }
+          })
         } else if (item.type === 'table' && item.subtype === 'normaltable') {
           item.action && item.action.forEach(btn => {
             this.checkBtn(btn)
@@ -854,8 +872,6 @@
             })
           })
         }
-
-        if (m.children.length === 0) return null
 
         return m
       })
@@ -1294,12 +1310,7 @@
             delButtons: [],
             copyButtons: [],
             thawButtons: [],
-            menuloading: false,
-            comloading: true
-          }, () => {
-            this.setState({
-              comloading: false
-            })
+            menuloading: false
           })
           notification.success({
             top: 92,
@@ -1311,6 +1322,7 @@
             menuloading: false
           })
         }
+        MKEmitter.emit('completeSave')
       })
     }, 300)
   }
@@ -1375,7 +1387,7 @@
           check(item.components)
           return
         }
-        if (['propcard', 'brafteditor', 'sandbox', 'stepform'].includes(item.subtype) && item.wrap.datatype === 'static') return
+        if (['propcard', 'brafteditor', 'sandbox', 'stepform', 'tabform'].includes(item.subtype) && item.wrap.datatype === 'static') return
         if (['balcony'].includes(item.type) && item.wrap.datatype === 'static') return
         
         if (item.setting) {
@@ -1570,19 +1582,21 @@
                 {controlshow ? <Icon onClick={() => {sessionStorage.setItem('controlshow', 'false'); this.setState({controlshow: false})}} type="double-right" /> : null}
                 {!controlshow ? <Icon onClick={() => {sessionStorage.setItem('controlshow', 'true'); this.setState({controlshow: true})}} type="double-left" /> : null}
               </div>
-              <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
-              <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
-              <CreateView resetmenu={this.getAppMenus} />
-              <PasteController type="menu" Tab={null} insert={this.insert} />
-              <StyleCombControlButton menu={config} />
-              <SysInterface config={config} updateConfig={this.updateConfig}/>
-              <PictureController/>
-              <Quotecomponent config={config} updateConfig={this.updateConfig}/>
-              <Button className="mk-border-green" icon="home" onClick={this.setHomeView}>璁句负棣栭〉</Button>
-              <Button className="mk-border-danger" icon="redo" onClick={this.refreshView}>寮哄埗鍒锋柊</Button>
-              <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
-              <Transfer MenuID={MenuId} />
-              <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
+              <div className="wrap">
+                <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
+                <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config && config.enabled} onChange={this.onEnabledChange} />
+                <CreateView resetmenu={this.getAppMenus} />
+                <PasteController type="menu" Tab={null} insert={this.insert} />
+                <StyleCombControlButton menu={config} />
+                <SysInterface config={config} updateConfig={this.updateConfig}/>
+                <PictureController/>
+                <Quotecomponent config={config} updateConfig={this.updateConfig}/>
+                <Button className="mk-border-green" icon="home" onClick={this.setHomeView}>璁句负棣栭〉</Button>
+                <Button className="mk-border-danger" icon="redo" onClick={this.refreshView}>寮哄埗鍒锋柊</Button>
+                <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
+                <Transfer MenuID={MenuId} />
+                <Button type="default" onClick={this.closeView}>鍏抽棴</Button>
+              </div>
             </div>
             <div className={'menu-body menu-view' + (menuloading ? 'saving' : '')}>
               {config && !comloading ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
diff --git a/src/views/pcdesign/index.scss b/src/views/pcdesign/index.scss
index 3cf126b..8b4d1bd 100644
--- a/src/views/pcdesign/index.scss
+++ b/src/views/pcdesign/index.scss
@@ -39,6 +39,7 @@
       box-shadow: 0px 2px 5px #bcbcbc;
       overflow-y: auto;
       overflow-x: hidden;
+      padding-bottom: 50px;
 
       > .ant-collapse {
         background-color: #ffffff;
@@ -127,7 +128,6 @@
     right: 0;
     top: 0px;
     height: 100vh;
-    padding: 20px 10px;
     background: #ffffff;
     z-index: 10;
     transition: right 0.3s;
@@ -164,6 +164,25 @@
       width: 22px;
       height: 22px;
     }
+    .wrap {
+      height: 100%;
+      padding: 20px 10px;
+      overflow-y: auto;
+    }
+    .wrap::-webkit-scrollbar {
+      width: 2px;
+    }
+    .wrap::-webkit-scrollbar-thumb {
+      border-radius: 5px;
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
+      background: rgba(0, 0, 0, 0.08);
+    }
+    .wrap::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border-radius: 3px;
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+    }
   }
   .menu-control.hidden {
     right: -130px;

--
Gitblit v1.8.0