From f3167f8371d19d0ea8fe7d0e7af5517ff0b08cd2 Mon Sep 17 00:00:00 2001
From: king <18310653075@163.com>
Date: 星期三, 07 四月 2021 23:25:29 +0800
Subject: [PATCH] 2021-04-07

---
 src/templates/zshare/modalform/index.jsx                                           |  158 
 src/menu/components/form/formaction/index.scss                                     |   21 
 src/menu/components/table/normal-table/wrapsetting/index.jsx                       |    2 
 src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx                     |   42 
 src/tabviews/formtab/index.jsx                                                     |    7 
 src/tabviews/verupmanage/actionList/index.jsx                                      |   23 
 src/tabviews/zshare/mutilform/index.scss                                           |   22 
 src/mob/mobshell/index.jsx                                                         |  136 
 src/tabviews/scriptmanage/config.jsx                                               |   18 
 src/templates/zshare/verifycard/customscript/index.jsx                             |   21 
 src/menu/components/carousel/data-card/wrapsetting/index.jsx                       |   83 
 src/templates/sharecomponent/chartcomponent/index.jsx                              |    2 
 src/locales/zh-CN/model.js                                                         |    3 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx  |   21 
 src/menu/components/tabs/tabsetting/index.jsx                                      |    2 
 public/README.txt                                                                  |    4 
 src/menu/components/form/normal-form/index.scss                                    |   78 
 src/menu/datasource/verifycard/index.jsx                                           |    5 
 src/menu/components/form/normal-form/groupform/index.scss                          |    5 
 src/views/menudesign/menuform/index.scss                                           |    6 
 src/components/header/index.scss                                                   |   59 
 src/menu/components/card/cardcellcomponent/index.jsx                               |   78 
 src/menu/components/editor/braft-editor/wrapsetting/index.jsx                      |    2 
 src/menu/components/form/dragtitle/index.scss                                      |   39 
 src/store/reducer.js                                                               |   31 
 src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx     |   18 
 src/pc/menushell/card.jsx                                                          |   99 
 src/menu/components/carousel/prop-card/index.jsx                                   |  358 
 src/menu/components/card/cardcomponent/index.jsx                                   |    2 
 src/menu/components/code/sandbox/wrapsetting/index.jsx                             |    2 
 src/templates/modalconfig/dragelement/index.scss                                   |  108 
 src/menu/components/chart/antv-pie/index.jsx                                       |   19 
 src/menu/components/editor/braft-editor/editorcontent/index.jsx                    |    2 
 src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx           |  184 
 src/pc/components/navbar/normal-navbar/index.scss                                  |  159 
 src/pc/modulesource/dragsource/index.scss                                          |   46 
 src/pc/components/navbar/normal-navbar/menusetting/index.scss                      |   11 
 src/menu/components/card/prop-card/index.jsx                                       |   24 
 src/templates/zshare/customscript/index.jsx                                        |    2 
 src/menu/components/tabs/tabcomponents/card.jsx                                    |    6 
 src/tabviews/zshare/fileupload/index.scss                                          |   10 
 src/templates/sharecomponent/treesettingcomponent/index.jsx                        |    2 
 src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx                       |   83 
 src/menu/datasource/verifycard/utils.jsx                                           |   25 
 src/tabviews/custom/components/chart/antv-bar-line/index.jsx                       |   80 
 package-lock.json                                                                  |   13 
 src/assets/css/viewstyle.scss                                                      |  128 
 src/views/login/loginform.jsx                                                      |    3 
 src/menu/components/card/cardcomponent/settingform/index.jsx                       |  119 
 src/views/pcdesign/menuform/index.scss                                             |   16 
 src/tabviews/verupmanage/subtabtable/index.jsx                                     |    1 
 src/tabviews/zshare/topSearch/index.jsx                                            |    7 
 src/menu/urlfieldcomponent/settingform/index.scss                                  |    0 
 src/templates/comtableconfig/index.jsx                                             |   70 
 src/menu/components/carousel/cardcomponent/index.scss                              |    0 
 src/menu/components/form/wrapsetting/index.scss                                    |    7 
 src/api/index.js                                                                   |  636 
 src/views/mobdesign/menuform/index.scss                                            |   16 
 src/templates/sharecomponent/actioncomponent/index.scss                            |    2 
 src/templates/sharecomponent/cardcomponent/index.jsx                               |    2 
 src/mob/mobshell/card.jsx                                                          |   74 
 src/views/appmanage/mutilform/index.jsx                                            |   89 
 src/tabviews/custom/components/card/prop-card/index.scss                           |    3 
 public/index.html                                                                  |    8 
 src/menu/sysinterface/settingform/baseform/index.jsx                               |   16 
 src/menu/urlfieldcomponent/index.scss                                              |   28 
 src/pc/bgcontroller/index.jsx                                                      |  172 
 src/tabviews/custom/components/carousel/cardItem/index.jsx                         |   53 
 src/views/appmanage/mutilform/index.scss                                           |    0 
 src/views/pcdesign/index.jsx                                                       | 1485 +++
 src/tabviews/zshare/dategroup/quarterpicker/index.scss                             |    4 
 src/menu/datasource/verifycard/settingform/index.jsx                               |    8 
 src/menu/components/share/actioncomponent/index.jsx                                |   60 
 src/templates/menuconfig/menuelement/card.jsx                                      |    2 
 src/setupProxy.js                                                                  |  103 
 src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx           |  214 
 src/menu/components/search/main-search/wrapsetting/index.jsx                       |    2 
 src/menu/picturecontroller/editform/index.jsx                                      |   18 
 src/templates/modalconfig/source.jsx                                               |   27 
 src/tabviews/custom/components/carousel/cardItem/index.scss                        |   58 
 src/tabviews/zshare/mutilform/customTextArea/index.jsx                             |   24 
 src/menu/components/form/dragtitle/card.jsx                                        |   59 
 src/pc/modulesource/option.jsx                                                     |   44 
 src/templates/zshare/editTable/index.jsx                                           |    2 
 src/tabviews/zshare/normalTable/index.jsx                                          |   77 
 src/menu/components/form/formaction/actionform/index.scss                          |   47 
 src/menu/components/share/searchcomponent/index.jsx                                |   15 
 src/menu/components/form/normal-form/groupform/index.jsx                           |  144 
 src/pc/components/navbar/normal-navbar/menusetting/index.jsx                       |   62 
 src/templates/treepageconfig/index.jsx                                             |    2 
 src/views/menudesign/menuform/index.jsx                                            |   30 
 src/menu/datasource/index.jsx                                                      |   28 
 src/mob/components/login/mob-login-1/index.jsx                                     |    2 
 src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx                     |   14 
 src/templates/menuconfig/editsecmenu/index.scss                                    |    5 
 src/templates/treepageconfig/source.jsx                                            |    2 
 src/pc/bgcontroller/index.scss                                                     |   19 
 src/menu/components/form/formaction/index.jsx                                      |  253 
 src/mob/components/login/mob-login-2/index.jsx                                     |    2 
 src/pc/components/navbar/normal-navbar/linksetting/index.scss                      |   11 
 src/views/billprint/index.jsx                                                      |    8 
 src/tabviews/subtabtable/index.jsx                                                 |    4 
 src/menu/components/carousel/cardcomponent/index.jsx                               |  229 
 src/tabviews/zshare/actionList/normalbutton/index.jsx                              |  160 
 src/tabviews/custom/components/table/normal-table/index.scss                       |    2 
 src/views/menudesign/index.jsx                                                     |  124 
 src/menu/modulesource/option.jsx                                                   |    6 
 src/tabviews/custom/components/card/data-card/index.jsx                            |  100 
 src/views/appmanage/index.scss                                                     |   41 
 src/assets/mobimg/navbar.png                                                       |    0 
 src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx         |    2 
 src/templates/sharecomponent/actioncomponent/actionform/index.jsx                  |   39 
 src/templates/calendarconfig/index.jsx                                             |   24 
 src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx              |  164 
 src/tabviews/verupmanage/config.jsx                                                |    6 
 src/tabviews/zshare/mutilform/checkCard/index.jsx                                  |    7 
 src/views/login/logincloudform.jsx                                                 |    2 
 src/menu/stylecontroller/styleInput/index.jsx                                      |   13 
 src/router/index.js                                                                |   19 
 src/menu/components/card/cardcomponent/settingform/index.scss                      |    8 
 src/menu/components/card/data-card/index.jsx                                       |   22 
 src/templates/menuconfig/editthdmenu/index.scss                                    |    5 
 src/menu/components/share/clockcomponent/settingform/index.jsx                     |   72 
 src/mob/modulesource/index.scss                                                    |    2 
 src/mob/header/index.scss                                                          |   48 
 src/views/appmanage/index.jsx                                                      |  587 +
 src/menu/components/card/cardcellcomponent/elementform/index.scss                  |   15 
 src/menu/components/carousel/data-card/index.scss                                  |   87 
 src/tabviews/verupmanage/index.jsx                                                 |    1 
 src/menu/components/tabs/antv-tabs/index.jsx                                       |   16 
 src/components/tabview/index.scss                                                  |    9 
 src/menu/datasource/verifycard/settingform/index.scss                              |    3 
 src/templates/sharecomponent/settingcomponent/index.jsx                            |    9 
 src/views/design/index.jsx                                                         |   29 
 src/tabviews/zshare/dategroup/quarterpicker/index.jsx                              |    2 
 src/templates/subtableconfig/source.jsx                                            |    2 
 src/components/header/index.jsx                                                    |  437 
 src/components/sidemenu/index.scss                                                 |   12 
 src/views/pcdesign/menuform/index.jsx                                              |  138 
 src/mob/modulesource/dragsource/index.jsx                                          |   15 
 src/templates/formtabconfig/index.jsx                                              |   19 
 src/templates/zshare/verifycard/callbackcustomscript/index.jsx                     |   21 
 src/menu/padcontroller/index.jsx                                                   |    2 
 src/tabviews/custom/components/card/cardItem/index.jsx                             |    4 
 src/tabviews/zshare/chartcomponent/index.jsx                                       |    2 
 src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss          |   11 
 src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx             |  549 +
 src/tabviews/calendar/index.jsx                                                    |   41 
 src/pc/quotecomponent/index.jsx                                                    |  123 
 src/templates/zshare/editcomponent/index.jsx                                       |   26 
 src/menu/components/share/clockcomponent/settingform/index.scss                    |    0 
 src/tabviews/custom/components/carousel/data-card/index.jsx                        |  357 
 src/templates/sharecomponent/fieldscomponent/index.jsx                             |  139 
 src/menu/components/code/sandbox/index.jsx                                         |   17 
 src/menu/urlfieldcomponent/settingform/index.jsx                                   |   82 
 src/menu/bgcontroller/index.jsx                                                    |    2 
 src/templates/sharecomponent/settingcalcomponent/index.jsx                         |    5 
 src/menu/sysinterface/settingform/utils.jsx                                        |    4 
 src/templates/sharecomponent/settingcomponent/settingform/utils.jsx                |   23 
 src/templates/calendarconfig/calcomponent/index.jsx                                |    2 
 src/tabviews/zshare/actionList/tabbutton/index.jsx                                 |   31 
 src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss          |   11 
 src/templates/modalconfig/index.scss                                               |   98 
 src/utils/utils-update.js                                                          |   59 
 src/menu/stylecontroller/index.jsx                                                 |   24 
 src/templates/zshare/verifycard/index.jsx                                          |   64 
 src/tabviews/zshare/actionList/printbutton/index.jsx                               |  114 
 src/store/options.js                                                               |   76 
 src/menu/components/card/cardcellcomponent/formconfig.jsx                          |  105 
 src/menu/components/form/wrapsetting/index.jsx                                     |   83 
 src/templates/sharecomponent/actioncomponent/index.jsx                             |   59 
 src/menu/components/editor/braft-editor/index.jsx                                  |   17 
 package.json                                                                       |    2 
 src/tabviews/zshare/fileupload/index.jsx                                           |    8 
 src/mob/home/index.jsx                                                             |    2 
 src/templates/sharecomponent/columncomponent/columnform/index.scss                 |    8 
 src/mob/header/index.jsx                                                           |   92 
 src/menu/components/share/markcomponent/index.jsx                                  |    3 
 src/tabviews/custom/components/form/normal-form/index.scss                         |  108 
 src/menu/components/carousel/data-card/wrapsetting/index.scss                      |    7 
 src/menu/components/card/cardcellcomponent/elementform/index.jsx                   |  147 
 src/pc/components/navbar/normal-navbar/linksetting/index.jsx                       |   61 
 src/templates/modalconfig/index.jsx                                                |  406 
 public/admin.html                                                                  |   38 
 src/pc/menushell/index.scss                                                        |   23 
 src/pc/quotecomponent/settingform/index.scss                                       |   11 
 src/templates/sharecomponent/tablecomponent/index.jsx                              |   24 
 src/templates/menuconfig/editthdmenu/index.jsx                                     |    6 
 src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx              |    8 
 src/views/design/sidemenu/index.jsx                                                |  323 
 src/menu/stylecombcontroller/index.jsx                                             |    4 
 src/tabviews/commontable/index.jsx                                                 |   71 
 src/components/header/loginform.jsx                                                |    2 
 src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx               |    4 
 src/menu/components/form/formaction/formconfig.jsx                                 |  292 
 src/menu/components/carousel/cardcomponent/settingform/index.jsx                   |  194 
 src/tabviews/custom/components/card/cardcellList/index.jsx                         |   15 
 src/tabviews/custom/components/card/prop-card/index.jsx                            |  180 
 src/tabviews/custom/components/table/normal-table/index.jsx                        |   19 
 src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx |    2 
 src/store/action-type.js                                                           |    9 
 src/mob/modulesource/index.jsx                                                     |   93 
 src/tabviews/custom/components/chart/antv-pie/index.jsx                            |   80 
 src/templates/subtableconfig/index.jsx                                             |   46 
 src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx            |    4 
 src/components/breadview/index.jsx                                                 |  161 
 src/tabviews/zshare/mutilform/index.jsx                                            |  427 
 src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss             |    0 
 src/tabviews/commontable/index.scss                                                |    2 
 src/menu/components/share/actioncomponent/actionform/index.jsx                     |   55 
 src/tabviews/custom/index.jsx                                                      |  100 
 src/pc/quotecomponent/index.scss                                                   |    6 
 src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx            |    2 
 scripts/start.js                                                                   |    2 
 src/menu/components/table/normal-table/columns/index.jsx                           |    2 
 src/views/appmanage/submutilform/index.jsx                                         |  192 
 src/tabviews/zshare/cardcomponent/index.jsx                                        |    2 
 src/menu/popview/index.jsx                                                         |   71 
 src/tabviews/treepage/index.jsx                                                    |    2 
 src/menu/components/share/logcomponent/index.jsx                                   |    2 
 src/pc/components/navbar/normal-navbar/index.jsx                                   |  237 
 src/menu/components/card/cardcellcomponent/dragaction/index.scss                   |    2 
 src/views/design/header/index.jsx                                                  |  382 
 src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss            |   20 
 src/menu/components/form/formaction/actionform/index.jsx                           |  367 
 src/tabviews/custom/components/carousel/data-card/index.scss                       |   49 
 src/menu/components/card/cardcellcomponent/dragaction/index.jsx                    |    6 
 src/menu/components/form/normal-form/index.jsx                                     |  704 +
 src/pc/menushell/index.jsx                                                         |  167 
 src/assets/mobimg/form.png                                                         |    0 
 src/tabviews/zshare/actionList/exceloutbutton/index.jsx                            |   38 
 src/menu/components/card/cardcellcomponent/dragaction/card.jsx                     |    6 
 src/pc/modulesource/dragsource/index.jsx                                           |   15 
 src/menu/components/chart/antv-bar/index.jsx                                       |   24 
 src/views/design/sidemenu/index.scss                                               |  119 
 src/pc/modulesource/index.jsx                                                      |   93 
 src/store/action.js                                                                |   23 
 src/tabviews/zshare/actionList/index.jsx                                           |    2 
 src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss             |    0 
 src/tabviews/subtable/index.jsx                                                    |   46 
 src/views/appmanage/submutilform/index.scss                                        |    0 
 src/menu/components/table/normal-table/columns/editColumn/index.scss               |    8 
 src/pc/components/navbar/normal-navbar/wrapsetting/index.scss                      |    7 
 src/menu/components/card/data-card/wrapsetting/index.jsx                           |    2 
 src/templates/sharecomponent/tabscomponent/index.jsx                               |    2 
 src/menu/components/carousel/data-card/index.jsx                                   |  276 
 src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx                     |   42 
 src/tabviews/scriptmanage/actionList/index.jsx                                     |    4 
 public/manifest.json                                                               |   13 
 src/menu/components/share/actioncomponent/index.scss                               |    2 
 src/templates/modalconfig/settingform/index.jsx                                    |   43 
 src/pc/quotecomponent/settingform/index.jsx                                        |   88 
 src/templates/comtableconfig/source.jsx                                            |    2 
 src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx              |  207 
 src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx              |   33 
 src/menu/urlfieldcomponent/index.jsx                                               |  135 
 src/menu/components/search/main-search/index.jsx                                   |   14 
 src/menu/components/form/wrapsetting/settingform/index.scss                        |   14 
 scripts/build.js                                                                   |    2 
 src/tabviews/scriptmanage/index.jsx                                                |    2 
 src/components/sidemenu/index.jsx                                                  |  222 
 src/tabviews/zshare/settingcomponent/editTable/index.jsx                           |    4 
 src/assets/mobimg/navbar-mob.png                                                   |    0 
 src/mob/modulesource/dragsource/index.scss                                         |   46 
 src/views/login/index.jsx                                                          |   95 
 src/utils/utils-datamanage.js                                                      |   64 
 src/menu/picturecontroller/index.jsx                                               |    4 
 src/menu/sysinterface/settingform/simplescript/index.jsx                           |    2 
 src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx   |   14 
 src/menu/components/card/table-card/index.jsx                                      |   15 
 src/menu/components/carousel/cardcomponent/settingform/index.scss                  |   11 
 src/utils/utils-custom.js                                                          |   16 
 src/views/main/index.jsx                                                           |   18 
 src/mob/modulesource/option.jsx                                                    |   42 
 src/tabviews/custom/components/share/normalTable/index.scss                        |   15 
 src/tabviews/zshare/actionList/excelInbutton/index.jsx                             |    4 
 public/options.json                                                                |    6 
 src/templates/modalconfig/checkCard/index.jsx                                      |   16 
 src/tabviews/custom/components/carousel/prop-card/index.scss                       |   42 
 src/menu/components/tabs/tabcomponents/index.jsx                                   |    3 
 src/menu/components/table/normal-table/columns/editColumn/index.jsx                |   54 
 src/pc/modulesource/index.scss                                                     |    2 
 src/views/pcdesign/index.scss                                                      |  204 
 src/templates/formtabconfig/source.jsx                                             |    2 
 src/menu/components/group/groupsetting/index.jsx                                   |    2 
 src/menu/menushell/card.jsx                                                        |    9 
 src/templates/sharecomponent/settingcomponent/settingform/index.jsx                |   25 
 src/templates/calendarconfig/source.jsx                                            |    2 
 src/templates/modalconfig/dragelement/card.jsx                                     |   62 
 src/views/mobdesign/index.jsx                                                      | 1566 +++
 src/views/design/index.scss                                                        |    5 
 src/api/cacheutils.js                                                              |  371 
 src/menu/sysinterface/settingform/index.jsx                                        |   10 
 src/menu/components/share/clockcomponent/index.scss                                |    7 
 src/menu/components/share/actioncomponent/formconfig.jsx                           |  114 
 src/tabviews/custom/components/group/normal-group/index.jsx                        |    5 
 src/templates/menuconfig/editfirstmenu/index.jsx                                   |    4 
 src/views/design/sidemenu/config.jsx                                               |   14 
 src/templates/menuconfig/editfirstmenu/index.scss                                  |    5 
 src/tabviews/custom/components/carousel/prop-card/index.jsx                        |  363 
 src/templates/zshare/createinterface/index.jsx                                     |   17 
 src/templates/calendarconfig/tabcomponent/index.jsx                                |    2 
 src/menu/components/share/clockcomponent/index.jsx                                 |   70 
 src/menu/components/table/normal-table/index.jsx                                   |   19 
 src/menu/components/code/sandbox/editorcode/index.jsx                              |    2 
 src/menu/components/group/normal-group/index.jsx                                   |   15 
 src/tabviews/zshare/normalTable/index.scss                                         |   51 
 src/components/Image/index.jsx                                                     |    3 
 src/components/tabview/index.jsx                                                   |   22 
 src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx           |   25 
 src/views/design/header/index.scss                                                 |  160 
 src/menu/components/carousel/prop-card/index.scss                                  |   92 
 src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss            |   64 
 src/views/mobdesign/menuform/index.jsx                                             |  138 
 src/menu/datasource/verifycard/customscript/index.jsx                              |    2 
 src/templates/sharecomponent/searchcomponent/index.jsx                             |    2 
 src/assets/mobimg/carousel1.png                                                    |    0 
 src/menu/components/form/dragtitle/index.jsx                                       |   64 
 src/templates/sharecomponent/columncomponent/index.jsx                             |   17 
 src/menu/components/card/table-card/cardcomponent/index.jsx                        |    2 
 src/locales/en-US/model.js                                                         |    3 
 src/views/mobdesign/index.scss                                                     |  282 
 src/tabviews/custom/components/share/normalTable/index.jsx                         |   81 
 src/tabviews/iframe/index.jsx                                                      |    2 
 src/templates/modalconfig/dragelement/index.jsx                                    |   39 
 src/components/breadview/index.scss                                                |   73 
 src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx             |  160 
 src/tabviews/custom/components/card/data-card/index.scss                           |    3 
 src/utils/utils.js                                                                 |  515 +
 src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx              |   36 
 src/index.js                                                                       |   53 
 src/views/sso/index.jsx                                                            |    6 
 src/menu/components/share/usercomponent/index.jsx                                  |    9 
 src/menu/components/form/wrapsetting/settingform/index.jsx                         |  188 
 src/assets/mobimg/carousel.png                                                     |    0 
 src/tabviews/custom/components/share/tabtransfer/index.jsx                         |    5 
 src/tabviews/rolemanage/index.jsx                                                  |    5 
 src/templates/zshare/formconfig.jsx                                                |  120 
 src/components/querylog/index.jsx                                                  |    3 
 src/menu/components/tabs/tablabelform/index.jsx                                    |   31 
 src/tabviews/custom/components/form/normal-form/index.jsx                          |  378 
 /dev/null                                                                          |   10 
 src/templates/menuconfig/editsecmenu/index.jsx                                     |    4 
 src/menu/modalconfig/index.scss                                                    |   18 
 src/menu/modalconfig/index.jsx                                                     |  632 -
 src/templates/menuconfig/editthdmenu/menuform/index.jsx                            |    3 
 src/menu/menushell/index.jsx                                                       |   19 
 src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss |    4 
 src/templates/sharecomponent/chartgroupcomponent/index.jsx                         |    2 
 src/templates/sharecomponent/columncomponent/columnform/index.jsx                  |   55 
 350 files changed, 19,859 insertions(+), 4,659 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 20b5512..fd8ff65 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4195,7 +4195,7 @@
       "dependencies": {
         "immutable": {
           "version": "3.7.6",
-          "resolved": "https://registry.npmjs.org/immutable/-/immutable-3.7.6.tgz",
+          "resolved": "http://registry.npm.taobao.org/immutable/download/immutable-3.7.6.tgz",
           "integrity": "sha1-E7TTyxK++hVIKib+Gy665kAHHks="
         }
       }
@@ -4519,9 +4519,9 @@
       }
     },
     "caniuse-lite": {
-      "version": "1.0.30001102",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001102.tgz",
-      "integrity": "sha512-fOjqRmHjRXv1H1YD6QVLb96iKqnu17TjcLSaX64TwhGYed0P1E1CCWZ9OujbbK4Z/7zax7zAzvQidzdtjx8RcA=="
+      "version": "1.0.30001191",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001191.tgz",
+      "integrity": "sha512-xJJqzyd+7GCJXkcoBiQ1GuxEiOBCLQ0aVW9HMekifZsAVGdj5eJ4mFB9fEhSHipq9IOk/QXFJUiIr9lZT+EsGw=="
     },
     "capture-exit": {
       "version": "2.0.0",
@@ -11573,6 +11573,11 @@
         "verror": "1.10.0"
       }
     },
+    "jssha": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz",
+      "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q=="
+    },
     "jsx-ast-utils": {
       "version": "2.2.1",
       "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz",
diff --git a/package.json b/package.json
index 0e71613..5ca8645 100644
--- a/package.json
+++ b/package.json
@@ -23,7 +23,6 @@
     "braft-extensions": "^0.1.1",
     "browserslist": "^4.13.0",
     "camelcase": "^5.2.0",
-    "caniuse-lite": "^1.0.30001102",
     "case-sensitive-paths-webpack-plugin": "2.2.0",
     "codemirror": "^5.52.2",
     "css-loader": "2.1.1",
@@ -54,6 +53,7 @@
     "jest-resolve": "24.8.0",
     "jest-watch-typeahead": "0.3.1",
     "jsbarcode": "^3.11.3",
+    "jssha": "^3.2.0",
     "md5": "^2.2.1",
     "mini-css-extract-plugin": "0.5.0",
     "moment": "^2.24.0",
diff --git a/public/README.txt b/public/README.txt
index 902b54d..5429398 100644
--- a/public/README.txt
+++ b/public/README.txt
@@ -6,4 +6,6 @@
 systemType        -- 鍒ゆ柇涓氬姟绯荤粺涓烘祴璇� (绌�) 鎴栨寮� (production) 锛屾寮忕郴缁熷紑鍙戞潈闄愬彧鍚湁绯荤粺鍗囩骇绛夐檺瀹氬姛鑳�
 externalDatabase  -- 澶栬仈搴擄紝涓嶄娇鐢ㄦ椂榛樿涓篺alse
 lineColor         -- 鐧诲綍椤靛垎鍓茬嚎棰滆壊
-filter            -- 椤甸潰婊ら暅锛屽�间负'true'鏃讹紝椤甸潰鏄剧ず涓洪粦鐧借壊
\ No newline at end of file
+filter            -- 椤甸潰婊ら暅锛屽�间负'true'鏃讹紝椤甸潰鏄剧ず涓洪粦鐧借壊
+defaultApp        -- 榛樿搴旂敤锛岀郴缁熼渶榛樿鎵撳紑鏌愪釜瀛愬簲鐢ㄦ椂闇�濉啓搴旂敤缂栫爜锛岀┖鍊兼椂璺宠浆鍒扮鐞嗗悗鍙�
+defaultLang       -- 榛樿鎵撳紑鐨勫瓙搴旂敤璇█绫诲瀷锛屽~鍏efaultApp鏃舵湁鏁�
\ No newline at end of file
diff --git a/public/admin.html b/public/admin.html
new file mode 100644
index 0000000..1c43083
--- /dev/null
+++ b/public/admin.html
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8" />
+    <title></title>
+  </head>
+  <body>
+    <script>
+      if ('ActiveXObject' in window) {
+        window.onload = function() {
+          document.getElementById('root').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">鎮ㄧ殑娴忚鍣�<span style="color: #ff4d4f">涓嶅彈鏀寔</span></div>'
+        }
+      } else {
+        fetch(`./options.json`)
+          .then(function(response) {return response.json()})
+          .catch(function() {
+            document.body.innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">绯荤粺閰嶇疆淇℃伅鑾峰彇澶辫触锛岃鑱旂郴绠$悊鍛橈紒</div>'
+          })
+          .then(function(config) {
+            if (!config) {
+              document.body.innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">绯荤粺閰嶇疆淇℃伅鑾峰彇澶辫触锛岃鑱旂郴绠$悊鍛橈紒</div>'
+            } else {
+              var url = window.location.href.split(/(index.html)+/ig)[0]
+              var appPort = 'admin/index.html'
+              if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
+                appPort = 'mob/index.html'
+              } else {
+                if (config.defaultApp) {
+                  appPort = 'pc/'
+                }
+              }
+              window.location.replace(url + appPort)
+            }
+          })
+      }
+    </script>
+  </body>
+</html>
diff --git a/public/index.html b/public/index.html
index a48f176..2e1c8b4 100644
--- a/public/index.html
+++ b/public/index.html
@@ -3,6 +3,7 @@
   <head>
     <meta charset="utf-8" />
     <meta name="renderer" content="webkit">
+    <!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests" /> -->
     <meta name="viewport" content="width=device-width, initial-scale=1" />
     <meta name="theme-color" content="#000000" />
     <link rel="shortcut icon" href="#">
@@ -12,5 +13,12 @@
   <body>
     <noscript>You need to enable JavaScript to run this app.</noscript>
     <div id="root"></div>
+    <script>
+      if ('ActiveXObject' in window) {
+        window.onload = function() {
+          document.getElementById('root').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">鎮ㄧ殑娴忚鍣�<span style="color: #ff4d4f">涓嶅彈鏀寔</span></div>'
+        }
+      }
+    </script>
   </body>
 </html>
diff --git a/public/manifest.json b/public/manifest.json
index f8dfb04..2895bc5 100644
--- a/public/manifest.json
+++ b/public/manifest.json
@@ -1,18 +1,7 @@
 {
   "short_name": "MingKeOS",
   "name": "MingKe Operating System 6.0",
-  "icons": [
-    {
-      "src": "favicon.ico",
-      "sizes": "64x64 32x32 24x24 16x16",
-      "type": "image/x-icon"
-    },
-    {
-      "src": "logo.png",
-      "type": "image/png",
-      "sizes": "64x64"
-    }
-  ],
+  "icons": [],
   "start_url": ".",
   "display": "standalone",
   "theme_color": "#000000",
diff --git a/public/options.json b/public/options.json
index 1b4c2d5..9e7abbb 100644
--- a/public/options.json
+++ b/public/options.json
@@ -5,5 +5,9 @@
   "systemType": "",
   "externalDatabase": "false",
   "lineColor": "",
-  "filter": "false"
+  "filter": "false",
+  "defaultApp": "",
+  "defaultLang": "zh-CN",
+  "host": "http://qingqiumarket.cn",
+  "service": "mkwms/"
 }
\ No newline at end of file
diff --git a/scripts/build.js b/scripts/build.js
index a8cd5a2..1178ec6 100644
--- a/scripts/build.js
+++ b/scripts/build.js
@@ -1,4 +1,4 @@
-'use strict';
+// 'use strict';
 
 // Do this as the first thing so that any code reading it knows the right env.
 process.env.BABEL_ENV = 'production';
diff --git a/scripts/start.js b/scripts/start.js
index 0014802..284a7ce 100644
--- a/scripts/start.js
+++ b/scripts/start.js
@@ -1,4 +1,4 @@
-'use strict';
+// 'use strict';
 
 // Do this as the first thing so that any code reading it knows the right env.
 process.env.BABEL_ENV = 'development';
diff --git a/src/api/cacheutils.js b/src/api/cacheutils.js
new file mode 100644
index 0000000..2aa5642
--- /dev/null
+++ b/src/api/cacheutils.js
@@ -0,0 +1,371 @@
+/**
+ * @description 缂撳瓨宸ュ叿绫�
+ */
+export default class CacheUtils {
+  /**
+   * @description 鎵撳紑websql
+   */
+  static openWebSql () {
+    let service = window.GLOB.service ? '-' + window.GLOB.service.replace('/', '') : ''
+    try {
+      window.GLOB.WebSql = openDatabase(`mkdb${service}`, '1', 'mk-pc-database', 50 * 1024 * 1024)
+      window.GLOB.WebSql.transaction(tx => {
+        tx.executeSql('CREATE TABLE IF NOT EXISTS VERSIONS (version varchar(50), createDate varchar(50), CDefine1 varchar(50), CDefine2 varchar(50), CDefine3 varchar(50))', [], () => {
+        
+        }, () => {
+          // eslint-disable-next-line
+          throw 'CREATE TABLE ERROR'
+        })
+        tx.executeSql('CREATE TABLE IF NOT EXISTS CONFIGS (menuid varchar(50), userid varchar(50), openEdition varchar(50), webEdition varchar(50), LongParam text, LongParamUser text, CDefine1 varchar(50), CDefine2 varchar(50), CDefine3 varchar(50), CDefine4 varchar(50), CDefine5 varchar(50))', [], () => {
+
+        }, () => {
+          // eslint-disable-next-line
+          throw 'CREATE TABLE ERROR'
+        })
+      })
+      // window.GLOB.WebSql.transaction(tx => {
+      //   tx.executeSql('DROP TABLE VERSIONS')
+      //   tx.executeSql('DROP TABLE CONFIGS')
+      // })
+    } catch (e) {
+      console.warn('WebSql 鍒濆鍖栧け璐ワ紒')
+      window.GLOB.WebSql = null
+    }
+  }
+
+  /**
+   * @description 鑾峰彇websql涓繚瀛樹俊鎭増鏈�
+   */
+  static getWebSqlVersion () {
+    if (!window.GLOB.WebSql) {
+      return Promise.reject()
+    }
+    return new Promise((resolve, reject) => {
+      window.GLOB.WebSql.transaction(tx => {
+        tx.executeSql('SELECT * FROM VERSIONS', [], (tx, results) => {
+          if (results.rows.length === 0) {
+            tx.executeSql('DELETE FROM CONFIGS')
+            resolve({version: '', createDate: ''})
+          } else if (results.rows.length === 1) {
+            resolve(results.rows[0])
+          } else if (results.rows.length > 1) {
+            tx.executeSql('DELETE FROM VERSIONS')
+            tx.executeSql('DELETE FROM CONFIGS')
+            resolve({version: '', createDate: ''})
+          }
+        }, (tx, results) => {
+          window.GLOB.WebSql = null
+          reject()
+          console.warn(results)
+        })
+      })
+    })
+  }
+
+  /**
+   * @description 娓呯┖websql涓繚瀛樼殑閰嶇疆淇℃伅
+   */
+  static clearWebSqlConfig () {
+    if (!window.GLOB.WebSql) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql(`DELETE FROM CONFIGS`, [], () => {}, () => { window.GLOB.WebSql = null })
+    })
+  }
+
+  /**
+   * @description 鍒犻櫎websql涓繚瀛樼殑閰嶇疆淇℃伅
+   */
+  static delWebSqlConfig (keys) {
+    if (!window.GLOB.WebSql || !keys) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql(`DELETE FROM CONFIGS where menuid in (${keys})`, [], () => {}, () => {
+        window.GLOB.WebSql = null
+      })
+    })
+  }
+
+  /**
+   * @description 鍒犻櫎websql涓繚瀛樼殑閰嶇疆淇℃伅
+   */
+  static delMenuWebSqlConfig (menuId) {
+    if (!window.GLOB.WebSql || !menuId) return Promise.resolve()
+    return new Promise(resolve => {
+      window.GLOB.WebSql.transaction(tx => {
+        tx.executeSql(`DELETE FROM CONFIGS where menuid='${menuId}'`, [], () => {
+          resolve()
+        }, () => {
+          window.GLOB.WebSql = null
+          resolve()
+        })
+      })
+    })
+  }
+
+  /**
+   * @description 鏇存柊websql涓厤缃俊鎭殑淇濆瓨鏃堕棿
+   */
+  static updateWebSqlTime (curTime) {
+    if (!window.GLOB.WebSql || !curTime) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql(`UPDATE VERSIONS SET createDate='${curTime}'`, [], () => {}, () => {
+        window.GLOB.WebSql = null
+      })
+    })
+  }
+
+  /**
+   * @description 鏇存柊websql涓厤缃俊鎭殑鐗堟湰
+   */
+  static updateWebSqlversion (version, curTime) {
+    if (!window.GLOB.WebSql || !curTime || !version) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql(`UPDATE VERSIONS SET version='${version}', createDate='${curTime}'`, [], () => {}, () => {
+        window.GLOB.WebSql = null
+      })
+    })
+  }
+  
+  /**
+   * @description 鍒涘缓websql涓厤缃俊鎭殑鐗堟湰
+   */
+  static createWebSqlversion (version, curTime) {
+    if (!window.GLOB.WebSql || !curTime || !version) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql('INSERT INTO VERSIONS (version, createDate) VALUES (?, ?)', [version, curTime], () => {}, () => {
+        window.GLOB.WebSql = null
+      })
+    })
+  }
+
+  /**
+   * @description 鑾峰彇websql涓殑閰嶇疆淇℃伅
+   */
+  static getWebSqlMenuConfig (MenuID, userid) {
+    if (!window.GLOB.WebSql || !MenuID || !userid) return Promise.reject()
+    return new Promise((resolve, reject) => {
+      window.GLOB.WebSql.transaction(tx => {
+        tx.executeSql(`SELECT * FROM CONFIGS WHERE menuid='${MenuID}' and userid='${userid}'`, [], (tx, results) => {
+          let paramItem = results.rows[0]
+          if (paramItem) {
+            resolve({
+              ErrCode: 'S',
+              ErrMesg: '',
+              LongParam: paramItem.LongParam,
+              LongParamUser: paramItem.LongParamUser,
+              message: '',
+              open_edition: paramItem.openEdition,
+              status: true,
+              web_edition: paramItem.webEdition
+            })
+          } else {
+            reject()
+          }
+        }, (tx, results) => {
+          window.GLOB.WebSql = null
+          console.warn(results)
+          reject()
+        })
+      })
+    })
+  }
+
+  /**
+   * @description 灏嗘暟鎹啓鍏ebsql
+   */
+  static writeInWebSql (data) {
+    if (!window.GLOB.WebSql || !data) return
+    window.GLOB.WebSql.transaction(tx => {
+      tx.executeSql('INSERT INTO CONFIGS (menuid, userid, openEdition, webEdition, LongParam, LongParamUser) VALUES (?, ?, ?, ?, ?, ?)', data)
+    })
+  }
+
+  /**
+   * @description 鎵撳紑IndexedDB
+   */
+  static openIndexDB () {
+    let service = window.GLOB.service ? '-' + window.GLOB.service.replace('/', '') : ''
+    try {
+      let request = window.indexedDB.open(`mkdb${service}`, 1)
+      request.onerror = () => {
+        console.warn('IndexedDB 鍒濆鍖栧け璐ワ紒')
+      }
+      request.onsuccess = () => {
+        window.GLOB.IndexDB = request.result
+      }
+      request.onupgradeneeded = (event) => {
+        window.GLOB.IndexDB = event.target.result
+        if (!window.GLOB.IndexDB.objectStoreNames.contains('version')) {
+          window.GLOB.IndexDB.createObjectStore('version', { keyPath: 'id' })
+        }
+        if (!window.GLOB.IndexDB.objectStoreNames.contains('configs')) {
+          let objectStore = window.GLOB.IndexDB.createObjectStore('configs', { keyPath: 'id' })
+          objectStore.createIndex('menuid', 'menuid', { unique: false })
+          objectStore.createIndex('userid', 'userid', { unique: false })
+        }
+      }
+    } catch (e) {
+      console.warn('IndexedDB 鍒濆鍖栧け璐ワ紒')
+      window.GLOB.IndexDB = null
+    }
+  }
+
+  /**
+   * @description 鑾峰彇IndexedDB涓繚瀛樹俊鎭増鏈�
+   */
+  static getIndexDBVersion () {
+    if (!window.GLOB.IndexDB) {
+      return Promise.reject()
+    }
+    return new Promise((resolve, reject) => {
+      let request = window.GLOB.IndexDB.transaction(['version'])
+        .objectStore('version')
+        .get('mksoft')
+
+      request.onerror = (event) => {
+        window.GLOB.IndexDB = null
+        console.warn(event)
+        reject()
+      }
+
+      request.onsuccess = () => {
+        if (request.result) {
+          resolve(request.result)
+        } else {
+          this.clearIndexDBConfig()
+          resolve({version: '', createDate: ''})
+        }
+      }
+    })
+  }
+
+  /**
+   * @description 娓呯┖IndexedDB涓繚瀛樼殑閰嶇疆淇℃伅
+   */
+  static clearIndexDBConfig () {
+    if (!window.GLOB.IndexDB) return
+    let request = window.GLOB.IndexDB.transaction(['configs'], 'readwrite').objectStore('configs').clear()
+
+    request.onerror = () => {
+      window.GLOB.IndexDB = null
+    }
+  }
+
+  /**
+   * @description 鏇存柊IndexedDB涓厤缃俊鎭殑鐗堟湰
+   */
+  static updateIndexDBversion (version) {
+    if (!window.GLOB.IndexDB || !version) return
+
+    version.id = 'mksoft'
+
+    let objectStore = window.GLOB.IndexDB.transaction(['version'], 'readwrite').objectStore('version')
+    let request = objectStore.get('mksoft')
+
+    request.onerror = () => {
+      window.GLOB.IndexDB = null
+    }
+
+    request.onsuccess = () => {
+      if (request.result) {
+        let put = objectStore.put(version)
+
+        put.onerror = () => {
+          window.GLOB.IndexDB = null
+        }
+      } else {
+        this.clearIndexDBConfig()
+
+        let add = objectStore.add(version)
+
+        add.onerror = () => {
+          window.GLOB.IndexDB = null
+        }
+      }
+    }
+  }
+
+  /**
+   * @description 鍒犻櫎IndexedDB涓繚瀛樼殑閰嶇疆淇℃伅
+   */
+  static delMenuIndexDBConfig (key) {
+    if (!window.GLOB.IndexDB || !key) return Promise.resolve()
+
+    return new Promise(resolve => {
+      let request = window.GLOB.IndexDB.transaction(['configs'], 'readwrite')
+        .objectStore('configs')
+        .delete(key)
+
+      request.onsuccess = () => {
+        resolve()
+      }
+      request.onerror = () => {
+        window.GLOB.IndexDB = null
+        resolve()
+      }
+    })
+  }
+
+  /**
+   * @description 鍒犻櫎IndexedDB涓繚瀛樼殑閰嶇疆淇℃伅-鎵归噺
+   */
+  static delIndexDBConfig (keys) {
+    if (!window.GLOB.IndexDB || !keys) return
+
+    let objectStore = window.GLOB.IndexDB.transaction(['configs'], 'readwrite').objectStore('configs')
+
+    objectStore.openCursor().onsuccess = (event) => {
+      let cursor = event.target.result
+
+      if (cursor) {
+        if (cursor.value && keys.includes(cursor.value.menuid)) {
+          let request = objectStore.delete(cursor.key)
+
+          request.onerror = () => {
+            window.GLOB.IndexDB = null
+          }
+        }
+
+        cursor.continue()
+      }
+    }
+  }
+
+  /**
+   * @description 鑾峰彇IndexedDB涓殑閰嶇疆淇℃伅
+   */
+  static getIndexDBMenuConfig (MenuID, userid) {
+    if (!window.GLOB.IndexDB || !MenuID || !userid) return Promise.reject()
+    let key = MenuID + userid
+    return new Promise((resolve, reject) => {
+      let request = window.GLOB.IndexDB.transaction(['configs']).objectStore('configs').get(key)
+
+      request.onerror = () => {
+        window.GLOB.IndexDB = null
+        reject()
+      }
+
+      request.onsuccess = () => {
+        if (request.result) {
+          resolve(request.result)
+        } else {
+          reject()
+        }
+      }
+    })
+  }
+
+  /**
+   * @description 灏嗘暟鎹啓鍏ndexedDB
+   */
+  static writeInIndexDB (data) {
+    if (!window.GLOB.IndexDB || !data) return
+
+    let request = window.GLOB.IndexDB.transaction(['configs'], 'readwrite')
+      .objectStore('configs')
+      .add(data)
+
+    request.onerror = () => {
+      window.GLOB.IndexDB = null
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/api/index.js b/src/api/index.js
index 9595a99..ad91d61 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -2,50 +2,36 @@
 import qs from 'qs'
 import { notification } from 'antd'
 import md5 from 'md5'
+import jsSHA from 'jssha'
 import moment from 'moment'
 import Utils from '@/utils/utils.js'
+import CacheUtils from './cacheutils'
 import options from '@/store/options.js'
 
-let mkDataBase = null
+window.GLOB.WebSql = null
+window.GLOB.IndexDB = null
+const systemMenuKeys = `1581067625930haged11ieaivpavv77k,1581734956310scks442ul2d955g9tu5,1583991994144ndddg0bhh0is6shi0v1,1583979633842550imkchl4qt4qppsiv,1578900109100np8aqd0a77q3na46oas,16044812935562g807p3p12huk8kokmb,
+  1585192949946f3et2ts8tn82krmumdf,15855615451212m12ip23vpcm79kloro,1587005717541lov40vg61q7l1rbveon,1590458676585agbbr63t6ihighg2i1g,1602315375262ikd33ii0nii34pt861o,1582771068837vsv54a089lgp45migbg,
+  1582777675954ifu05upurs465omoth7,158294809668898cklbv6c5bou8e1fpu,1584676379094iktph45fb8imhg96bql,1584695125339vo5g7iqgfn01qmrd6s2,1584699661372vhmpp9dn9foo0eob722,15848421131551gg04ie8sitsd3f7467,
+  1589782279158ngr675kk3oksin35sul,1589788042787ffdt9hle4s45k9r1nvs,15900310928174dro07ihfckghpb5h13,1594095599055qicg2eb642v5qglhnuo,1599613340050c8nu6rbst9d4emnnbsq,1577972969199lei1g0qkvlh4tkc908m,
+  1578479100252lfbp29v1kafk4s4q4ig,1577971621421tg4v0i1ur8873k7e0ob,1577929944419lgc5h3hepum765e2k7u,1588493493409k9guqp067d31lu7blsv`
 
 if (window.openDatabase) {
-  let service = window.GLOB.service ? '-' + window.GLOB.service.replace('/', '') : ''
-  try {
-    mkDataBase = openDatabase(`mkdb${service}`, '1', 'mk-pc-database', 50 * 1024 * 1024)
-    mkDataBase.transaction(tx => {
-      tx.executeSql('CREATE TABLE IF NOT EXISTS VERSIONS (version varchar(50), createDate varchar(50), CDefine1 varchar(50), CDefine2 varchar(50), CDefine3 varchar(50))', [], () => {
-      
-      }, () => {
-        // eslint-disable-next-line
-        throw 'CREATE TABLE ERROR'
-      })
-      tx.executeSql('CREATE TABLE IF NOT EXISTS CONFIGS (menuid varchar(50), userid varchar(50), openEdition varchar(50), webEdition varchar(50), LongParam text, LongParamUser text, CDefine1 varchar(50), CDefine2 varchar(50), CDefine3 varchar(50), CDefine4 varchar(50), CDefine5 varchar(50))', [], () => {
-
-      }, () => {
-        // eslint-disable-next-line
-        throw 'CREATE TABLE ERROR'
-      })
-    })
-    // mkDataBase.transaction(tx => {
-    //   tx.executeSql('DROP TABLE VERSIONS')
-    //   tx.executeSql('DROP TABLE CONFIGS')
-    // })
-  } catch (e) {
-    console.warn(e)
-    mkDataBase = null
-  }
+  CacheUtils.openWebSql()
+} else if (window.indexedDB) {
+  CacheUtils.openIndexDB()
 }
 
 axios.defaults.crossDomain = true
 axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
-axios.defaults.withCredentials = true
+axios.defaults.withCredentials = false
 
 axios.interceptors.request.use((config) => {
   if (config.url.includes('LoginAndRedirect') || config.url.includes('getjsonresult') || config.url.includes('wxNativePay')) {
     config.data = qs.stringify(config.data)
   } else if (config.url.includes('Upload') || config.url.includes('doupload') || config.url.includes('dopreload')) {
     config.headers = { 'Content-Type': 'multipart/form-data' }
-  } else if (config.method === 'post') {
+  } else if (config.method === 'post' && config.data) {
     config.data = JSON.stringify(config.data)
   }
 
@@ -107,7 +93,7 @@
     if (process.env.NODE_ENV === 'production') {
       axios.defaults.baseURL = document.location.origin + '/' + window.GLOB.service
     } else {
-      // axios.defaults.baseURL = 'http://127.0.0.1:8888'
+      axios.defaults.baseURL = window.GLOB.location + '/' + window.GLOB.service
     }
   }
   
@@ -147,7 +133,20 @@
   /* @description 鐩存帴璇锋眰
    * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
    */
-  directRequest (url, method = 'post', param) {
+  directRequest (url, method = 'post', param, cross) {
+    if (cross === 'true' && param) {
+      return axios({
+        url,
+        method,
+        data: param
+      })
+    } else if (cross === 'true') {
+      return axios({
+        url,
+        method
+      })
+    }
+
     let params = { method: 'post' }
     let _url = url
 
@@ -162,27 +161,9 @@
     }
 
     _url = _url.replace(/&/ig, '%26')
-    // _url = window.btoa(_url)
     params.url = '/trans/redirect?rd=' + _url + '&method=' + method
 
     return axios(params)
-  }
-
-  /**
-   * @description 浣跨敤dostar鎺ュ彛锛岃烦杞嚦dostars
-   * @param {Object} param 鏌ヨ鍙婃彁浜ゅ弬鏁�
-   */
-  dostarToDostars (param) {
-    param.userid = param.userid || sessionStorage.getItem('UserID') || ''
-    param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
-
-    param = this.encryptParam(param)
-
-    return axios({
-      url: '/webapi/dostar',
-      method: 'post',
-      data: param
-    })
   }
 
   /**
@@ -208,12 +189,14 @@
 
     param.appkey = window.GLOB.appkey || ''
 
+    let url = '/webapi/dologon/s_visitor_login'
     if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
+      // url = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon/s_visitor_login')
+      param.rduri = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon/s_visitor_login')
     }
 
     return axios({
-      url: '/webapi/dologon',
+      url: url,
       method: 'post',
       data: param
     })
@@ -222,7 +205,7 @@
   /**
    * @description 鎵嬫満鍙烽獙璇佺爜鐧诲綍
    */
-  getphoneusermsg (phoneNo, checkcode, isCloud = false) {
+  getphoneusermsg (phoneNo, checkcode, isCloud = false, ipAddress, city) {
     let param = {
       // func: 'webapi_login',
       mob: phoneNo,
@@ -230,22 +213,30 @@
       Password: '',
       check_code: checkcode,
       way_no: 'sms_vcode',
-      systemType: options.sysType
+      systemType: options.sysType,
+      login_city: city,
+      login_id_address: ipAddress
     }
 
     param.appkey = window.GLOB.appkey || ''
 
+    let url = '/webapi/dologon'
     if (isCloud) {
       param.debug = 'Y'
       if (options.cloudServiceApi) {
+        // url = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
         param.rduri = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       }
-    } else if (!isCloud && window.GLOB.mainSystemApi) {
+    } else if (window.GLOB.mainSystemApi) {
+      if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
+        param.linkurl = window.GLOB.linkurl
+      }
+      // url = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       param.rduri = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
     }
 
     return axios({
-      url: '/webapi/dologon',
+      url,
       method: 'post',
       data: param
     })
@@ -254,29 +245,44 @@
   /**
    * @description 鐧诲綍绯荤粺, 鑾峰彇鐢ㄦ埛淇℃伅
    */
-  getusermsg (username, password, isCloud = false) {
+  getusermsg (username, password, isCloud = false, ipAddress, city) {
     let param = {
       // func: 'webapi_login',
       UserName: username,
-      Password: password,
       systemType: options.sysType,
-      Type: 'X'
+      Type: 'S',
+      login_city: city,
+      login_id_address: ipAddress
     }
 
-    param.Password = Utils.formatOptions(param.Password)
+    // Type: 'S' 鏃�
+    let shaObj = new jsSHA('SHA-1', 'TEXT')
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    shaObj.update(password)
+    param.Password = shaObj.getHash('HEX').toUpperCase()
+    param.Password = md5(username + param.Password + param.timestamp)
+
+    // Type: 'X' 鏃�
+    // param.Password = Utils.formatOptions(password)
     param.appkey = window.GLOB.appkey || ''
+    let url = '/webapi/dologon'
 
     if (isCloud) {
       param.debug = 'Y'
       if (options.cloudServiceApi) {
+        // url = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
         param.rduri = options.cloudServiceApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       }
-    } else if (!isCloud && window.GLOB.mainSystemApi) {
+    } else if (window.GLOB.mainSystemApi) {
+      if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
+        param.linkurl = window.GLOB.linkurl
+      }
+      // url = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
       param.rduri = window.GLOB.mainSystemApi.replace(/\/webapi(.*)/, '/webapi/dologon')
     }
 
     return axios({
-      url: '/webapi/dologon',
+      url,
       method: 'post',
       data: param
     })
@@ -287,265 +293,178 @@
    */
   getAppVersion (_resolve, _reject) {
     let appVersion = {}
-    
-    new Promise((resolve, reject) => {
-      if (!mkDataBase) {
-        reject()
-      } else {
-        mkDataBase.transaction(tx => {
-          if (!mkDataBase) {
-            reject()
-            return
-          }
-          tx.executeSql('SELECT * FROM VERSIONS', [], (tx, results) => {
-            if (results.rows.length === 0) {
-              tx.executeSql('DELETE FROM CONFIGS')
-              resolve({version: '', createDate: ''})
-            } else if (results.rows.length === 1) {
-              resolve(results.rows[0])
-            } else if (results.rows.length > 1) {
-              tx.executeSql('DELETE FROM VERSIONS')
-              tx.executeSql('DELETE FROM CONFIGS')
-              resolve({version: '', createDate: ''})
-            }
-          }, (tx, results) => {
-            reject()
-            console.warn(results)
-          })
-        })
-      }
-    }).then(msg => {
-      if (msg.version) {
-        appVersion.oldVersion = msg.version
-      }
-      let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
-
-      let param = {
-        func: 's_get_app_version',
-        modifydate: msg.createDate || curTime,
-      }
-  
-      param.userid = sessionStorage.getItem('UserID') || ''
-      param.lang = localStorage.getItem('lang') || ''
-      param.SessionUid = localStorage.getItem('SessionUid') || ''
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-      param.appkey = window.GLOB.appkey || ''
-  
-      if (window.GLOB.mainSystemApi) {
-        param.rduri = window.GLOB.mainSystemApi
-      }
-  
-      param = this.encryptParam(param)
-  
-      return new Promise((resolve, reject) => {
-        axios({
-          url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
-          method: 'post',
-          data: param
-        }).then(res => {
-          if (!res.status) {
-            reject()
-            return
-          }
-          let clear = false
-          let version = res.app_version || '1.00'
-          appVersion.newVersion = version
-          appVersion.oldVersion = appVersion.oldVersion || version
-
-          if (res.menu_data && res.menu_data.length > 0) {
-            let keys = `1581067625930haged11ieaivpavv77k,1581734956310scks442ul2d955g9tu5,1583991994144ndddg0bhh0is6shi0v1,1583979633842550imkchl4qt4qppsiv,1578900109100np8aqd0a77q3na46oas,16044812935562g807p3p12huk8kokmb,
-              1585192949946f3et2ts8tn82krmumdf,15855615451212m12ip23vpcm79kloro,1587005717541lov40vg61q7l1rbveon,1590458676585agbbr63t6ihighg2i1g,1602315375262ikd33ii0nii34pt861o,1582771068837vsv54a089lgp45migbg,
-              1582777675954ifu05upurs465omoth7,158294809668898cklbv6c5bou8e1fpu,1584676379094iktph45fb8imhg96bql,1584695125339vo5g7iqgfn01qmrd6s2,1584699661372vhmpp9dn9foo0eob722,15848421131551gg04ie8sitsd3f7467,
-              1589782279158ngr675kk3oksin35sul,1589788042787ffdt9hle4s45k9r1nvs,15900310928174dro07ihfckghpb5h13,1594095599055qicg2eb642v5qglhnuo,1599613340050c8nu6rbst9d4emnnbsq,1577972969199lei1g0qkvlh4tkc908m,
-              1578479100252lfbp29v1kafk4s4q4ig,1577971621421tg4v0i1ur8873k7e0ob,1577929944419lgc5h3hepum765e2k7u,1588493493409k9guqp067d31lu7blsv`
-
-            res.menu_data.forEach(mid => {
-              if (keys.indexOf(mid.menuid) > -1) {
-                clear = true
-              }
-            })
-          }
-
-          mkDataBase.transaction(tx => {
-            if (clear) {
-              tx.executeSql(`DELETE FROM CONFIGS`, [], () => {}, () => { reject() })
-            } else if (res.menu_data && res.menu_data.length > 0) {
-              let keys = res.menu_data.map(mid => `'${mid.menuid}'`)
-              tx.executeSql(`DELETE FROM CONFIGS where menuid in (${keys.join(',')})`, [], () => {}, () => {
-                reject()
-              })
-            }
-            if (msg.version) {
-              tx.executeSql(`UPDATE VERSIONS SET createDate='${curTime}'`, [], () => {
-                resolve()
-              }, () => {
-                reject()
-              })
-            } else {
-              tx.executeSql('INSERT INTO VERSIONS (version, createDate) VALUES (?, ?)', [version, curTime], () => {
-                resolve()
-              }, () => {
-                reject()
-              })
-            }
-          })
-        })
-      }) 
-    }, () => {
-      mkDataBase = null
-      _reject()
+    if (!window.GLOB.WebSql && !window.GLOB.IndexDB) {
       return Promise.reject()
-    }).then(() => {
-      _resolve(appVersion)
-    }, () => {
-      mkDataBase = null
-      _reject()
-    })
+    }
+
+    if (window.GLOB.WebSql) {
+      return new Promise((resolve, reject) => {
+        CacheUtils.getWebSqlVersion().then(msg => {
+          appVersion.oldVersion = msg.version || ''
+          let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+          let param = {
+            func: 's_get_app_version',
+            modifydate: msg.createDate || curTime,
+          }
+  
+          this.getSystemConfig(param).then(res => {
+            if (!res.status) {
+              reject()
+              return
+            }
+            let clear = false
+            let version = res.app_version || '1.00'
+            appVersion.newVersion = version
+            appVersion.oldVersion = appVersion.oldVersion || version
+  
+            if (res.menu_data && res.menu_data.length > 0) {
+              res.menu_data.forEach(mid => {
+                if (systemMenuKeys.indexOf(mid.menuid) > -1) {
+                  clear = true
+                }
+              })
+
+              if (clear) {
+                CacheUtils.clearWebSqlConfig()
+              } else {
+                let keys = res.menu_data.map(mid => `'${mid.menuid}'`).join(',')
+                CacheUtils.delWebSqlConfig(keys)
+              }
+            }
+            
+            if (msg.version) {
+              CacheUtils.updateWebSqlTime(curTime)
+            } else {
+              CacheUtils.createWebSqlversion(version, curTime)
+            }
+  
+            resolve(appVersion)
+          })
+        }, () => {
+          reject()
+        })
+      })
+    } else {
+      return new Promise((resolve, reject) => {
+        CacheUtils.getIndexDBVersion().then(msg => {
+          appVersion.oldVersion = msg.version || ''
+          let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+          let param = {
+            func: 's_get_app_version',
+            modifydate: msg.createDate || curTime,
+          }
+
+          this.getSystemConfig(param).then(res => {
+            if (!res.status) {
+              reject()
+              return
+            }
+            let clear = false
+            let version = res.app_version || '1.00'
+            appVersion.newVersion = version
+            appVersion.oldVersion = appVersion.oldVersion || version
+  
+            if (res.menu_data && res.menu_data.length > 0) {
+              res.menu_data.forEach(mid => {
+                if (systemMenuKeys.indexOf(mid.menuid) > -1) {
+                  clear = true
+                }
+              })
+
+              if (clear) {
+                CacheUtils.clearIndexDBConfig()
+              } else {
+                let keys = res.menu_data.map(mid => `'${mid.menuid}'`)
+                CacheUtils.delIndexDBConfig(keys)
+              }
+            }
+
+            CacheUtils.updateIndexDBversion({version: appVersion.oldVersion, createDate: curTime})
+  
+            resolve(appVersion)
+          })
+        }, () => {
+          reject()
+        })
+      })
+    }
   }
 
   /**
    * @description 鏇存柊绯荤粺鐗堟湰淇℃伅锛屾竻绌洪厤缃俊鎭�
    */
   updateAppVersion (newVersion) {
-    return new Promise(resolve => {
-      if (!mkDataBase) {
-        resolve({status: false})
-        return
-      }
-      let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
-      
-      mkDataBase.transaction(tx => {
-        tx.executeSql(`DELETE FROM CONFIGS`, [], () => {}, () => {
-          resolve({status: false})
-        })
-        tx.executeSql(`UPDATE VERSIONS SET version='${newVersion}', createDate='${curTime}'`, [], () => {
-          resolve({status: true})
-        }, () => {
-          resolve({status: false})
-        })
-      })
-    })
+    let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
+    CacheUtils.clearWebSqlConfig()
+    CacheUtils.updateWebSqlversion(newVersion, curTime)
+    CacheUtils.clearIndexDBConfig()
+    CacheUtils.updateIndexDBversion({version: newVersion, createDate: curTime})
   }
 
   /**
    * @description 鍒犻櫎鏌愪釜鑿滃崟閰嶇疆淇℃伅
    */
   deleteMenuStorage (menuId) {
-    return new Promise(resolve => {
-      if (!mkDataBase) {
-        resolve()
-        return
+    if (window.GLOB.IndexDB) {
+      let key = menuId + (sessionStorage.getItem('UserID') || '')
+    
+      if (sessionStorage.getItem('isEditState') === 'true' && options.cloudServiceApi) {
+        key = menuId + (sessionStorage.getItem('CloudUserID') || '')
       }
-      mkDataBase.transaction(tx => {
-        tx.executeSql(`DELETE FROM CONFIGS where menuid='${menuId}'`, [], () => {
-          resolve()
-        }, () => {
-          mkDataBase = null
-        })
-      })
-    })
+
+      return CacheUtils.delMenuIndexDBConfig(key)
+    } else {
+      return CacheUtils.delMenuWebSqlConfig(menuId)
+    }
   }
 
   /**
    * @description 鑾峰彇鎴栦慨鏀逛簯绔厤缃�
    */
   getCloudConfig (param) {
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.appkey = window.GLOB.appkey || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
-
-    if (sessionStorage.getItem('CloudUserID') && options.cloudServiceApi) { // 瀛樺湪浜戠鐧诲綍淇℃伅锛屼笖瀛樺湪浜戠鍦板潃
-      param.rduri = options.cloudServiceApi
-      param.userid = sessionStorage.getItem('CloudUserID')
-      param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
-    } else if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    } else {
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    }
+    param.userid = sessionStorage.getItem('CloudUserID') || ''
+    param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
 
     param = this.encryptParam(param)
 
+    let url = options.cloudServiceApi ? options.cloudServiceApi : '/webapi/dostars'
+    if (param.func) {
+      url = url + '/' + param.func
+    }
+
     return axios({
-      url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      url,
       method: 'post',
       data: param
     })
   }
 
   /**
-   * @description 鑾峰彇浜戠閰嶇疆锛屽苟缂撳瓨淇℃伅
-   */
-  getCloudCacheConfig (param) {
-    param.lang = localStorage.getItem('lang') || ''
-    param.appkey = window.GLOB.appkey || ''
-    param.SessionUid = localStorage.getItem('SessionUid') || ''
-
-    if (sessionStorage.getItem('CloudUserID') && options.cloudServiceApi) { // 瀛樺湪浜戠鐧诲綍淇℃伅锛屼笖瀛樺湪浜戠鍦板潃
-      param.rduri = options.cloudServiceApi
-      param.userid = sessionStorage.getItem('CloudUserID')
-      param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
-    } else if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    } else {
-      param.userid = sessionStorage.getItem('UserID')
-      param.LoginUID = sessionStorage.getItem('LoginUID') || ''
-    }
-
-    let _param = JSON.parse(JSON.stringify(param)) // 缂撳瓨鏍¢獙锛屽幓闄ゆ椂闂村拰鍔犲瘑瀛楃
-    delete _param.timestamp
-    delete _param.secretkey
-    delete _param.open_key
-    _param = JSON.stringify(_param)
-    _param  = md5(_param)
-    
-    if (window.GLOB.CacheMap.has(_param)) {
-      return Promise.resolve(window.GLOB.CacheMap.get(_param))
-    } else {
-      param = this.encryptParam(param)
-
-      return new Promise(resolve => {
-        axios({
-          url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
-          method: 'post',
-          data: param
-        }).then(res => {
-          if (res.status) {
-            window.GLOB.CacheMap.set(_param, res)
-          }
-          resolve(res)
-        })
-      })
-    }
-  }
-
-  /**
    * @description 鑾峰彇鎴栦慨鏀圭郴缁熼厤缃紝澧炲姞appkey
    */
   getSystemConfig (param) {
-    param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.userid = param.userid || sessionStorage.getItem('UserID') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
-    param.LoginUID = sessionStorage.getItem('LoginUID') || ''
+    param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
+    let url = '/webapi/dostars'
     if (sessionStorage.getItem('isEditState') === 'true' && options.cloudServiceApi) { // 缂栬緫鐘舵�侊紝涓斿瓨鍦ㄤ簯绔湴鍧�
-      param.rduri = options.cloudServiceApi
+      url = options.cloudServiceApi
       param.userid = sessionStorage.getItem('CloudUserID') || ''
       param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
     } else if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
+      url = window.GLOB.mainSystemApi
     }
 
     param = this.encryptParam(param)
 
     return axios({
-      url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      url: `${url}${param.func ? '/' + param.func : ''}`,
       method: 'post',
       data: param
     })
@@ -555,16 +474,22 @@
    * @description 鑾峰彇鎴栦慨鏀规湰鍦伴厤缃紝澧炲姞appkey
    */
   getLocalConfig (param) {
-    param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.userid = param.userid || sessionStorage.getItem('UserID') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
-    param.LoginUID = sessionStorage.getItem('LoginUID') || ''
+    param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
+
+    let url = '/webapi/dostars'
+    if (param.rduri) {
+      url = param.rduri
+      delete param.rduri
+    }
 
     param = this.encryptParam(param)
 
     return axios({
-      url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+      url: `${url}${param.func ? '/' + param.func : ''}`,
       method: 'post',
       data: param
     })
@@ -576,19 +501,20 @@
    */
   getCacheConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
+    let url = '/webapi/dostars'
     if (sessionStorage.getItem('isEditState') === 'true') { // 缂栬緫鐘舵�侊紝鍗曠偣鐧诲綍鏈嶅姟鍣ㄤ负浜戠
       if (options.cloudServiceApi) { // 瀛樺湪浜戠鍦板潃鏃讹紝浣跨敤浜戠绯荤粺鍙傛暟
-        param.rduri = options.cloudServiceApi
+        url = options.cloudServiceApi
         param.userid = sessionStorage.getItem('CloudUserID') || ''
         param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
       }
     } else if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
+      url = window.GLOB.mainSystemApi
     }
 
     let _param = JSON.parse(JSON.stringify(param)) // 缂撳瓨鏍¢獙锛屽幓闄ゆ椂闂村拰鍔犲瘑瀛楃
@@ -598,50 +524,49 @@
     _param = JSON.stringify(_param)
     _param  = md5(_param)
     
-    if (mkDataBase) {
-      param = this.encryptParam(param)
-
+    if (window.GLOB.WebSql) {
       return new Promise(resolve => {
-        mkDataBase.transaction(tx => {
-          tx.executeSql(`SELECT * FROM CONFIGS WHERE menuid='${param.MenuID}' and userid='${param.userid}'`, [], (tx, results) => {
-            let paramItem = results.rows[0]
-            if (paramItem) {
-              resolve({
-                ErrCode: 'S',
-                ErrMesg: '',
-                LongParam: paramItem.LongParam,
-                LongParamUser: paramItem.LongParamUser,
-                message: '',
-                open_edition: paramItem.openEdition,
-                status: true,
-                web_edition: paramItem.webEdition
-              })
-            } else {
-              axios({
-                url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
-                method: 'post',
-                data: param
-              }).then(res => {
-                if (res.status) {
-                  this.writeInWebSql([param.MenuID, param.userid, res.open_edition, res.web_edition, res.LongParam, res.LongParamUser])
-                }
-                resolve(res)
-              })
+        CacheUtils.getWebSqlMenuConfig(param.MenuID, param.userid).then(res => {
+          resolve(res)
+        }, () => {
+          param = this.encryptParam(param)
+          axios({
+            url: `${url}${param.func ? '/' + param.func : ''}`,
+            method: 'post',
+            data: param
+          }).then(res => {
+            if (res.status && window.GLOB.WebSql) {
+              CacheUtils.writeInWebSql([param.MenuID, param.userid, res.open_edition, res.web_edition, res.LongParam, res.LongParamUser])
+            } else if (res.status) {
+              window.GLOB.CacheMap.set(_param, res)
             }
-          }, (tx, results) => {
-            mkDataBase = null
-            console.warn(results)
-
-            axios({
-              url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
-              method: 'post',
-              data: param
-            }).then(res => {
-              if (res.status) {
-                window.GLOB.CacheMap.set(_param, res)
+            resolve(res)
+          })
+        })
+      })
+    } else if (window.GLOB.IndexDB) {
+      return new Promise(resolve => {
+        CacheUtils.getIndexDBMenuConfig(param.MenuID, param.userid).then(res => {
+          resolve(res)
+        }, () => {
+          param = this.encryptParam(param)
+          axios({
+            url: `${url}${param.func ? '/' + param.func : ''}`,
+            method: 'post',
+            data: param
+          }).then(res => {
+            if (res.status && window.GLOB.IndexDB) {
+              let msg = {
+                ...res,
+                userid: param.userid,
+                menuid: param.MenuID,
+                id: param.MenuID + param.userid
               }
-              resolve(res)
-            })
+              CacheUtils.writeInIndexDB(msg)
+            } else if (res.status) {
+              window.GLOB.CacheMap.set(_param, res)
+            }
+            resolve(res)
           })
         })
       })
@@ -652,7 +577,7 @@
 
       return new Promise(resolve => {
         axios({
-          url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          url: `${url}${param.func ? '/' + param.func : ''}`,
           method: 'post',
           data: param
         }).then(res => {
@@ -671,31 +596,19 @@
    */
   getLocalCacheConfig (param) {
     param.userid = sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
-    if (window.GLOB.mainSystemApi) {
-      param.rduri = window.GLOB.mainSystemApi
-    }
-
     let _param  = md5(JSON.stringify(param))
     
-    if (mkDataBase) {
+    if (window.GLOB.WebSql) {
       return new Promise(resolve => {
-        mkDataBase.transaction(tx => {
-          tx.executeSql(`SELECT * FROM CONFIGS WHERE menuid='${param.MenuID}' and userid='${param.userid}'`, [], (tx, results) => {
-            let paramItem = results.rows[0]
-            if (paramItem) {
-              resolve({ ErrCode: 'S', ErrMesg: '', LongParam: paramItem.LongParam, message: '', status: true })
-            } else {
-              resolve({ ErrCode: 'S', ErrMesg: '', LongParam: '', message: '', status: false })
-            }
-          }, (tx, results) => {
-            mkDataBase = null
-            resolve({ErrCode: 'S', ErrMesg: '', LongParam: '', message: '', status: false})
-          })
+        CacheUtils.getWebSqlMenuConfig(param.MenuID, param.userid).then(res => {
+          resolve(res)
+        }, () => {
+          resolve({ ErrCode: 'S', ErrMesg: '', LongParam: '', message: '', status: false })
         })
       })
     } else if (window.GLOB.CacheMap.has(_param)) {
@@ -703,16 +616,6 @@
     } else {
       return Promise.resolve({ErrCode: 'S', ErrMesg: '', LongParam: '', message: '', status: false})
     }
-  }
-
-  /**
-   * @description 灏嗘暟鎹啓鍏ebsql
-   */
-  writeInWebSql (data) {
-    if (!mkDataBase) return
-    mkDataBase.transaction(tx => {
-      tx.executeSql('INSERT INTO CONFIGS (menuid, userid, openEdition, webEdition, LongParam, LongParamUser) VALUES (?, ?, ?, ?, ?, ?)', data)
-    })
   }
 
   /**
@@ -746,10 +649,16 @@
    */
   getSystemCacheConfig (param) {
     param.userid = param.userid || sessionStorage.getItem('UserID') || ''
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = param.lang || sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = param.LoginUID || sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
+
+    let url = '/webapi/dostars'
+    if (param.rduri) {
+      url = param.rduri
+      delete param.rduri
+    }
 
     let _param = JSON.parse(JSON.stringify(param)) // 缂撳瓨鏍¢獙锛屽幓闄ゆ椂闂村拰鍔犲瘑瀛楃
     delete _param.timestamp
@@ -765,7 +674,7 @@
 
       return new Promise(resolve => {
         axios({
-          url: `/webapi/dostars${param.func ? '/' + param.func : ''}`,
+          url: `${url}${param.func ? '/' + param.func : ''}`,
           method: 'post',
           data: param
         }).then(res => {
@@ -782,16 +691,23 @@
    * @description 鑾峰彇涓氬姟閫氱敤鎺ュ彛
    */
   genericInterface (param) {
-    param.userid = sessionStorage.getItem('UserID')
-    param.lang = localStorage.getItem('lang') || ''
+    param.userid = sessionStorage.getItem('UserID') || ''
+    param.lang = sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
 
     if (options.cloudServiceApi && param.rduri === options.cloudServiceApi) { // HS涓嬭彍鍗�
-      param.userid = sessionStorage.getItem('CloudUserID') || ''
-      param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+      param.userid = sessionStorage.getItem('CloudUserID') || param.userid || ''
+      param.LoginUID = sessionStorage.getItem('CloudLoginUID') || param.LoginUID || ''
     }
+
+    // 寰呬紭鍖栵紝澧炲姞鏄惁鏀寔璺ㄥ煙璇锋眰
+    // let url = '/webapi/dostars'
+    // if (param.rduri) {
+    //   url = param.rduri
+    //   delete param.rduri
+    // }
 
     param = this.encryptParam(param)
 
@@ -807,7 +723,7 @@
    */
   getExcelOut (param, name) {
     param.userid = sessionStorage.getItem('UserID')
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = sessionStorage.getItem('lang') || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
     param.LoginUID = sessionStorage.getItem('LoginUID') || ''
     param.appkey = window.GLOB.appkey || ''
@@ -819,7 +735,6 @@
         method: 'post',
         data: param
       }).then(res => {
-
         try {
           const blob = new Blob([res])
           
@@ -861,7 +776,7 @@
   fileuploadbase64 (param) {
     param.func = ''
     param.BasePath = 'Content/Upload'
-    param.lang = localStorage.getItem('lang') || ''
+    param.lang = sessionStorage.getItem('lang') || ''
     param.appkey = window.GLOB.appkey || ''
     param.SessionUid = localStorage.getItem('SessionUid') || ''
 
@@ -871,10 +786,11 @@
     param = this.encryptParam(param)
 
     if (param.rduri) {
-      param.rduri = param.rduri.replace(/webapi(.*)$/, 'webapi/SaveBase64Image')
+      let url = param.rduri.replace(/webapi(.*)$/, 'webapi/SaveBase64Image')
+      delete param.rduri
 
       return axios({
-        url: '/webapi/dostars',
+        url,
         method: 'post',
         data: param
       })
diff --git a/src/assets/css/global.scss b/src/assets/css/global.scss
deleted file mode 100644
index d117c7c..0000000
--- a/src/assets/css/global.scss
+++ /dev/null
@@ -1,59 +0,0 @@
-$header-bg: #2b3643;
-$header-font: #c4c7d0;
-
-$blue: #3190e8;
-$bc: #e4e4e4;
-$fc:#fff;
-$common-left: 0.2rem;
-$common-right: 0.6rem;
-
-// $header-bg: #505771;
-// 鍏冪礌闀垮
-@mixin wh($width, $height) {
-  width: $width;
-  height: $height;
-}
-
-// 瀛椾綋棰滆壊,澶у皬, 姘村钩甯冨眬
-@mixin font($size, $color: #333, $text-align: left){
-  font-size: $size;
-  color: $color;
-  text-align: $text-align;
-}
-
-// flex甯冨眬
-@mixin flex($justify-content: center, $align-items: center) {
-  display: flex;
-  justify-content: $justify-content;
-  align-items: $align-items;
-}
-
-// 榛樿鎸夌収鐖跺厓绱犵殑鍓т腑
-@mixin positionCenter($position: absolute) {
-  position: $position;
-  top: 50%;
-  left: 50%;
-  transform: translate3d(-50%, -50%, 0);
-}
-
-// 榛樿鎸夌収鐖跺厓绱犵殑鍓т腑
-@mixin positionHCenter($position: absolute) {
-  position: $position;
-  top: 50%;
-  transform: translate3d(0, -50%, 0);
-}
-
-@mixin positionLeft($position: absolute, $left: $common-left) {
-  position: $position;
-  top: 50%;
-  left: $left;
-  transform: translate3d(0, -50%, 0);
-}
-
-@mixin positionRight($position: absolute, $right: $common-right) {
-  position: $position;
-  top: 50%;
-  right: $right;
-  transform: translate3d(0, -50%, 0);
-}
-
diff --git a/src/assets/css/viewstyle.scss b/src/assets/css/viewstyle.scss
index 09c8fb5..d1ca60c 100644
--- a/src/assets/css/viewstyle.scss
+++ b/src/assets/css/viewstyle.scss
@@ -153,41 +153,7 @@
           }
         }
       }
-      .normal-data-table, .normal-custom-table {
-        table {
-          .ant-table-tbody {
-            > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td {
-              background-color: $color1;
-            }
-            > tr.ant-table-row-selected:not(.background) td {
-              background-color: $color1;
-            }
-            > tr.ant-table-row-selected:not(.background):hover .ant-table-column-sort {
-              background-color: $color1;
-            }
-            > tr.mk-row-active:not(.background) td {
-              background-color: $color3;
-            }
-            > tr.ant-table-row-selected.mk-row-active:not(.background):hover .ant-table-column-sort {
-              background-color: $color3;
-            }
-          }
-        }
-      }
-      .top-search {
-        >.ant-row {
-          .ant-col.search-button {
-            .ant-btn:not(.ant-btn-primary):active, .ant-btn:not(.ant-btn-primary).active, .ant-btn:not(.ant-btn-primary):hover, .ant-btn:not(.ant-btn-primary):focus {
-              color: $color7;
-              border-color: $color7;
-            }
-            .ant-btn-primary {
-              background-color: $color6;
-              border-color: $color6;
-            }
-          }
-        }
-      }
+
       .ant-tabs-nav .ant-tabs-tab-active {
         color: $color6;
       }
@@ -200,21 +166,11 @@
       .ant-tabs-nav .ant-tabs-tab:hover {
         color: $color5;
       }
-      .ant-pagination {
-        .ant-pagination-item-active {
-          border-color: $color6;
-          a {
-            color: $color6;
-          }
-        }
-        .ant-pagination-item:hover a {
-          color: $color6;
-        }
-        .ant-pagination-prev:hover .ant-pagination-item-link {
-          color: $color6;
-        }
-        .ant-pagination-next:hover .ant-pagination-item-link {
-          color: $color6;
+    }
+    >.mk-breadview-wrap {
+      >.ant-breadcrumb {
+        .anticon-redo:hover, .anticon-home:hover {
+          color: $color5;
         }
       }
     }
@@ -248,6 +204,78 @@
       color: $color5;
     }
   }
+  // 鎼滅储鏍�
+  .top-search {
+    >.ant-row {
+      .ant-col.search-button {
+        .ant-btn:not(.ant-btn-primary):active, .ant-btn:not(.ant-btn-primary).active, .ant-btn:not(.ant-btn-primary):hover, .ant-btn:not(.ant-btn-primary):focus {
+          color: $color7;
+          border-color: $color7;
+        }
+        .ant-btn-primary {
+          background-color: $color6;
+          border-color: $color6;
+        }
+      }
+    }
+  }
+  // 琛ㄦ牸
+  .normal-data-table, .normal-custom-table {
+    table {
+      .ant-table-tbody {
+        > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) > td {
+          background-color: $color1;
+        }
+        > tr.ant-table-row-selected:not(.background) td {
+          background-color: $color1;
+        }
+        > tr.ant-table-row-selected:not(.background):hover .ant-table-column-sort {
+          background-color: $color1;
+        }
+        > tr.mk-row-active:not(.background) td {
+          background-color: $color3;
+        }
+        > tr.ant-table-row-selected.mk-row-active:not(.background):hover .ant-table-column-sort {
+          background-color: $color3;
+        }
+      }
+    }
+  }
+  // 寮圭獥鎸夐挳
+  .popview-modal {
+    .ant-modal-footer {
+      .ant-btn:not(.ant-btn-primary):active, .ant-btn:not(.ant-btn-primary).active, .ant-btn:not(.ant-btn-primary):hover, .ant-btn:not(.ant-btn-primary):focus {
+        color: $color7;
+        border-color: $color7;
+      }
+    }
+  }
+  // 琛ㄥ崟寮圭獥
+  .action-modal {
+    .ant-modal-footer {
+      .ant-btn:not(.ant-btn-primary):active, .ant-btn:not(.ant-btn-primary).active, .ant-btn:not(.ant-btn-primary):hover, .ant-btn:not(.ant-btn-primary):focus {
+        color: $color7;
+        border-color: $color7;
+      }
+      .ant-btn-primary {
+        background-color: $color6;
+        border-color: $color6;
+      }
+    }
+  }
+  // 鏄惁妗�
+  .ant-modal-confirm-confirm {
+    .ant-modal-confirm-btns {
+      .ant-btn:not(.ant-btn-primary):active, .ant-btn:not(.ant-btn-primary).active, .ant-btn:not(.ant-btn-primary):hover, .ant-btn:not(.ant-btn-primary):focus {
+        color: $color7;
+        border-color: $color7;
+      }
+      .ant-btn-primary {
+        background-color: $color6;
+        border-color: $color6;
+      }
+    }
+  }
 
   // 绯荤粺鏍峰紡淇敼
   // .ant-select-dropdown-menu-item:hover:not(.ant-select-dropdown-menu-item-disabled) {
diff --git a/src/assets/mobimg/carousel.png b/src/assets/mobimg/carousel.png
new file mode 100644
index 0000000..ec67284
--- /dev/null
+++ b/src/assets/mobimg/carousel.png
Binary files differ
diff --git a/src/assets/mobimg/carousel1.png b/src/assets/mobimg/carousel1.png
new file mode 100644
index 0000000..09e1a4b
--- /dev/null
+++ b/src/assets/mobimg/carousel1.png
Binary files differ
diff --git a/src/assets/mobimg/form.png b/src/assets/mobimg/form.png
new file mode 100644
index 0000000..fe05d33
--- /dev/null
+++ b/src/assets/mobimg/form.png
Binary files differ
diff --git a/src/assets/mobimg/mobile.png b/src/assets/mobimg/mobile.png
deleted file mode 100644
index c17bc0b..0000000
--- a/src/assets/mobimg/mobile.png
+++ /dev/null
Binary files differ
diff --git a/src/assets/mobimg/navbar-mob.png b/src/assets/mobimg/navbar-mob.png
new file mode 100644
index 0000000..d6be81a
--- /dev/null
+++ b/src/assets/mobimg/navbar-mob.png
Binary files differ
diff --git a/src/assets/mobimg/navbar.png b/src/assets/mobimg/navbar.png
new file mode 100644
index 0000000..1ea001e
--- /dev/null
+++ b/src/assets/mobimg/navbar.png
Binary files differ
diff --git a/src/components/Image/index.jsx b/src/components/Image/index.jsx
index 01bd23e..508c7be 100644
--- a/src/components/Image/index.jsx
+++ b/src/components/Image/index.jsx
@@ -6,6 +6,8 @@
     let Img = new Image()
     Img.src = this.props.url
     
+    if (!this.ImageWrapDom) return
+    
     if (Img.complete) {
       this.setSize(Img.width, Img.height)
     } else {
@@ -20,6 +22,7 @@
   }
 
   setSize = (width, height) => {
+    if (!this.ImageWrapDom) return
     const { clientWidth, clientHeight } = this.ImageWrapDom
 
     if (!clientWidth || !clientHeight || !width || !height) return
diff --git a/src/components/breadview/index.jsx b/src/components/breadview/index.jsx
new file mode 100644
index 0000000..8d25025
--- /dev/null
+++ b/src/components/breadview/index.jsx
@@ -0,0 +1,161 @@
+import React, {Component} from 'react'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { BackTop, Breadcrumb, Icon} from 'antd'
+import moment from 'moment'
+import 'moment/locale/zh-cn'
+
+import asyncComponent from '@/utils/asyncLoadComponent'
+import NotFount from '@/components/404'
+import mzhCN from '@/locales/zh-CN/main.js'
+import menUS from '@/locales/en-US/main.js'
+import MKEmitter from '@/utils/events.js'
+
+import './index.scss'
+
+const Home = asyncComponent(() => import('@/tabviews/home'))
+const CustomPage = asyncComponent(() => import('@/tabviews/custom'))
+const CommonTable = asyncComponent(() => import('@/tabviews/commontable'))
+const CalendarPage = asyncComponent(() => import('@/tabviews/calendar'))
+const TreePage = asyncComponent(() => import('@/tabviews/treepage'))
+const Iframe = asyncComponent(() => import('@/tabviews/iframe'))
+const RoleManage = asyncComponent(() => import('@/tabviews/rolemanage'))
+const FormTab = asyncComponent(() => import('@/tabviews/formtab'))
+
+let service = ''
+
+if (process.env.NODE_ENV === 'production') {
+  service = document.location.origin + '/' + window.GLOB.service + 'zh-CN/'
+} else {
+  service = window.GLOB.location + '/' + window.GLOB.service + 'zh-CN/'
+}
+
+class BreadView extends Component {
+  state = {
+    tabview: null, // 鏍囩
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
+    hasNavBar: window.GLOB.navBar !== 'linkage'
+  }
+
+  refreshTabview = () => {
+    const { tabview } = this.state
+    window.GLOB.CacheMap = new Map()
+
+    MKEmitter.emit('reloadMenuView', tabview.MenuID)
+  }
+
+  selectcomponent = (view) => {
+    // 鏍规嵁tab椤典腑鑿滃崟淇℃伅锛岄�夋嫨鎵�闇�鐨勭粍浠�
+    if (view.type === 'Home') {
+      return (<Home MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
+    } else if (view.type === 'CommonTable' || view.type === 'ManageTable') {
+      return (<CommonTable MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'CustomPage') {
+      return (<CustomPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'TreePage') {
+      return (<TreePage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'CalendarPage') {
+      return (<CalendarPage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'RolePermission') {
+      return (<RoleManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
+    } else if (view.type === 'FormTab') {
+      return (<FormTab MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID} param={view.param}/>)
+    } else if (view.type === 'iframe') {
+      return (<Iframe key={view.MenuID} MenuID={view.MenuID} MenuNo={view.MenuNo} title={view.MenuName} MenuName={view.MenuName} url={service + view.LinkUrl}/>)
+    } else {
+      return (<NotFount key={view.MenuID} />)
+    }
+  }
+
+  UNSAFE_componentWillMount () {
+    if (!sessionStorage.getItem('lang') || sessionStorage.getItem('lang') === 'zh-CN') {
+      moment.locale('zh-cn')
+    } else {
+      moment.locale('en')
+    }
+
+    let home = {
+      MenuID: 'home_page_id',
+      MenuName: this.state.dict['main.homepage'],
+      selected: true,
+      type: 'Home'
+    }
+    this.setState({tabview: home})
+  }
+
+  gotoHome = () => {
+    let home = {
+      MenuID: 'home_page_id',
+      MenuName: this.state.dict['main.homepage'],
+      selected: true,
+      type: 'Home'
+    }
+    this.setState({tabview: home})
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    if (nextProps.tabviews && !is(fromJS(this.state.tabviews), fromJS(nextProps.tabviews))) {
+      // 淇濆瓨淇敼鏍囩闆�
+      this.setState({
+        tabview: nextProps.tabviews[nextProps.tabviews.length - 1]
+      })
+
+      let node = document.getElementById('root').parentNode.parentNode
+      if (node) {
+        node.scrollTop = 0
+      }
+    }
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  render () {
+    const { tabview, hasNavBar } = this.state
+
+    return (
+      <section id="mk-breadview-wrap" className="mk-breadview-wrap">
+        {hasNavBar && tabview ? <Breadcrumb separator="">
+          <Breadcrumb.Item>
+            <Icon type="home" onClick={this.gotoHome} />
+          </Breadcrumb.Item>
+          {tabview.ParentNames && tabview.ParentNames[0] ?
+            <Breadcrumb.Item>{tabview.ParentNames[0]}</Breadcrumb.Item> : null}
+          {tabview.ParentNames && tabview.ParentNames[0] ?
+              <Breadcrumb.Separator children={<Icon type="right" />} /> : null}
+          {tabview.ParentNames && tabview.ParentNames[1] ?
+            <Breadcrumb.Item>{tabview.ParentNames[1]}</Breadcrumb.Item> : null}
+          {tabview.ParentNames && tabview.ParentNames[1] ?
+              <Breadcrumb.Separator children={<Icon type="right" />} /> : null}
+          <Breadcrumb.Item><Icon type="redo" onClick={this.refreshTabview}/>{tabview.MenuName}</Breadcrumb.Item>
+        </Breadcrumb> : null}
+        {tabview ? this.selectcomponent(tabview) : null}
+        <BackTop>
+          <div className="ant-back-top">
+            <div className="ant-back-top-content">
+              <div className="ant-back-top-icon"></div>
+            </div>
+          </div>
+        </BackTop>
+      </section>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    tabviews: state.tabviews
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(BreadView)
\ No newline at end of file
diff --git a/src/components/breadview/index.scss b/src/components/breadview/index.scss
new file mode 100644
index 0000000..10194f5
--- /dev/null
+++ b/src/components/breadview/index.scss
@@ -0,0 +1,73 @@
+.mk-breadview-wrap {
+  min-height: 100%;
+  padding-top: 48px;
+  width: 100%;
+  box-sizing: content-box;
+  >.ant-breadcrumb {
+    padding: 10px;
+    .anticon-home {
+      cursor: pointer;
+      margin-right: 10px;
+    }
+    .anticon-redo {
+      cursor: pointer;
+      margin-right: 5px;
+    }
+    .ant-breadcrumb-link + .ant-breadcrumb-separator {
+      display: none;
+    }
+  }
+  >.commontable, >.calendar-page {
+    padding-left: 15px;
+    padding-right: 15px;
+  }
+  >.commontable, >.calendar-page {
+    > .top-search {
+      padding-left: 0;
+      padding-right: 0;
+      margin: 0 24px;
+    }
+  }
+  iframe {
+    width: 100%;
+    height: calc(100vh - 115px);
+    overflow-y: scroll;
+    border: 0;
+    margin-top: 16px;
+  }
+  .main-copy {
+    position: fixed;
+    z-index: 20;
+    bottom: 65px;
+    right: 30px;
+    width: 40px;
+    height: 40px;
+    i {
+      font-size: 18px;
+    }
+  }
+  .main-copy.ifr-copy {
+    bottom: 65px;
+    right: 40px;
+    width: 30px;
+    height: 32px;
+    border: 2px solid #687991;
+    opacity: 0.6;
+    i {
+      font-size: 14px;
+      color: #687991;
+    }
+  }
+  .main-copy.ifr-copy:hover {
+    opacity: 1;
+  }
+  .ant-back-top {
+    bottom: 20px;
+    right: 30px;
+  }
+}
+
+.ant-message {
+  top: 50px;
+  z-index: 1080;
+}
\ No newline at end of file
diff --git a/src/components/header/index.jsx b/src/components/header/index.jsx
index 5e85185..aa7b4d2 100644
--- a/src/components/header/index.jsx
+++ b/src/components/header/index.jsx
@@ -4,33 +4,28 @@
 import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import moment from 'moment'
-import { Dropdown, Menu, Icon, Modal, Form, notification, Switch, Button, Input, Badge } from 'antd'
+import { Dropdown, Menu, Icon, Modal, Form, notification, Switch, Input, Badge } from 'antd'
 
-import asyncComponent from '@/utils/asyncComponent'
 import {
   toggleCollapse,
   modifyMenuTree,
   modifyMainMenu,
   modifyTabview,
-  resetState,
-  resetEditState,
-  resetEditLevel,
   initActionPermission,
   initMenuPermission,
   logout
 } from '@/store/action'
 import Api from '@/api'
+import MKEmitter from '@/utils/events.js'
 import options from '@/store/options.js'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import Utils from '@/utils/utils.js'
 import avatar from '@/assets/img/avatar.jpg'
-import MainLogo from '@/assets/img/main-logo.png'
 import Resetpwd from './resetpwd'
 import LoginForm from './loginform'
 import './index.scss'
 
-const EditMenu = asyncComponent(() => import('@/templates/menuconfig/editfirstmenu'))
 const { confirm } = Modal
 const { Search } = Input
 
@@ -41,7 +36,7 @@
   state = {
     menulist: null, // 涓�绾ц彍鍗�
     visible: false, // 淇敼瀵嗙爜妯℃�佹
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     confirmLoading: false,
     userName: sessionStorage.getItem('User_Name'),
     logourl: window.GLOB.mainlogo,
@@ -53,15 +48,14 @@
     thdMenuList: [],
     oriVersion: '',
     newVersion: '',
-    debug: sessionStorage.getItem('debug') === 'true'
+    debug: sessionStorage.getItem('debug') === 'true',
+    navBar: ['linkage_navigation', 'linkage'].includes(window.GLOB.navBar) ? 'topmenu' : ''
   }
 
   handleCollapse = () => {
     // 灞曞紑銆佹敹璧峰乏渚ц彍鍗曟爮
-    if (!this.props.editState) {
-      this.props.toggleCollapse(!this.props.collapse)
-      localStorage.setItem('collapse', !this.props.collapse)
-    }
+    this.props.toggleCollapse(!this.props.collapse)
+    localStorage.setItem('collapse', !this.props.collapse)
   }
 
   changePassword = () => {
@@ -133,50 +127,10 @@
 
   changeMenu (value) {
     // 涓昏彍鍗曞垏鎹�
-    if (this.props.editState && this.props.editLevel) {
-      // 缂栬緫鐘舵�佷笅锛屼笉鍙垏鎹㈣彍鍗�
-      return
-    }
-
-    if (value.PageParam.OpenType === 'menu') {
+    if (value.OpenType === 'outpage' && value.linkUrl) {
+      window.open(value.linkUrl)
+    } else if (value.OpenType === 'menu') {
       this.props.modifyMainMenu(value)
-    } else if (value.PageParam.OpenType === 'outpage') {
-      window.open(value.PageParam.linkUrl)
-    }
-  }
-
-  async loadmenu () {
-    // 鑾峰彇涓昏彍鍗�
-    let _param = {func: 's_get_pc_menus', systemType: options.sysType}
-    if (sessionStorage.getItem('isEditState') === 'true') { // 缂栬緫鐘舵�佹椂锛屽鍔犲弬鏁癲ebug
-      _param.debug = 'Y'
-    }
-    if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
-      _param.linkurl = window.GLOB.linkurl
-    }
-    _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
-
-    let result = await Api.getSystemConfig(_param)
-
-    // 鐧诲綍瓒呮椂
-    if (!result) return
-
-    if (result.status) {
-      const { menulist } = this.getMenulist(result)
-
-      this.setState({
-        menulist,
-        systems: []
-      })
-
-      this.props.modifyMenuTree(menulist)
-      this.props.modifyMainMenu(menulist[0] || null)
-    } else {
-      notification.error({
-        top: 92,
-        message: result.message,
-        duration: 10
-      })
     }
   }
 
@@ -214,9 +168,6 @@
     // 鑾峰彇涓昏彍鍗曞弬鏁�
     let menudefer = new Promise(resolve => {
       let _param = {func: 's_get_pc_menus', systemType: options.sysType}
-      if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
-        _param.linkurl = window.GLOB.linkurl
-      }
       _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
       
       Api.getSystemConfig(_param).then(result => {
@@ -274,14 +225,19 @@
         let tabs = fromJS(this.props.tabviews).toJS()
         let menu = fromJS(response[1]).toJS()
 
-        tabs = tabs.map(tab => {
-          tab.selected = false
-          return tab
-        })
+        if (this.state.navBar === 'topmenu') {
+          menu.selected = true
+          this.props.modifyTabview([menu])
+        } else {
+          tabs = tabs.map(tab => {
+            tab.selected = false
+            return tab
+          })
 
-        menu.selected = true
-        tabs.push(menu)
-        this.props.modifyTabview(tabs)
+          menu.selected = true
+          tabs.push(menu)
+          this.props.modifyTabview(tabs)
+        }
       }
     })
   }
@@ -294,14 +250,18 @@
       let fstItem = {
         MenuID: fst.MenuID,
         MenuName: fst.MenuName,
-        PageParam: {OpenType: 'menu', linkUrl: ''},
+        OpenType: 'menu',
         children: []
       }
       if (fst.PageParam) {
         try {
           fstItem.PageParam = JSON.parse(fst.PageParam)
         } catch (e) {
-          fstItem.PageParam = {OpenType: 'menu', linkUrl: ''}
+          fstItem.PageParam = null
+        }
+        if (fstItem.PageParam && fstItem.PageParam.OpenType === 'outpage' && fstItem.PageParam.linkUrl) {
+          fstItem.OpenType = 'outpage'
+          fstItem.linkUrl = fstItem.PageParam.linkUrl
         }
       }
 
@@ -334,6 +294,7 @@
             debug: sessionStorage.getItem('debug'),
             role_id: sessionStorage.getItem('role_id'),
             mainlogo: window.GLOB.mainlogo,
+            navBar: window.GLOB.navBar || '',
             mstyle: window.GLOB.style
           }
 
@@ -344,6 +305,7 @@
                 ParentId: snd.MenuID,
                 MenuID: trd.MenuID,
                 MenuName: trd.MenuName,
+                ParentNames: [fst.MenuName, snd.MenuName],
                 MenuNo: trd.MenuNo,
                 EasyCode: trd.EasyCode,
                 type: 'CommonTable',            // 榛樿鍊间负甯哥敤琛�
@@ -352,8 +314,7 @@
   
               if (trd.LinkUrl && iframes.includes(trd.LinkUrl.split('?')[0])) {
                 trdItem.type = 'iframe'
-                trdItem.LinkUrl = trd.LinkUrl
-                trdItem.forbidden = true
+                trdItem.LinkUrl = trd.LinkUrl.replace('&amp;', '&')
               } else {
                 try {
                   trdItem.PageParam = trd.PageParam ? JSON.parse(trd.PageParam) : {OpenType: 'newtab'}
@@ -364,9 +325,6 @@
                 trdItem.type = trdItem.PageParam.Template || trdItem.type
                 trdItem.OpenType = trdItem.PageParam.OpenType || trdItem.OpenType
 
-                if (trdItem.type === 'CustomPage' && this.props.memberLevel < 20) { // 浼氬憳绛夌骇澶т簬绛変簬20鏃讹紝鏈夌紪杈戞潈闄�
-                  trdItem.forbidden = true
-                }
                 if (trdItem.type === 'NewPage') {
                   trdItem.src = trdItem.PageParam.url || ''
                   
@@ -409,16 +367,7 @@
     return { menulist, thdMenuList }
   }
 
-  reload = () => {
-    this.loadmenu()
-  }
-
-  changeEditState = (state) => {
-    if (!state) { // 閫�鍑虹紪杈戯紝椤甸潰鍒锋柊
-      window.location.reload()
-      return
-    }
-
+  changeEditState = () => {
     // 淇敼缂栬緫鐘舵��
     let UserID = sessionStorage.getItem('CloudUserID')
     let LoginUID = sessionStorage.getItem('CloudLoginUID')
@@ -428,64 +377,15 @@
         loginVisible: true
       })
     } else {
-      sessionStorage.setItem('isEditState', 'true')
       sessionStorage.setItem('role_id', sessionStorage.getItem('cloudRole_id'))
       sessionStorage.setItem('dataM', sessionStorage.getItem('cloudDataM'))
+      sessionStorage.setItem('isEditState', 'true')
 
-      if (window.GLOB.systemType === 'production') {
-        this.props.resetEditLevel('HS')
-        this.props.modifyMainMenu({
-          MenuID: 'systemManageView'
-        })
-
-        this.setState({
-          userName: sessionStorage.getItem('CloudUserName'),
-          avatar: Utils.getrealurl(sessionStorage.getItem('CloudAvatar'))
-        })
-        this.props.resetEditState(state)
-
-        return
-      }
-
-      this.setState({
-        menulist: null,
-        userName: sessionStorage.getItem('CloudUserName'),
-        avatar: Utils.getrealurl(sessionStorage.getItem('CloudAvatar'))
-      })
-      this.loadmenu()
+      this.props.modifyMenuTree([])
       this.props.modifyMainMenu(null)
-      this.props.resetEditState(state)
-    }
+      this.props.modifyTabview([])
 
-    if (window.GLOB.systemType !== 'production') {
-      Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
-        if (res.status) {
-          let _permFuncField = []
-          let _sysRoles = []
-
-          if (res.Roles && res.Roles.length > 0) {
-            _sysRoles = res.Roles.map(role => {
-              return {
-                uuid: Utils.getuuid(),
-                value: role.RoleID,
-                text: role.RoleName
-              }
-            })
-          }
-
-          if (res.sModular && res.sModular.length > 0) {
-            res.sModular.forEach(field => {
-              if (field.ModularNo) {
-                _permFuncField.push(field.ModularNo)
-              }
-            })
-            _permFuncField = _permFuncField.sort()
-          }
-
-          sessionStorage.setItem('sysRoles', JSON.stringify(_sysRoles))
-          sessionStorage.setItem('permFuncField', JSON.stringify(_permFuncField))
-        }
-      })
+      this.props.history.replace('/design')
     }
   }
 
@@ -508,33 +408,11 @@
           sessionStorage.setItem('dataM', res.dataM ? 'true' : '')
           sessionStorage.setItem('isEditState', 'true')
 
-          if (window.GLOB.systemType === 'production') {
-            this.props.resetEditLevel('HS')
-            this.props.modifyMainMenu({
-              MenuID: 'systemManageView'
-            })
-    
-            this.setState({
-              loginVisible: false,
-              loginLoading: false,
-              userName: res.UserName,
-              avatar: res.icon
-            })
-            this.props.resetEditState(true)
-    
-            return
-          }
-
-          this.setState({
-            menulist: null,
-            loginVisible: false,
-            loginLoading: false,
-            userName: res.UserName,
-            avatar: res.icon
-          })
-          this.loadmenu()
+          this.props.modifyMenuTree([])
           this.props.modifyMainMenu(null)
-          this.props.resetEditState(true)
+          this.props.modifyTabview([])
+
+          this.props.history.replace('/design')
         } else {
           this.setState({
             loginLoading: false
@@ -547,43 +425,6 @@
         }
       })
     })
-  }
-
-  enterEdit = () => {
-    // 杩涘叆缂栬緫鐘舵��
-    this.props.resetEditLevel('level1')
-  }
-
-  enterEditManage = () => {
-    const { editLevel } = this.props
-
-    if (editLevel === 'HS')  return
-
-    this.props.resetEditLevel('HS')
-    this.props.modifyMainMenu({
-      MenuID: 'systemManageView'
-    })
-  }
-
-  /**
-   * @description 閫�鍑虹鐞嗙晫闈㈣彍鍗�
-   */
-  exitManage = () => {
-    const { menulist } = this.state
-
-    if (window.GLOB.systemType === 'production') { // 姝e紡绯荤粺鐗堟湰鍗囩骇鍚庯紝椤甸潰鍒锋柊
-      window.location.reload()
-      return
-    }
-
-    this.props.modifyMainMenu(menulist[0] || null)
-    this.props.resetEditLevel(false)
-    this.props.modifyTabview([])
-  }
-  
-  exitEdit = () => {
-    // 閫�鍑虹紪杈戠姸鎬�
-    this.props.resetEditLevel(false)
   }
 
   changeSystem = (system) => {
@@ -626,26 +467,6 @@
       }
     })
   }
-
-  selectMenu = (item) => {
-    let tabs = fromJS(this.props.tabviews).toJS()
-    let menu = fromJS(item).toJS()
-    menu.selected = true
-
-    tabs = tabs.filter(tab => {
-      tab.selected = false
-      return tab.MenuID !== menu.MenuID
-    })
-
-    if (this.props.tabviews.length !== tabs.length) {
-      this.props.modifyTabview(fromJS(tabs).toJS())
-    }
-
-    this.setState({}, () => {
-      tabs.push(menu)
-      this.props.modifyTabview(tabs)
-    })
-  }
   
   UNSAFE_componentWillMount () {
     // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
@@ -663,21 +484,26 @@
   componentDidMount () {
     // 鑾峰彇绯荤粺鐨勭増鏈俊鎭紝寤舵椂鏌ヨ
     setTimeout(() => {
-      new Promise((resolve, reject) => {
-        Api.getAppVersion(resolve, reject)
-      }).then(res => {
+      Api.getAppVersion().then(res => {
         this.setState({
           oriVersion: res.oldVersion,
           newVersion: res.newVersion
         })
-      }, () => {
-        console.warn('websql 鍒濆鍖栭敊璇紒')
-      })
+      }, () => {})
     }, 1000)
   }
 
   shouldComponentUpdate (nextProps, nextState) {
     return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
   }
 
   verup = () => {
@@ -689,23 +515,23 @@
       content: `鏈�鏂扮増鏈�${newVersion}锛屽綋鍓嶇増鏈�${oriVersion}`,
       onOk() {
         return new Promise(resolve => {
-          Api.updateAppVersion(newVersion).then(res => {
-            if (res.status) {
-              notification.success({
-                top: 92,
-                message: '鍗囩骇鎴愬姛锛�',
-                duration: 2
-              })
-              _this.setState({oriVersion: newVersion})
-            } else {
-              notification.warning({
-                top: 92,
-                message: '鍗囩骇澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯锛�',
-                duration: 2
-              })
-            }
+          if (!window.GLOB.WebSql) {
+            notification.warning({
+              top: 92,
+              message: '鍗囩骇澶辫触锛岃鍒锋柊椤甸潰閲嶈瘯锛�',
+              duration: 2
+            })
             resolve()
-          })
+          } else {
+            Api.updateAppVersion(newVersion)
+            notification.success({
+              top: 92,
+              message: '鍗囩骇鎴愬姛锛�',
+              duration: 2
+            })
+            _this.setState({oriVersion: newVersion})
+            resolve()
+          }
         })
       },
       onCancel() {}
@@ -722,17 +548,57 @@
     }
   }
 
+  changeVerMenu(menu, type) {
+    if (type === 'first') {
+      if (menu.OpenType === 'outpage' && menu.linkUrl) {
+        window.open(menu.linkUrl)
+      } else if (menu.OpenType === 'menu') {
+
+      }
+    } else {
+      if (menu.OpenType === 'newpage' || menu.OpenType === 'NewPage') { // NewPage涓烘墦寮�澶栭儴椤甸潰鍦板潃锛宯ewpage涓烘墦寮�绯荤粺鑿滃崟
+        window.open(menu.src)
+      } else if (menu.OpenType === 'blank') {
+        menu.selected = true
+        this.props.modifyTabview([menu])
+      } else if (this.state.navBar === 'topmenu') {
+        menu.selected = true
+        this.props.modifyTabview([menu])
+      } else {
+        let tabs = fromJS(this.props.tabviews).toJS()
+        tabs = tabs.filter(tab => {
+          tab.selected = false
+          return tab.MenuID !== menu.MenuID
+        })
+  
+        if (this.props.tabviews.length > tabs.length) {
+          this.props.modifyTabview(fromJS(tabs).toJS())
+        }
+  
+        this.setState({}, () => {
+          menu.selected = true
+          tabs.push(menu)
+          this.props.modifyTabview(tabs)
+        })
+      }
+  
+      if (window.GLOB.systemType === 'production') {
+        MKEmitter.emit('queryTrigger', {menuId: menu.MenuID, name: '鑿滃崟'})
+      }
+    }
+  }
+
   render () {
     const { mainMenu, collapse } = this.props
-    const { thdMenuList, searchkey, oriVersion, newVersion, debug, menulist } = this.state
+    const { thdMenuList, searchkey, oriVersion, newVersion, debug, menulist, navBar } = this.state
 
     const menu = (
       <Menu className="header-dropdown">
         {debug && <Menu.Item key="switch">
           {this.state.dict['main.edit']}
-          <Switch size="small" style={{marginLeft: '7px'}} disabled={!!this.props.editLevel} checked={this.props.editState} onChange={this.changeEditState} />
+          <Switch size="small" style={{marginLeft: '7px'}} checked={false} onChange={this.changeEditState} />
         </Menu.Item>}
-        {!this.props.editState ? <Menu.Item key="password" onClick={this.changePassword}>{this.state.dict['main.password']}</Menu.Item> : null}
+        <Menu.Item key="password" onClick={this.changePassword}>{this.state.dict['main.password']}</Menu.Item>
         {this.state.systems.length ? <Menu.SubMenu style={{minWidth: '110px'}} title="鍒囨崲绯荤粺">
           {this.state.systems.map((system, index) => (
             <Menu.Item style={{minWidth: '100px', lineHeight: '30px'}} key={'sub' + index} onClick={() => {this.changeSystem(system)}}> {system.AppName} </Menu.Item>
@@ -748,13 +614,13 @@
 
     return (
       <header className="header-container ant-menu-dark" id="main-header-container">
-        <div className={'header-logo ' + (collapse ? 'collapse' : '')}><img src={!this.props.editState ? this.state.logourl : MainLogo} alt=""/></div>
-        <div className={'header-collapse ' + (collapse ? 'collapse' : '')}>
-          {menulist && menulist.length ? <Icon type={collapse ? 'menu-unfold' : 'menu-fold'} onClick={this.handleCollapse}/> : null}
+        <div className={'header-logo ' + (collapse && navBar !== 'topmenu' ? 'collapse' : '')}><img src={this.state.logourl} alt=""/></div>
+        <div className={'header-collapse ' + (collapse && navBar !== 'topmenu' ? 'collapse' : '')}>
+          {navBar !== 'topmenu' ? <Icon type={collapse ? 'menu-unfold' : 'menu-fold'} onClick={this.handleCollapse}/> : null}
         </div>
         {/* 姝e父鑿滃崟 */}
-        {this.props.editLevel !== 'level1' && menulist ?
-          <ul className={'header-menu ' + this.props.editLevel}>{
+        {navBar !== 'topmenu' && menulist ?
+          <ul className="header-menu">{
             menulist.map(item => {
               return (
                 <li key={item.MenuID} onClick={() => {this.changeMenu(item)}} className={mainMenu && mainMenu.MenuID === item.MenuID ? 'active' : ''}>
@@ -762,27 +628,52 @@
                 </li>
               )
             })}
-            {this.props.editState && (!this.props.editLevel || this.props.editLevel === 'HS') ?
-              <li key="HS" onClick={this.enterEditManage} className={this.props.editLevel === 'HS' ? 'active' : ''}>
-                <span>HS</span>
-              </li> : null
-            }
           </ul> : null
         }
-        {this.props.editLevel === 'HS' ? <Button className="level4-close" type="primary" onClick={this.exitManage}>閫�鍑�</Button> : null}
-        {/* 杩涘叆缂栬緫鎸夐挳 */}
-        {this.props.editState && !this.props.editLevel ? <Icon onClick={this.enterEdit} className="edit-check" type="edit" /> : null}
-        {/* {this.props.editState && !this.props.editLevel && options.sysType === 'local' && window.GLOB.systemType !== 'production' ?
-          <a href="#/mobmanage" target="_blank" className="mobile" type="edit"> 搴旂敤绠$悊 <Icon type="arrow-right" /></a> : null
-        } */}
-        {/* window.btoa(window.encodeURIComponent(JSON.stringify({ MenuType: 'home', MenuId: 'home_page_id', MenuName: '棣栭〉' }))) */}
-        {this.props.editState && !this.props.editLevel && window.GLOB.systemType !== 'production' && this.props.memberLevel >= 20 ?
-          <a className="home-edit" href={`#/menudesign/JTdCJTIyTWVudVR5cGUlMjIlM0ElMjJob21lJTIyJTJDJTIyTWVudUlkJTIyJTNBJTIyaG9tZV9wYWdlX2lkJTIyJTJDJTIyTWVudU5hbWUlMjIlM0ElMjIlRTklQTYlOTYlRTklQTElQjUlMjIlN0Q=`} target="_blank" rel="noopener noreferrer">
-            棣栭〉 <Icon type="arrow-right" />
-          </a> : null
+        {/* 姝e父鑿滃崟 */}
+        {navBar === 'topmenu' && menulist ?
+          <ul className="header-menu vertical-menu">{
+            menulist.map(item => {
+              if (item.children && item.children.length > 0) {
+                return (
+                  <Dropdown key={item.MenuID} overlayClassName="vertical-dropdown-menu" overlay={
+                    <Menu mode="vertical">
+                      {item.children.map(cell => {
+                        if (!cell.children || cell.children.length === 0) {
+                          return (
+                            <Menu.Item key={cell.MenuID}>
+                              {cell.MenuName}
+                            </Menu.Item>
+                          )
+                        } else {
+                          return (
+                            <Menu.SubMenu popupClassName="vertical-dropdown-submenu" key={cell.MenuID} title={cell.MenuName}>
+                              {cell.children.map(m => (
+                                <Menu.Item key={m.MenuID} onClick={() => {this.changeVerMenu(m)}}>
+                                  {m.MenuName}
+                                </Menu.Item>
+                              ))}
+                            </Menu.SubMenu>
+                          )
+                        }
+                      })}
+                    </Menu>
+                  }>
+                    <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
         }
-        {/* 缂栬緫鑿滃崟 */}
-        {this.props.editLevel === 'level1' ? <EditMenu menulist={this.state.menulist} reload={this.reload} exitEdit={this.exitEdit}/> : null}
         {/* 澶村儚銆佺敤鎴峰悕 */}
         <Dropdown className="header-setting" overlay={menu}>
           <div>
@@ -793,7 +684,7 @@
           </div>
         </Dropdown>
         {/* 鑿滃崟鎼滅储 */}
-        {!this.props.editState && thdMenuList.length > 0 ?
+        {thdMenuList.length > 0 ?
           <Dropdown overlayClassName="menu-select-dropdown" getPopupContainer={() => document.getElementById('main-header-container')} overlay={
             <div>
               <Search
@@ -814,12 +705,12 @@
                         option.MenuNo.toLowerCase().indexOf(searchkey.toLowerCase()) >= 0 ||
                         option.EasyCode.toLowerCase().indexOf(searchkey.toLowerCase()) >= 0
                       ) {
-                        return <Menu.Item key={option.MenuID} onClick={() => this.selectMenu(option)}>{option.MenuName}</Menu.Item>
+                        return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.MenuName}</Menu.Item>
                       } else {
                         return null
                       }
                     }
-                    return <Menu.Item key={option.MenuID} onClick={() => this.selectMenu(option)}>{option.MenuName}</Menu.Item>
+                    return <Menu.Item key={option.MenuID} onClick={() => this.changeVerMenu(option)}>{option.MenuName}</Menu.Item>
                   })}
                 </Menu>
               </div>
@@ -862,10 +753,7 @@
     collapse: state.collapse,
     menuTree: state.menuTree,
     mainMenu: state.mainMenu,
-    editState: state.editState,
-    editLevel: state.editLevel,
     permAction: state.permAction,
-    memberLevel: state.memberLevel
   }
 }
 
@@ -875,11 +763,8 @@
     modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews)),
     modifyMenuTree: (menuTree) => dispatch(modifyMenuTree(menuTree)),
     modifyMainMenu: (mainMenu) => dispatch(modifyMainMenu(mainMenu)),
-    resetEditState: (state) => dispatch(resetEditState(state)),
-    resetEditLevel: (level) => dispatch(resetEditLevel(level)),
     initActionPermission: (permAction) => dispatch(initActionPermission(permAction)),
     initMenuPermission: (permMenus) => dispatch(initMenuPermission(permMenus)),
-    resetState: () => dispatch(resetState()),
     logout: () => dispatch(logout())
   }
 }
diff --git a/src/components/header/index.scss b/src/components/header/index.scss
index 28137d8..77b3ade 100644
--- a/src/components/header/index.scss
+++ b/src/components/header/index.scss
@@ -1,8 +1,6 @@
-@import '../../assets/css/global.scss';
-
 .header-container {
   position: fixed;
-  z-index: 1060;
+  z-index: 20;
   left: 0;
   top: 0;
   font-weight: bold!important;
@@ -77,22 +75,10 @@
       }
     }
   }
-
-  .header-menu.level4 {
-    li {
-      cursor: default;
-      &:hover {
-        span {
-          cursor: default;
-          color: rgba(255, 255, 255, 0.65);
-          border-bottom: none;
-        }
-      }
-      &.active {
-        span {
-          color: #ffffff;
-          border-bottom: 4px solid #1890ff;
-        }
+  .header-menu.vertical-menu {
+    >li {
+      >span {
+        border-color: transparent!important;
       }
     }
   }
@@ -120,25 +106,13 @@
       }
     }
   }
-  .edit-check {
-    font-size: 18px;
-    margin-top: 14px;
-    margin-left: 10px;
-    cursor: pointer;
-  }
+
   .search-menu {
     float: right;
     font-size: 18px;
     margin-top: 17px;
     margin-right: 20px;
     cursor: pointer;
-  }
-  .level4-close {
-    position: relative;
-    top: 13px;
-    left: 20px;
-    height: 26px;
-    padding: 0 10px;
   }
   .menu-select-dropdown {
     top: 48px!important;
@@ -183,18 +157,6 @@
       background: rgba(0, 0, 0, 0);
     }
   }
-  .mobile {
-    position: absolute;
-    top: 135px;
-    right: 50px;
-    color: #1890ff;
-  }
-  .home-edit {
-    position: absolute;
-    top: 100px;
-    right: 50px;
-    color: #1890ff;
-  }
 }
 .header-dropdown {
   >li {
@@ -207,3 +169,12 @@
     }
   }
 }
+
+.vertical-dropdown-menu, .vertical-dropdown-submenu {
+  ul.ant-dropdown-menu {
+    min-width: 125px;
+  }
+  .ant-dropdown-menu-submenu-title {
+    padding-right: 30px;
+  }
+}
diff --git a/src/components/header/loginform.jsx b/src/components/header/loginform.jsx
index 7010577..3ed68e7 100644
--- a/src/components/header/loginform.jsx
+++ b/src/components/header/loginform.jsx
@@ -11,7 +11,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
   }
 
   handleConfirm = () => {
diff --git a/src/components/querylog/index.jsx b/src/components/querylog/index.jsx
index 13e2293..16337a5 100644
--- a/src/components/querylog/index.jsx
+++ b/src/components/querylog/index.jsx
@@ -5,6 +5,9 @@
 import Utils from '@/utils/utils.js'
 import MKEmitter from '@/utils/events.js'
 
+/**
+ * @description 鎿嶄綔璁板綍锛屾瘡闅斿叚鍒嗛挓鏇存柊涓�娆�
+ */
 class QueryLog extends Component {
   state = {
     logs: []
diff --git a/src/components/sidemenu/index.jsx b/src/components/sidemenu/index.jsx
index fe1c20f..4bb0b8b 100644
--- a/src/components/sidemenu/index.jsx
+++ b/src/components/sidemenu/index.jsx
@@ -1,22 +1,15 @@
 import React, {Component} from 'react'
-import { withRouter } from 'react-router-dom'
 import PropTypes from 'prop-types'
 import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
-import { Menu, Icon, notification } from 'antd'
+import { Menu, Icon } from 'antd'
 
-import asyncComponent from '@/utils/asyncComponent'
-import { modifyTabview, resetEditLevel, modifyMenuTree, modifyMainMenu } from '@/store/action'
-import { SySMenuList } from './config'
-import options from '@/store/options.js'
+import { modifyTabview } from '@/store/action'
 import MKEmitter from '@/utils/events.js'
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
-import Api from '@/api'
 import './index.scss'
 
-const EditSecMenu = asyncComponent(() => import('@/templates/menuconfig/editsecmenu'))
-const EditThdMenu = asyncComponent(() => import('@/templates/menuconfig/editthdmenu'))
 const { SubMenu } = Menu
 
 class Sidemenu extends Component {
@@ -25,11 +18,9 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     subMenulist: [],         // 浜岀骇鑿滃崟
-    editMenu: null,          // 缂栬緫涓夌骇鑿滃崟鏃惰缃�
     rootSubmenuKeys: null,
-    createThirdMenu: false,
     openKeys: null,
     preview: null
   }
@@ -40,7 +31,6 @@
         subMenulist: [],
         rootSubmenuKeys: [],
         openKeys: [],
-        editMenu: null
       })
       return
     }
@@ -56,39 +46,11 @@
       subMenulist: menu.children,
       rootSubmenuKeys: menu.children.map(item => item.MenuID),
       openKeys: this.props.collapse ? [] : [openKey],
-      editMenu: this.props.editLevel === 'level3' ? menu.children.filter(_menu => _menu.MenuID === this.state.editMenu.MenuID)[0] : null
-    })
-  }
-
-  enterManageView = () => {
-    let menulist = SySMenuList
-
-    if (window.GLOB.systemType === 'production') {
-      menulist.forEach(menu => {
-        menu.children = menu.children.filter(item => item.systems && item.systems.includes(window.GLOB.systemType))
-      })
-
-      menulist = menulist.filter(menu => menu.children.length > 0)
-    } else {
-      menulist.forEach(menu => {
-        menu.children = menu.children.filter(item => !item.systems || item.systems.includes(options.sysType))
-      })
-
-      menulist = menulist.filter(menu => menu.children.length > 0)
-    }
-
-    this.setState({
-      subMenulist: menulist,
-      rootSubmenuKeys: menulist.map(item => item.MenuID),
-      openKeys: this.props.collapse ? [] : [menulist[0].MenuID]
     })
   }
 
   changemenu(e, menu) {
     e.preventDefault()
-    if (this.props.editState && this.props.editLevel !== 'HS') {
-      return
-    }
 
     if (menu.OpenType === 'newpage' || menu.OpenType === 'NewPage') {
       window.open(menu.src)
@@ -102,7 +64,7 @@
         return tab.MenuID !== menu.MenuID
       })
 
-      if (this.props.tabviews.length !== tabs.length) {
+      if (this.props.tabviews.length > tabs.length) {
         this.props.modifyTabview(fromJS(tabs).toJS())
       }
 
@@ -119,9 +81,7 @@
   }
 
   UNSAFE_componentWillReceiveProps (nextProps) {
-    if (!is(fromJS(this.props.mainMenu), fromJS(nextProps.mainMenu)) && nextProps.mainMenu && nextProps.mainMenu.MenuID === 'systemManageView') {
-      this.enterManageView()
-    } else if (!is(fromJS(this.props.mainMenu), fromJS(nextProps.mainMenu))) {
+    if (!is(fromJS(this.props.mainMenu), fromJS(nextProps.mainMenu))) {
       // 涓昏彍鍗曞垏鎹紝璇锋眰2銆�3绾ц彍鍗曟暟鎹�
       this.loadsubmenu(nextProps.mainMenu)
     } else if (nextProps.collapse && this.props.collapse !== nextProps.collapse) {
@@ -147,161 +107,25 @@
     }
   }
 
-  enterSubEdit = (e) => {
-    // 缂栬緫浜岀骇鑿滃崟
-    e.stopPropagation()
-    this.props.resetEditLevel('level2')
-  }
-
-  enterThrEdit = (e, menu) => {
-    // 缂栬緫涓夌骇鑿滃崟
-    e.stopPropagation()
-    this.props.resetEditLevel('level3')
-    this.setState({editMenu: menu})
-  }
-
-  reload = () => {
-    const { mainMenu } = this.props
-    let _param = {func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}
-    if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
-      _param.linkurl = window.GLOB.linkurl
-    }
-    _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
-
-    Api.getSystemConfig(_param).then(result => {
-      // 鐧诲綍瓒呮椂
-      if (!result) return
-  
-      if (result.status) {
-        let res = this.getMenulist(result)
-        let _mainMenu = res.menulist.filter(item => item.MenuID === mainMenu.MenuID)[0]
-  
-        this.props.modifyMenuTree(res.menulist)
-        this.props.modifyMainMenu(_mainMenu || null)
-      } else {
-        notification.error({
-          top: 92,
-          message: result.message,
-          duration: 10
-        })
-      }
-      this.loadsubmenu(this.props.mainMenu)
-    })
-  }
-
-  getMenulist = (result) => {
-    let iframes = ['Main/Index', 'bda/rdt', 'Home/rdt']
-    let menulist = result.fst_menu.map(fst => {
-      let fstItem = {
-        MenuID: fst.MenuID,
-        MenuName: fst.MenuName,
-        PageParam: {OpenType: 'menu', linkUrl: ''},
-        children: []
-      }
-      if (fst.PageParam) {
-        try {
-          fstItem.PageParam = JSON.parse(fst.PageParam)
-        } catch (e) {
-          fstItem.PageParam = {OpenType: 'menu', linkUrl: ''}
-        }
-      }
-
-      if (fst.snd_menu) {
-        fstItem.children = fst.snd_menu.map(snd => {
-          let sndItem = {
-            ParentId: fst.MenuID,
-            MenuID: snd.MenuID,
-            MenuName: snd.MenuName,
-            PageParam: {Icon: 'folder'},
-            children: []
-          }
-
-          if (snd.PageParam) {
-            try {
-              sndItem.PageParam = JSON.parse(snd.PageParam)
-            } catch (e) {
-              sndItem.PageParam = {Icon: 'folder'}
-            }
-          }
-
-          if (snd.trd_menu) {
-            sndItem.children = snd.trd_menu.map(trd => {
-              let trdItem = {
-                FstId: fst.MenuID,
-                ParentId: snd.MenuID,
-                MenuID: trd.MenuID,
-                MenuName: trd.MenuName,
-                MenuNo: trd.MenuNo,
-                EasyCode: trd.EasyCode,
-                type: 'CommonTable',            // 榛樿鍊间负甯哥敤琛�
-                OpenType: 'newtab'              // 鎵撳紑鏂瑰紡
-              }
-  
-              if (trd.LinkUrl && iframes.includes(trd.LinkUrl.split('?')[0])) {
-                trdItem.type = 'iframe'
-                trdItem.LinkUrl = trd.LinkUrl
-                trdItem.forbidden = true
-              } else {
-                try {
-                  trdItem.PageParam = trd.PageParam ? JSON.parse(trd.PageParam) : {OpenType: 'newtab'}
-                } catch (e) {
-                  trdItem.PageParam = {OpenType: 'newtab'}
-                }
-
-                trdItem.type = trdItem.PageParam.Template || trdItem.type
-                trdItem.OpenType = trdItem.PageParam.OpenType || trdItem.OpenType
-
-                if (trdItem.type === 'CustomPage' && this.props.memberLevel < 20) { // 浼氬憳绛夌骇澶т簬绛変簬20鏃讹紝鏈夌紪杈戞潈闄�
-                  trdItem.forbidden = true
-                }
-              }
-
-              return trdItem
-            })
-          }
-
-          return sndItem
-        })
-      }
-
-      return fstItem
-    })
-
-    return { menulist }
-  }
-
-  exitEdit = () => {
-    if (this.props.editLevel === 'level3') {
-      this.setState({editMenu: null})
-    }
-    this.props.resetEditLevel(false)
-  }
-
   render () {
     const { mainMenu } = this.props
-    const editShow = (this.props.editState && !this.props.editLevel) || false
 
     if (mainMenu === '') return (<span className="mk-side-menu-hidden"></span>)
 
     return (
-      <aside id="mk-sidemenu-wrap" className={'mk-side-menu ant-menu-dark' + (this.props.collapse ? ' collapsed' : '') + (this.props.isiframe ? ' mk-iframe' : '') + (this.props.editState ? ' mk-edit' : '')}>
-        {!(this.props.editLevel === 'level2' || this.props.editLevel === 'level3') &&
-          <Menu openKeys={this.state.openKeys} onOpenChange={this.onOpenChange} mode="inline" theme="dark" inlineCollapsed={this.props.collapse}>
-          {editShow && <li className="sup-menu"><Icon onClick={this.enterSubEdit} className="edit-check" type="edit" /></li>}
-          {this.state.subMenulist && this.state.subMenulist.map((item, index) => {
+      <aside id="mk-sidemenu-wrap" className={'mk-side-menu ant-menu-dark' + (this.props.collapse ? ' collapsed' : '') + (this.props.isiframe ? ' mk-iframe' : '')}>
+        <Menu openKeys={this.state.openKeys} onOpenChange={this.onOpenChange} mode="inline" theme="dark" inlineCollapsed={this.props.collapse}>
+          {this.state.subMenulist && this.state.subMenulist.map((item) => {
             return (
               <SubMenu
                 key={item.MenuID}
                 title={
-                  <span className={editShow && index === 0 ? 'edit-control' : ''}>
+                  <span>
                     <Icon type={item.PageParam.Icon} />
                     <span>{item.MenuName}</span>
                   </span>
                 }
               >
-                {editShow ? <li className={'ant-menu-item ' + (item.children.length > 0 ? 'sub-menu' : '')}>
-                  <Icon onClick={(e) => {this.enterThrEdit(e, item)}} className="edit-check" type="edit" />
-                </li> : null}
                 {item.children.map(cell => {
                   return (
                     <Menu.Item key={cell.MenuID}>
@@ -312,25 +136,7 @@
               </SubMenu>
             )
           })}
-        </Menu>}
-        {this.props.editLevel === 'level2' ?
-          <EditSecMenu
-            menulist={this.state.subMenulist}
-            menuTree={this.props.menuTree}
-            supMenu={this.props.mainMenu}
-            reload={this.reload}
-            exitEdit={this.exitEdit}
-          /> : null
-        }
-        {this.props.editLevel === 'level3' && this.state.editMenu ?
-          <EditThdMenu
-            menulist={this.state.editMenu.children}
-            supMenuList={this.state.subMenulist}
-            supMenu={this.state.editMenu}
-            reload={this.reload}
-            exitEdit={this.exitEdit}
-          /> : null
-        }
+        </Menu>
       </aside>
     )
   }
@@ -343,19 +149,13 @@
     isiframe: state.isiframe,
     mainMenu: state.mainMenu,
     menuTree: state.menuTree,
-    memberLevel: state.memberLevel,
-    editState: state.editState,
-    editLevel: state.editLevel
   }
 }
 
 const mapDispatchToProps = (dispatch) => {
   return {
-    modifyMenuTree: (menuTree) => dispatch(modifyMenuTree(menuTree)),
-    modifyMainMenu: (mainMenu) => dispatch(modifyMainMenu(mainMenu)),
     modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews)),
-    resetEditLevel: (level) => dispatch(resetEditLevel(level))
   }
 }
 
-export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Sidemenu))
\ No newline at end of file
+export default connect(mapStateToProps, mapDispatchToProps)(Sidemenu)
\ No newline at end of file
diff --git a/src/components/sidemenu/index.scss b/src/components/sidemenu/index.scss
index e0c6a33..9cce86f 100644
--- a/src/components/sidemenu/index.scss
+++ b/src/components/sidemenu/index.scss
@@ -1,5 +1,4 @@
 @import '../../assets/css/iconfont.css';
-@import '../../assets/css/global.scss';
 
 .mk-side-menu {
   flex: 0 0 235px;
@@ -94,16 +93,7 @@
     left: 187px;
   }
 }
-.mk-side-menu.mk-edit { // 缂栬緫鏃舵帶鍒惰彍鍗曞簳鑹�
-  .ant-menu-sub.ant-menu-inline {
-    > .ant-menu-item.ant-menu-item-selected {
-      background: unset;
-    }
-    > .ant-menu-item.ant-menu-item-active {
-      background: unset;
-    }
-  }
-}
+
 .mk-side-menu.mk-iframe { // tab椤典腑涓篿frame鏃�
   max-height: 100vh;
   overflow-y: scroll;
diff --git a/src/components/tabview/index.jsx b/src/components/tabview/index.jsx
index 01abafb..980b810 100644
--- a/src/components/tabview/index.jsx
+++ b/src/components/tabview/index.jsx
@@ -9,6 +9,7 @@
 import { modifyTabview, toggleIsiframe } from '@/store/action'
 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'
@@ -35,7 +36,7 @@
   service = window.GLOB.location + '/' + window.GLOB.service + 'zh-CN/'
 }
 
-class Header extends Component {
+class TabViews extends Component {
   static propTpyes = {
     collapse: PropTypes.bool,
     tabviews: PropTypes.array // 鏍囩椤垫暟缁�
@@ -45,7 +46,7 @@
     activeId: '',
     tabviews: null, // 鏍囩闆�
     iFrameHeight: 0,
-    dict: localStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
   }
 
   handleTabview = (e, menu) => {
@@ -157,13 +158,15 @@
   }
 
   componentDidMount () {
-    let home = {
-      MenuID: 'home_page_id',
-      MenuName: this.state.dict['main.homepage'],
-      selected: true,
-      type: 'Home'
+    if (sessionStorage.getItem('isEditState') !== 'true') {
+      let home = {
+        MenuID: 'home_page_id',
+        MenuName: this.state.dict['main.homepage'],
+        selected: true,
+        type: 'Home'
+      }
+      this.props.modifyTabview([home])
     }
-    this.props.modifyTabview([home])
   }
 
   UNSAFE_componentWillReceiveProps (nextProps) {
@@ -241,6 +244,7 @@
                         </div>
                       </div>
                     </BackTop>
+                    {/* {options.sysType === 'local' && window.GLOB.systemType !== 'production' ? <div className="mk-water-mark">娴嬭瘯绯荤粺</div> : null} */}
                   </Tabs.TabPane>
                 )
               })}
@@ -267,4 +271,4 @@
   }
 }
 
-export default connect(mapStateToProps, mapDispatchToProps)(Header)
\ No newline at end of file
+export default connect(mapStateToProps, mapDispatchToProps)(TabViews)
\ No newline at end of file
diff --git a/src/components/tabview/index.scss b/src/components/tabview/index.scss
index 24f4644..fe43335 100644
--- a/src/components/tabview/index.scss
+++ b/src/components/tabview/index.scss
@@ -28,7 +28,7 @@
           }
           span.tab-control i.anticon-redo {
             position: absolute;
-            left: -3px;
+            left: -2px;
             top: 18px;
             font-size: 14px;
             margin: 0px;
@@ -107,6 +107,13 @@
     bottom: 20px;
     right: 30px;
   }
+  .mk-water-mark {
+    position: absolute;
+    top: 47px;
+    left: 5px;
+    font-size: 13px;
+    font-style: italic;
+  }
 }
 .mk-tabview-wrap.collapsed {
   max-width: calc(100% - 80px);
diff --git a/src/index.js b/src/index.js
index 14b728a..5e8a1c2 100644
--- a/src/index.js
+++ b/src/index.js
@@ -4,7 +4,7 @@
 import { Provider } from 'react-redux'
 import store from '@/store'
 import * as serviceWorker from './serviceWorker'
-import options from '@/store/options.js'
+import options, { styles } from '@/store/options.js'
 import '@/assets/css/main.scss'
 import '@/assets/css/action.scss'
 import '@/assets/css/minkeicon.css'
@@ -12,6 +12,8 @@
 
 if ((navigator.userAgent.match(/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i))) {
   window.location.replace(window.location.href.split(/(index.html)+/ig)[0] + 'mob/index.html')
+} else if (window.location.href.indexOf('#/design') > -1) { // 缂栬緫椤甸潰鍒锋柊鏃讹紝璺宠浆鑷充富椤�
+  window.location.replace(window.location.href.replace(/design/ig, 'main'))
 }
 
 options.sysType = window.atob(options.sysType.replace('$mk', ''))
@@ -67,24 +69,24 @@
 sessionStorage.setItem('role_id', sessionStorage.getItem('localRole_id') || '')
 sessionStorage.setItem('dataM', sessionStorage.getItem('localDataM') || '')
 
-fetch(`./options.json`)
+// 鏂扮郴缁熸枃浠剁疆浜巃dmin涓� ../options.json
+
+fetch('./options.json')
   .then(response => response.json())
   .catch(() => {
-    console.warn('绯荤粺閰嶇疆淇℃伅鑾峰彇澶辫触锛岃鑱旂郴绠$悊鍛橈紒')
+    document.getElementById('root').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">绯荤粺閰嶇疆淇℃伅鑾峰彇澶辫触锛岃鑱旂郴绠$悊鍛橈紒</div>'
   })
   .then(config => {
     if (!config) return
-    
+
     let GLOB = {}
     GLOB.appId = config.appId || ''
     GLOB.lineColor = config.lineColor || ''
-    GLOB.filter = config.filter || ''
 
     if (config.externalDatabase !== false && config.externalDatabase !== 'false' && config.externalDatabase !== undefined) {
       GLOB.externalDatabase = config.externalDatabase ? `[${config.externalDatabase}]..` : ''
     } else {
       GLOB.externalDatabase = null
-
     }
 
     // 鍙湁涓氬姟绯荤粺鎵嶅彲浠ヨ缃负姝e紡绯荤粺
@@ -114,24 +116,6 @@
       }
       if (config.mainSystemApi) {
         let systemApi = config.mainSystemApi
-      
-        // if (/^(http|https):\/\//ig.test(systemApi)) {
-        //   let _systemApi = /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(systemApi)
-      
-        //   systemApi = _systemApi ? _systemApi[0] : ''
-        // } else {
-        //   systemApi = ''
-        // }
-      
-        // // 涓氬姟绯荤粺杩炴帴浜戠鏃讹紝鏍煎紡鍖栧鐞�
-        // if (systemApi && systemApi === /^(http|https):\/\/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62}|(:[0-9]{1,4}))+\.?/ig.exec(options.cloudServiceApi)[0]) {
-        //   GLOB.dataFormat = true
-        // }
-      
-        // if (systemApi) {
-        //   systemApi = systemApi + '/webapi/dostars'
-        // }
-
         // 涓氬姟绯荤粺涓嶅厑璁歌繛鎺ヤ簯绔紝涓氬姟绯荤粺杩炴帴sso.mk9h.cn鏃讹紝鏁版嵁铏氬寲澶勭悊
         if (systemApi && systemApi.indexOf('cloud.mk9h.cn') > -1) {
           systemApi = ''
@@ -144,6 +128,10 @@
     }
 
     let _href = window.location.href.split('#')[0]
+
+    if (localStorage.getItem(_href + 'lang')) {
+      sessionStorage.setItem('lang', localStorage.getItem(_href + 'lang'))
+    }
 
     let _systemMsg = localStorage.getItem(_href + 'system')
 
@@ -163,6 +151,7 @@
         GLOB.webSite = _systemMsg.webSite
         GLOB.style = _systemMsg.style
         GLOB.showline = _systemMsg.showline || ''
+        GLOB.navBar = _systemMsg.navBar || ''
 
         if (GLOB.favicon) {
           let link = document.querySelector("link[rel*='icon']") || document.createElement('link')
@@ -171,8 +160,8 @@
           link.href = GLOB.favicon
           document.getElementsByTagName('head')[0].appendChild(link)
         }
-        if (GLOB.style && options.styles[GLOB.style]) {
-          document.body.className = options.styles[GLOB.style] + ' ' + (GLOB.showline === 'false' ? 'hidden-split-line' : '')
+        if (GLOB.style && styles[GLOB.style]) {
+          document.body.className = styles[GLOB.style] + ' ' + (GLOB.showline === 'false' ? 'hidden-split-line' : '')
         }
       } catch {
         console.warn('Parse Failure')
@@ -181,7 +170,7 @@
 
     document.title = GLOB.platTitle || ''
 
-    if (GLOB.filter === 'true') {
+    if (config.filter === 'true') {
       let html = document.getElementsByTagName('html')[0]
       
       if (html) {
@@ -190,7 +179,7 @@
     }
 
     if (process.env.NODE_ENV === 'production') { // 鐢ㄤ簬鏍¢獙鏄惁瀛樺湪寮�鍙戞潈闄�
-      let _service = window.location.href.replace(/\/index.html(.*)|\/#(.*)/ig, '').replace(new RegExp(document.location.origin + '/?', 'ig'), '')
+      let _service = window.location.href.replace(/(\/admin)?\/index.html(.*)|(\/admin)?\/#(.*)/ig, '').replace(new RegExp(document.location.origin + '/?', 'ig'), '')
       GLOB.linkurl = _href
       if (!/index.html/ig.test(GLOB.linkurl)) {
         GLOB.linkurl = GLOB.linkurl + 'index.html'
@@ -198,12 +187,8 @@
       GLOB.service = _service ? _service + '/' : ''
     } else {
       GLOB.linkurl = ''
-      GLOB.location = 'http://bms-test.kresstools.cn'
-      GLOB.service = 'oc/'
-    }
-
-    if (GLOB.style && options.styles[GLOB.style]) {
-      document.getElementById('root').className = options.styles[GLOB.style]
+      GLOB.location = config.host
+      GLOB.service = config.service
     }
 
     Object.defineProperty(GLOB, 'appId', {
diff --git a/src/locales/en-US/model.js b/src/locales/en-US/model.js
index 799732f..2d2ef4b 100644
--- a/src/locales/en-US/model.js
+++ b/src/locales/en-US/model.js
@@ -155,8 +155,7 @@
   'model.form.excelOut': 'Export excel',
   'model.form.newpage': 'The new page',
   'model.form.newpage.type': 'Page type',
-  'model.form.newpage.url': 'Page address',
-  'header.form.blank': '褰撳墠椤佃烦杞�',
+  'model.pageUrl': 'Page address',
   'model.form.prompt': 'Prompt',
   'model.form.exec': 'Direct execution',
   'model.form.paramJoint': 'Joint param',
diff --git a/src/locales/zh-CN/model.js b/src/locales/zh-CN/model.js
index 0367a1b..c50cd3a 100644
--- a/src/locales/zh-CN/model.js
+++ b/src/locales/zh-CN/model.js
@@ -155,8 +155,7 @@
   'model.form.excelOut': '瀵煎嚭Excel',
   'model.form.newpage': '鏂伴〉闈�',
   'model.form.newpage.type': '椤甸潰绫诲瀷',
-  'model.form.newpage.url': '椤甸潰鍦板潃',
-  'header.form.blank': '褰撳墠椤佃烦杞�',
+  'model.pageUrl': '椤甸潰鍦板潃',
   'model.form.prompt': '鎻愮ず妗�',
   'model.form.exec': '鐩存帴鎵ц',
   'model.form.paramJoint': '鎷兼帴鍙傛暟',
diff --git a/src/menu/bgcontroller/index.jsx b/src/menu/bgcontroller/index.jsx
index ab2155c..9dbdec5 100644
--- a/src/menu/bgcontroller/index.jsx
+++ b/src/menu/bgcontroller/index.jsx
@@ -18,7 +18,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     backgroundColor: '',
     backgroundImage: '',
   }
diff --git a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
index 4bb5745..96b6549 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -19,7 +19,7 @@
 const Video = asyncComponent(() => import('@/components/video'))
 const MarkColumn = asyncIconComponent(() => import('@/menu/components/share/markcomponent'))
 
-const Card = ({ id, parent, fields, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, updateMarks }) => {
+const Card = ({ id, parent, fields, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, updateMarks, doubleClickCard }) => {
   const originalIndex = findCard(id).index
   const [{ isDragging }, drag] = useDrag({
     item: { type: 'action', id, originalIndex },
@@ -55,7 +55,7 @@
     } else if (card.eleType === 'text' || card.eleType === 'number') {
       let val = `${card.prefix || ''}${card.datatype === 'static' ? (card.value || '') : (card.field || '')}${card.postfix || ''}`
       return (
-        <div className={'ant-mk-text line' + card.height} style={{height: card.innerHeight || 21}}>{val}</div>
+        <div className={'ant-mk-text line' + (card.height || '')} style={{height: card.innerHeight || 'auto'}}>{val}</div>
       )
     } else if (card.eleType === 'icon') {
       return (<Icon type={card.icon}/>)
@@ -150,7 +150,7 @@
       </div>
     } trigger="hover">
       <div ref={node => drag(drop(node))} className={'ant-col card-cell ant-col-' + card.width}>
-        <div style={_style} onClick={clickComponent} id={card.uuid}>
+        <div style={_style} onClick={clickComponent} onDoubleClick={() => doubleClickCard(id)} 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 c90814c..eda8cc2 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -80,6 +80,11 @@
 
   const doubleClickCard = id => {
     const { card } = findCard(id)
+
+    if (card.eleType !== 'button' && card.eleType !== 'text' && card.eleType !== 'picture') {
+      return
+    }
+    
     handleSubConfig(card)
   }
 
@@ -129,6 +134,7 @@
               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 1d8b187..2b2ec2c 100644
--- a/src/menu/components/card/cardcellcomponent/dragaction/index.scss
+++ b/src/menu/components/card/cardcellcomponent/dragaction/index.scss
@@ -4,7 +4,7 @@
     font-weight: inherit;
     text-decoration: inherit;
   }
-  .ant-mk-text:not(.line1) {
+  .ant-mk-text:not(.line1):not(.line) {
     word-break: break-word;
     display: -webkit-box;
     -webkit-box-orient: vertical;
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.jsx b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
index f3507e3..2f0f75c 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -7,6 +7,7 @@
 import asyncComponent from '@/utils/asyncComponent'
 import './index.scss'
 
+const { TextArea } = Input
 const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
 const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
 
@@ -79,7 +80,7 @@
           item.required = card.eleType !== 'qrcode'
         }
         if (item.key === 'linkurl') {
-          item.type = card.link === 'dynamic' ? 'select' : 'text'
+          item.type = card.link === 'dynamic' ? 'select' : 'textarea'
         }
 
         return item
@@ -100,7 +101,13 @@
       }
 
       if (['text', 'picture'].includes(eleType) && link) {
-        _options.push('linkurl', 'joint')
+        if (link === 'dynamic' || link === 'static' || link === 'custom') {
+          _options.push('linkurl', 'joint')
+        } else if (link === 'page') {
+          _options.push('copyMenuId', 'joint', 'open')
+        } else if (link === 'linkpage') {
+          _options.push('linkmenu', 'joint', 'open')
+        }
       }
     } else if (eleType === 'icon') {
       if (datatype === 'dynamic') {
@@ -121,7 +128,7 @@
    */
   selectChange = (key, value, option) => {
     const { config } = this.props
-    const { datatype } = this.state
+    const { datatype, eleType } = this.state
 
     if (key === 'eleType') {
       let _options = this.getOptions(value, datatype, '')
@@ -185,6 +192,18 @@
       if (this.props.form.getFieldValue('value') !== undefined) {
         this.props.form.setFieldsValue({value: option.props.title})
       }
+    } else if (key === 'link') {
+      let _options = this.getOptions(eleType, this.state.datatype, value)
+      this.setState({
+        link: value,
+        formlist: this.state.formlist.map(item => {
+          item.hidden = !_options.includes(item.key)
+          if (item.key === 'linkurl') {
+            item.type = value === 'dynamic' ? 'select' : 'textarea'
+          }
+          return item
+        })
+      })
     }
   }
 
@@ -210,7 +229,7 @@
         formlist: this.state.formlist.map(item => {
           item.hidden = !_options.includes(item.key)
           if (item.key === 'linkurl') {
-            item.type = value === 'dynamic' ? 'select' : 'text'
+            item.type = value === 'dynamic' ? 'select' : 'textarea'
           }
           return item
         })
@@ -231,15 +250,13 @@
     const fields = []
 
     this.state.formlist.forEach((item, index) => {
-      if (item.hidden) return
+      if (item.hidden || item.forbid) return
 
       if (item.type === 'text') { // 鏂囨湰鎼滅储
-        let rules = []
-
         fields.push(
           <Col span={12} key={index}>
             <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+              <Tooltip placement="topLeft" title={item.tooltip}>
                 <Icon type="question-circle" />
                 {item.label}
               </Tooltip> : item.label
@@ -254,39 +271,17 @@
                   {
                     max: formRule.input.max,
                     message: formRule.input.message
-                  },
-                  ...rules
+                  }
                 ]
               })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
             </Form.Item>
           </Col>
         )
-      } else if (item.type === 'number') {
+      } else if (item.type === 'textarea') { // 鏂囨湰鎼滅储
         fields.push(
-          <Col span={12} key={index}>
+          <Col span={24} className="textarea" key={index}>
             <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
-              {getFieldDecorator(item.key, {
-                initialValue: item.initVal,
-                rules: [
-                  {
-                    required: item.readonly ? false : !!item.required,
-                    message: this.props.dict['form.required.input'] + item.label + '!'
-                  }
-                ]
-              })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-        )
-      } else if (item.type === 'select') { // 涓嬫媺鎼滅储
-        fields.push(
-          <Col span={12} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+              <Tooltip placement="topLeft" title={item.tooltip}>
                 <Icon type="question-circle" />
                 {item.label}
               </Tooltip> : item.label
@@ -295,10 +290,52 @@
                 initialValue: item.initVal || '',
                 rules: [
                   {
-                    required: !!item.required,
-                    message: this.props.dict['form.required.select'] + item.label + '!'
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  },
+                  {
+                    max: formRule.input.max,
+                    message: formRule.input.message
                   }
                 ]
+              })(<TextArea rows={2} disabled={item.readonly} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'number') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [{
+                  required: item.readonly ? false : !!item.required,
+                  message: this.props.dict['form.required.input'] + item.label + '!'
+                }]
+              })(<InputNumber min={item.min || 0} max={item.max || 10000} precision={item.precision || 0} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'select') { // 涓嬫媺鎼滅储
+        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 + '!'
+                }]
               })(
                 <Select
                   showSearch
@@ -319,24 +356,25 @@
       } else if (item.type === 'radio') {
         fields.push(
           <Col span={12} key={index}>
-            <Form.Item label={item.label}>
+            <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 + '!'
-                  }
-                ]
+                rules: [{
+                  required: !!item.required,
+                  message: this.props.dict['form.required.select'] + item.label + '!'
+                }]
               })(
                 <Radio.Group onChange={(e) => {this.onChange(e, item.key)}} disabled={item.readonly}>
-                  {
-                    item.options.map(option => {
-                      return (
-                        <Radio key={option.value} value={option.value}>{option.text}</Radio>
-                      )
-                    })
-                  }
+                  {item.options.map(option => {
+                    return (
+                      <Radio key={option.value} value={option.value}>{option.text}</Radio>
+                    )
+                  })}
                 </Radio.Group>
               )}
             </Form.Item>
@@ -390,15 +428,6 @@
         if (!err) {
           values.uuid = this.props.card.uuid
           values.marks = this.props.card.marks || null
-
-          // if (values.eleType === 'picture' && values.datatype === 'static' && !values.url) {
-          //   notification.warning({
-          //     top: 92,
-          //     message: '灏氭湭娣诲姞鍥剧墖鎴栧浘鐗囦笂浼犲け璐ワ紝璇烽噸鏂版坊鍔狅紒',
-          //     duration: 5
-          //   })
-          //   return
-          // }
 
           resolve(values)
         } else {
diff --git a/src/menu/components/card/cardcellcomponent/elementform/index.scss b/src/menu/components/card/cardcellcomponent/elementform/index.scss
index 58fef06..f969efa 100644
--- a/src/menu/components/card/cardcellcomponent/elementform/index.scss
+++ b/src/menu/components/card/cardcellcomponent/elementform/index.scss
@@ -10,8 +10,19 @@
       margin-right: 5px;
     }
   }
-  .ant-col {
-    height: 65px;
+  >.ant-row {
+    >.ant-col {
+      height: 65px;
+    }
+    .ant-col.textarea {
+      height: 80px;
+      .ant-form-item-label {
+        width: 14.2%;
+      }
+      .ant-form-item-control-wrapper {
+        width: 85.8%;
+      }
+    }
   }
   .color-form {
     .ant-form-item-control {
diff --git a/src/menu/components/card/cardcellcomponent/formconfig.jsx b/src/menu/components/card/cardcellcomponent/formconfig.jsx
index c103921..b81da36 100644
--- a/src/menu/components/card/cardcellcomponent/formconfig.jsx
+++ b/src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -1,7 +1,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鎸夐挳琛ㄥ崟閰嶇疆淇℃伅
@@ -24,6 +24,22 @@
 
   if (type === 'table') {
     _options.push({value: 'sequence', text: '搴忓彿'})
+  }
+  let appMenus = []
+  const isApp = sessionStorage.getItem('appType') === 'pc'
+
+  if (isApp) {
+    appMenus = sessionStorage.getItem('appMenus')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+        appMenus = appMenus.map(item => ({value: item.MenuID, text: item.MenuName}))
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
   }
 
   let forms = [
@@ -101,6 +117,7 @@
       min: 0,
       label: '鍐呭',
       initVal: card.value || '',
+      tooltip: '鏂囨湰绫诲瀷锛屼細鏇挎崲鍐呭涓殑@username@銆丂fullName@銆丂login_city@銆�',
       required: true
     },
     {
@@ -132,26 +149,6 @@
         { value: 'true', text: '鏄�' },
         { value: 'false', text: '鍚�' }
       ]
-    },
-    {
-      type: 'radio',
-      key: 'link',
-      label: '閾炬帴',
-      initVal: card.link || '',
-      required: false,
-      options: [
-        { value: '', text: '鏃�' },
-        { value: 'dynamic', text: '鍔ㄦ��' },
-        { value: 'static', text: '闈欐��' }
-      ]
-    },
-    {
-      type: 'select',
-      key: 'linkurl',
-      label: '閾炬帴鍦板潃',
-      initVal: card.linkurl || '',
-      required: true,
-      options: []
     },
     {
       type: 'select',
@@ -314,6 +311,64 @@
     },
     {
       type: 'radio',
+      key: 'link',
+      label: '閾炬帴',
+      initVal: card.link || '',
+      tooltip: '鍔ㄦ�佸湴鍧�涓虹粦瀹氬瓧娈靛�笺��',
+      required: false,
+      forbid: isApp,
+      options: [
+        { value: '', text: '鏃�' },
+        { value: 'dynamic', text: '鍔ㄦ��' },
+        { value: 'static', text: '闈欐��' }
+      ]
+    },
+    {
+      type: 'select',
+      key: 'link',
+      label: '閾炬帴',
+      initVal: card.link || '',
+      required: false,
+      forbid: !isApp,
+      options: [
+        { value: '', text: '鏃�' },
+        { value: 'page', text: '鑿滃崟' },
+        { value: 'linkpage', text: '鍏宠仈鑿滃崟' },
+        { value: 'custom', text: '閾炬帴' }
+      ]
+    },
+    {
+      type: 'select',
+      key: 'linkmenu',
+      label: '鍏宠仈鑿滃崟',
+      initVal: card.linkmenu || '',
+      required: true,
+      forbid: !isApp,
+      options: appMenus
+    },
+    {
+      type: 'radio',
+      key: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initVal: card.open || 'blank',
+      required: false,
+      forbid: !isApp,
+      options: [
+        { value: 'blank', text: '鏂伴〉闈�' },
+        { value: 'self', text: '褰撳墠椤甸潰' }
+      ]
+    },
+    {
+      type: 'select',
+      key: 'copyMenuId',
+      label: '澶嶅埗鑿滃崟',
+      initVal: card.copyMenuId || '',
+      required: false,
+      forbid: !isApp,
+      options: appMenus
+    },
+    {
+      type: 'radio',
       key: 'joint',
       label: Formdict['model.form.paramJoint'],
       initVal: card.joint || 'true',
@@ -326,6 +381,14 @@
         text: Formdict['model.false']
       }]
     },
+    {
+      type: 'select',
+      key: 'linkurl',
+      label: '閾炬帴鍦板潃',
+      initVal: card.linkurl || '',
+      required: true,
+      options: []
+    },
   ]
 
   return forms
diff --git a/src/menu/components/card/cardcellcomponent/index.jsx b/src/menu/components/card/cardcellcomponent/index.jsx
index 24433e0..58deb61 100644
--- a/src/menu/components/card/cardcellcomponent/index.jsx
+++ b/src/menu/components/card/cardcellcomponent/index.jsx
@@ -1,7 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import {connect} from 'react-redux'
 import { Modal, Button } from 'antd'
 
 import zhCN from '@/locales/zh-CN/model.js'
@@ -11,7 +10,7 @@
 import { getActionForm } from '@/menu/components/share/actioncomponent/formconfig'
 
 import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import ElementForm from './elementform'
 import DragElement from './dragaction'
 import './index.scss'
@@ -19,7 +18,6 @@
 const { confirm } = Modal
 
 const ActionForm = asyncComponent(() => import('@/menu/components/share/actioncomponent/actionform'))
-const CreateFunc = asyncComponent(() => import('@/templates/zshare/createfunc'))
 const VerifyCard = asyncComponent(() => import('@/templates/zshare/verifycard'))
 const VerifyPrint = asyncComponent(() => import('@/templates/sharecomponent/actioncomponent/verifyprint'))
 const VerifyExcelIn = asyncComponent(() => import('@/templates/sharecomponent/actioncomponent/verifyexcelin'))
@@ -35,7 +33,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,          // 缂栬緫涓厓绱�
     formlist: null,      // 琛ㄥ崟淇℃伅
     elements: null,      // 鎸夐挳缁�
@@ -153,7 +151,7 @@
     const { cards, cardCell } = this.props
     const { card, elements } = this.state
 
-    if (comIds.length !== 3 || comIds[0] !== cards.uuid || comIds[1] !== cardCell.uuid) return
+    if (comIds.length !== 3 || comIds[0] !== cards.uuid || comIds[1] !== cardCell.uuid || !card) return
 
     let _card = this.resetCardStyle(card, style)
 
@@ -177,7 +175,7 @@
 
       let fontSize = 14
       let lineHeight = 1.5
-      let line = _card.height || 1
+      let line = _card.height || null
 
       if (_card.style.fontSize) {
         fontSize = parseInt(_card.style.fontSize)
@@ -186,7 +184,9 @@
         lineHeight = parseFloat(_card.style.lineHeight)
       }
 
-      _card.innerHeight = fontSize * lineHeight * line
+      if (line) {
+        _card.innerHeight = fontSize * lineHeight * line
+      }
     } else if (_card.eleType === 'barcode') {
       _card.style = style
 
@@ -276,7 +276,7 @@
    * @description 鎸夐挳缂栬緫锛岃幏鍙栨寜閽〃鍗曚俊鎭�
    */
   handleAction = (card) => {
-    const { menu, cards } = this.props
+    const { cards } = this.props
 
     let usefulFields = sessionStorage.getItem('permFuncField')
     if (usefulFields) {
@@ -305,7 +305,7 @@
       menulist = []
     }
 
-    let modules = MenuUtils.getSubModules(menu.components, cards.uuid)
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, cards.uuid)
 
     this.setState({
       actvisible: true,
@@ -352,7 +352,7 @@
           } else if (res.eleType === 'text' || res.eleType === 'number') {
             let fontSize = 14
             let lineHeight = 1.5
-            let line = res.height || 1
+            let line = res.height || null
       
             if (res.style && res.style.fontSize) {
               fontSize = parseInt(res.style.fontSize)
@@ -360,7 +360,10 @@
             if (res.style && res.style.lineHeight) {
               lineHeight = parseFloat(res.style.lineHeight)
             }
-            res.innerHeight = fontSize * lineHeight * line
+
+            if (line) {
+              res.innerHeight = fontSize * lineHeight * line
+            }
 
             if (res.eleType === 'text' && res.link && !res.style.color) {
               res.style.color = '#2440B3'
@@ -499,21 +502,33 @@
     const { cards } = this.props
     let btn = fromJS(item).toJS()
 
-    if (btn.eleType !== 'button' || (sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') !== 'false')) return
+    if ((sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') !== 'false')) return
 
-    if (btn.OpenType === 'pop') {
-      if (!btn.modal) {
-        btn.modal = {
-          setting: { title: btn.label, width: 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
-          tables: [],
-          groups: [],
-          fields: []
+    if (btn.eleType === 'button') {
+      if (btn.OpenType === 'pop') {
+        if (!btn.modal) {
+          btn.modal = {
+            setting: { title: btn.label, width: 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
+            tables: [],
+            groups: [],
+            fields: []
+          }
         }
+  
+        MKEmitter.emit('changeModal', cards, btn)
+      } else if (btn.OpenType === 'popview') {
+        MKEmitter.emit('changePopview', cards, btn)
+      } else if (btn.OpenType === 'innerpage' && btn.pageTemplate === 'page') {
+        MKEmitter.emit('changeEditMenu', {MenuID: btn.uuid, copyMenuId: btn.copyMenuId})
+      } else if (btn.OpenType === 'innerpage' && btn.pageTemplate === 'linkpage') {
+        MKEmitter.emit('changeEditMenu', {MenuID: btn.linkmenu})
       }
-
-      MKEmitter.emit('changeModal', cards, btn)
-    } else if (btn.OpenType === 'popview') {
-      MKEmitter.emit('changePopview', cards, btn)
+    } else {
+      if (btn.link === 'page') {
+        MKEmitter.emit('changeEditMenu', {MenuID: btn.uuid, copyMenuId: btn.copyMenuId})
+      } else if (btn.link === 'linkpage') {
+        MKEmitter.emit('changeEditMenu', {MenuID: btn.linkmenu})
+      }
     }
   }
 
@@ -559,6 +574,9 @@
 
   dropButton = (id) => {
     const { cards } = this.props
+
+    if (!cards.action) return
+
     let index = cards.action.findIndex(item => item.uuid === id)
 
     if (index === -1) return
@@ -622,7 +640,6 @@
           maskClosable={false}
           onCancel={this.editModalCancel}
           footer={[
-            <CreateFunc key="create" dict={dict} ref="btnCreatFunc" trigger={this.creatFunc}/>,
             <Button key="cancel" onClick={this.editModalCancel}>{dict['model.cancel']}</Button>,
             <Button key="confirm" type="primary" onClick={this.handleActionSubmit}>{dict['model.confirm']}</Button>
           ]}
@@ -645,7 +662,6 @@
           visible={profVisible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.verifySubmit}
           onCancel={() => { this.setState({ profVisible: false }) }}
@@ -690,14 +706,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(CardCellComponent)
\ No newline at end of file
+export default CardCellComponent
\ No newline at end of file
diff --git a/src/menu/components/card/cardcomponent/index.jsx b/src/menu/components/card/cardcomponent/index.jsx
index e424497..f32968a 100644
--- a/src/menu/components/card/cardcomponent/index.jsx
+++ b/src/menu/components/card/cardcomponent/index.jsx
@@ -27,7 +27,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
     formlist: null,        // 璁剧疆琛ㄥ崟淇℃伅
     elements: null,        // 缂栬緫缁�
diff --git a/src/menu/components/card/cardcomponent/settingform/index.jsx b/src/menu/components/card/cardcomponent/settingform/index.jsx
index 4045ba3..738bd50 100644
--- a/src/menu/components/card/cardcomponent/settingform/index.jsx
+++ b/src/menu/components/card/cardcomponent/settingform/index.jsx
@@ -1,8 +1,10 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { Form, Row, Col, Radio, Tooltip, Icon, Input, InputNumber, Select } from 'antd'
+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 = {
@@ -13,7 +15,32 @@
   }
 
   state = {
-    type: this.props.setting.type || 'simple'
+    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 = () => {
@@ -40,6 +67,7 @@
   render() {
     const { setting, cards } = this.props
     const { getFieldDecorator } = this.props.form
+    const { menulist, click, isApp } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -126,6 +154,93 @@
                 })(<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>
diff --git a/src/menu/components/card/cardcomponent/settingform/index.scss b/src/menu/components/card/cardcomponent/settingform/index.scss
index 159130b..8898344 100644
--- a/src/menu/components/card/cardcomponent/settingform/index.scss
+++ b/src/menu/components/card/cardcomponent/settingform/index.scss
@@ -8,4 +8,12 @@
   .ant-input-number {
     width: 100%;
   }
+  .textarea {
+    .ant-form-item-label {
+      width: 16%;
+    }
+    .ant-form-item-control-wrapper {
+      width: 84%;
+    }
+  }
 }
\ 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 37cebdb..7b12967 100644
--- a/src/menu/components/card/data-card/index.jsx
+++ b/src/menu/components/card/data-card/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, Modal, Pagination, notification } from 'antd'
 
@@ -33,7 +32,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -42,6 +41,7 @@
     const { card } = this.props
 
     if (card.isNew) {
+      let ismob = sessionStorage.getItem('appType') === 'mob'
       let _card = {
         uuid: card.uuid,
         type: card.type,
@@ -66,7 +66,7 @@
         btnlog: [],
         subcards: [{
           uuid: Utils.getuuid(),
-          setting: { width: 6, type: 'simple'},
+          setting: { width: ismob ? 24 : 6, type: 'simple'},
           style: {
             borderWidth: '1px', borderColor: '#e8e8e8',
             paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
@@ -129,7 +129,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -295,7 +295,7 @@
     const { card } = this.state
     let btn = fromJS(item).toJS()
 
-    if (btn.OpenType === 'pop') {
+    if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
       if (!btn.modal) {
         btn.modal = {
           setting: { title: btn.label, width: 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
@@ -424,14 +424,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(DataCardEditComponent)
\ No newline at end of file
+export default DataCardEditComponent
\ 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
index eca0dc3..81346a6 100644
--- a/src/menu/components/card/data-card/wrapsetting/index.jsx
+++ b/src/menu/components/card/data-card/wrapsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     wrap: null
   }
diff --git a/src/menu/components/card/prop-card/index.jsx b/src/menu/components/card/prop-card/index.jsx
index 9032026..8cac50c 100644
--- a/src/menu/components/card/prop-card/index.jsx
+++ b/src/menu/components/card/prop-card/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, Modal, notification } from 'antd'
 
@@ -20,6 +19,7 @@
 const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 
 const { confirm } = Modal
@@ -32,7 +32,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -41,6 +41,7 @@
     const { card } = this.props
 
     if (card.isNew) {
+      let ismob = sessionStorage.getItem('appType') === 'mob'
       let _card = {
         uuid: card.uuid,
         type: card.type,
@@ -55,14 +56,14 @@
         name: card.name,
         subtype: card.subtype,
         setting: { interType: 'system' },
-        wrap: { name: card.name, width: card.width || 24, title: '', addable: 'false', switch: 'false', datatype: 'static' },
+        wrap: { name: card.name, width: card.width || 24, title: '', switch: 'false', datatype: 'static' },
         style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
         headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
         columns: [],
         scripts: [],
         subcards: [{
           uuid: Utils.getuuid(),
-          setting: { width: 6, type: 'simple'},
+          setting: { width: ismob ? 24 : 6, type: 'simple'},
           style: {
             borderWidth: '1px', borderColor: '#e8e8e8',
             paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
@@ -114,7 +115,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -383,6 +384,7 @@
             <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
+            <ClockComponent config={card} updateConfig={this.updateComponent}/>
             <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}
@@ -397,14 +399,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(PropCardEditComponent)
\ No newline at end of file
+export default PropCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/card/table-card/cardcomponent/index.jsx b/src/menu/components/card/table-card/cardcomponent/index.jsx
index 09b052e..e3d9ac7 100644
--- a/src/menu/components/card/table-card/cardcomponent/index.jsx
+++ b/src/menu/components/card/table-card/cardcomponent/index.jsx
@@ -25,7 +25,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
     formlist: null,        // 璁剧疆琛ㄥ崟淇℃伅
     elements: null,        // 缂栬緫缁�
diff --git a/src/menu/components/card/table-card/index.jsx b/src/menu/components/card/table-card/index.jsx
index b84b815..540f163 100644
--- a/src/menu/components/card/table-card/index.jsx
+++ b/src/menu/components/card/table-card/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, Modal, Pagination, notification } from 'antd'
 
@@ -32,7 +31,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -353,14 +352,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(TableCardEditComponent)
\ No newline at end of file
+export default TableCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/carousel/cardcomponent/index.jsx b/src/menu/components/carousel/cardcomponent/index.jsx
new file mode 100644
index 0000000..5a3909e
--- /dev/null
+++ b/src/menu/components/carousel/cardcomponent/index.jsx
@@ -0,0 +1,229 @@
+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 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
+
+    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/mob/datasource/verifycard/columnform/index.scss b/src/menu/components/carousel/cardcomponent/index.scss
similarity index 100%
copy from src/mob/datasource/verifycard/columnform/index.scss
copy to src/menu/components/carousel/cardcomponent/index.scss
diff --git a/src/menu/components/carousel/cardcomponent/settingform/index.jsx b/src/menu/components/carousel/cardcomponent/settingform/index.jsx
new file mode 100644
index 0000000..d8046cc
--- /dev/null
+++ b/src/menu/components/carousel/cardcomponent/settingform/index.jsx
@@ -0,0 +1,194 @@
+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
new file mode 100644
index 0000000..159130b
--- /dev/null
+++ b/src/menu/components/carousel/cardcomponent/settingform/index.scss
@@ -0,0 +1,11 @@
+.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
new file mode 100644
index 0000000..df2cf25
--- /dev/null
+++ b/src/menu/components/carousel/data-card/index.jsx
@@ -0,0 +1,276 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover, Modal, notification } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+
+import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
+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 LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
+const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+
+const { confirm } = Modal
+
+class DataCardEditComponent extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    card: null,
+    back: false
+  }
+
+  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: 'array',    // 缁勪欢灞炴�� - 鏁版嵁鏍煎紡
+        pageable: false,    // 缁勪欢灞炴�� - 鏄惁鍙垎椤�
+        switchable: false,   // 缁勪欢灞炴�� - 鏁版嵁鏄惁鍙垏鎹�
+        dataName: card.dataName || '',
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        setting: { interType: 'system' },
+        wrap: { name: card.name, width: card.width || 24, autoplay: 'false', dots: 'true' },
+        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginTop: '8px', marginBottom: '8px', height: '300px' },
+        columns: [],
+        scripts: [],
+        btnlog: [],
+        subcards: [{
+          uuid: Utils.getuuid(),
+          setting: {},
+          style: {
+            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
+          },
+          elements: []
+        }]
+      }
+
+      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.elements = scard.elements.map(elem => {
+            elem.uuid = Utils.getuuid()
+            return elem
+          })
+          return scard
+        })
+      }
+
+      this.setState({
+        card: _card
+      })
+      this.props.updateConfig(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+    MKEmitter.addListener('logButton', this.logButton)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+    MKEmitter.removeListener('logButton', this.logButton)
+  }
+
+  logButton = (id, item) => {
+    const { card } = this.state
+
+    if (id !== card.uuid) return
+
+    let btnlog = card.btnlog || []
+    btnlog.push(item)
+
+    this.setState({
+      card: {...card, btnlog}
+    })
+    this.props.updateConfig({...card, btnlog})
+  }
+  
+  /**
+   * @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)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  deleteCard = (cell) => {
+    let card = fromJS(this.state.card).toJS()
+    let _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎鍗$墖鍚楋紵',
+      onOk() {
+        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
+
+        let uuids = []
+        cell.elements && cell.elements.forEach(c => {
+          if (c.eleType === 'button') {
+            uuids.push(c.uuid)
+          }
+        })
+        MKEmitter.emit('delButtons', uuids)
+
+        if (card.btnlog) {
+          card.btnlog = card.btnlog.filter(c => c.$parentId !== cell.uuid)
+        }
+
+        _this.setState({card})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], 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)
+  }
+
+  handleLog = (type, logs, item) => {
+    let card = fromJS(this.state.card).toJS()
+
+    if (type === 'revert') {
+      card.subcards.forEach(col => {
+        col.elements = [...col.elements, item]
+        if (item.$parentId === col.uuid) {
+          col.elements = [...col.elements, item]
+        }
+      })
+
+      card.btnlog = logs
+
+      this.setState({ card })
+      this.props.updateConfig(card)
+      notification.success({
+        top: 92,
+        message: '鎭㈠鎴愬姛锛�',
+        duration: 2
+      })
+    } else {
+      card.btnlog = logs
+      this.setState({ card })
+      this.props.updateConfig(card)
+      notification.success({
+        top: 92,
+        message: '娓呴櫎鎴愬姛锛�',
+        duration: 2
+      })
+    }
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  render() {
+    const { card } = this.state
+
+    return (
+      <div className="menu-data-carousel-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}/>
+            <CopyComponent type="datacard" card={card}/>
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors"/>
+            <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog}/>
+            <UserComponent config={card}/>
+            <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)}/>
+            <SettingComponent config={card} updateConfig={this.updateComponent}/>
+          </div>
+        } trigger="hover">
+          <Icon type="tool"/>
+        </Popover>
+        <CardComponent cards={card} card={card.subcards[0]} updateElement={this.updateCard} deleteElement={this.deleteCard}/>
+      </div>
+    )
+  }
+}
+
+export default DataCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/carousel/data-card/index.scss b/src/menu/components/carousel/data-card/index.scss
new file mode 100644
index 0000000..b62bd6b
--- /dev/null
+++ b/src/menu/components/carousel/data-card/index.scss
@@ -0,0 +1,87 @@
+.menu-data-carousel-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);
+  }
+
+  .card-item {
+    overflow: hidden;
+    position: relative;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+    min-height: 20px;
+    height: 100%;
+  }
+  
+  .card-item:hover {
+    box-shadow: 0px 0px 2px #1890ff;
+  }
+
+  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
+    position: absolute;
+    right: -30px;
+    font-size: 16px;
+  }
+  .model-menu-action-list {
+    line-height: 40px;
+    .ant-row > .anticon-plus {
+      position: absolute;
+      right: -30px;
+      font-size: 16px;
+    }
+  }
+  .card-add-button {
+    text-align: right;
+    clear: left;
+    .anticon-plus {
+      font-size: 20px;
+      color: #26C281;
+      padding: 5px;
+      margin-right: 10px;
+    }
+  }
+  .ant-pagination {
+    float: right;
+    margin: 10px;
+  }
+
+  .model-menu-action-list {
+    .page-card {
+      line-height: 55px;
+    }
+  }
+}
+.menu-carousel-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-carousel-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/carousel/data-card/wrapsetting/index.jsx b/src/menu/components/carousel/data-card/wrapsetting/index.jsx
new file mode 100644
index 0000000..7b28116
--- /dev/null
+++ b/src/menu/components/carousel/data-card/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+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
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/menu/components/carousel/data-card/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.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
new file mode 100644
index 0000000..0007143
--- /dev/null
+++ b/src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx
@@ -0,0 +1,214 @@
+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, 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>
+            {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>
+            <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>
+            <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>
+            <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
new file mode 100644
index 0000000..159130b
--- /dev/null
+++ b/src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss
@@ -0,0 +1,11 @@
+.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
new file mode 100644
index 0000000..b1f7abf
--- /dev/null
+++ b/src/menu/components/carousel/prop-card/index.jsx
@@ -0,0 +1,358 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover, Modal, notification, Carousel } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import asyncIconComponent from '@/utils/asyncIconComponent'
+
+import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
+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 CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
+const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
+const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
+const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+
+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,
+    card: null,
+    back: false
+  }
+
+  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', autoplay: 'false', dots: 'true' },
+        style: { borderWidth: '1px', borderColor: '#e8e8e8', marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px', height: '300px' },
+        columns: [],
+        scripts: [],
+        subcards: [{
+          uuid: Utils.getuuid(),
+          setting: {},
+          style: {},
+          elements: [],
+        }],
+        btnlog: [],
+      }
+
+      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.elements = scard.elements.map(elem => {
+            elem.uuid = Utils.getuuid()
+            return elem
+          })
+          return scard
+        })
+      }
+      this.setState({
+        card: _card
+      })
+      this.props.updateConfig(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+    MKEmitter.addListener('logButton', this.logButton)
+    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('logButton', this.logButton)
+    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})
+    })
+  }
+
+  logButton = (id, item) => {
+    const { card } = this.state
+
+    if (id !== card.uuid) return
+
+    let btnlog = card.btnlog || []
+    btnlog.push(item)
+
+    this.setState({
+      card: {...card, btnlog}
+    })
+    this.props.updateConfig({...card, btnlog})
+  }
+
+  /**
+   * @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)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  deleteCard = (cell) => {
+    let card = fromJS(this.state.card).toJS()
+    let _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎鍗$墖鍚楋紵',
+      onOk() {
+        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
+        
+        let uuids = []
+        cell.elements && cell.elements.forEach(c => {
+          if (c.eleType === 'button') {
+            uuids.push(c.uuid)
+          }
+        })
+        MKEmitter.emit('delButtons', uuids)
+
+        if (card.btnlog) {
+          card.btnlog = card.btnlog.filter(c => c.$parentId !== cell.uuid)
+        }
+
+        _this.setState({card})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], 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: {},
+      style: {},
+      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)
+    
+    this.setState({card})
+    this.props.updateConfig(card)
+  }
+
+  handleLog = (type, logs, item) => {
+    let card = fromJS(this.state.card).toJS()
+
+    if (type === 'revert') {
+      let done = false
+      if (item.$parentId) {
+        card.subcards.forEach(col => {
+          if (item.$parentId === col.uuid) {
+            col.elements = [...col.elements, item]
+            done = true
+          }
+        })
+      }
+
+      card.btnlog = logs
+
+      this.setState({ card: {...card, subcards: []} }, () => {
+        this.setState({ card })
+        this.props.updateConfig(card)
+      })
+      if (!done) {
+        notification.warning({
+          top: 92,
+          message: '闄勫睘鍗$墖宸插垹闄わ紒',
+          duration: 2
+        })
+      } else {
+        notification.success({
+          top: 92,
+          message: '鎭㈠鎴愬姛锛�',
+          duration: 2
+        })
+      }
+    } else {
+      card.btnlog = logs
+      this.setState({ card })
+      this.props.updateConfig(card)
+      notification.success({
+        top: 92,
+        message: '娓呴櫎鎴愬姛锛�',
+        duration: 2
+      })
+    }
+  }
+
+  move = (item, direction) => {
+    let card = fromJS(this.state.card).toJS()
+
+    let dragIndex = card.subcards.findIndex(c => c.uuid === item.uuid)
+    let hoverIndex = null
+
+    if (direction === 'left') {
+      hoverIndex = dragIndex - 1
+    } else {
+      hoverIndex = dragIndex + 1
+    }
+
+    if (hoverIndex === -1 || hoverIndex === card.subcards.length) return 
+
+    card.subcards.splice(hoverIndex, 0, ...card.subcards.splice(dragIndex, 1))
+
+    this.setState({card: {...card, subcards: []}}, () => {
+      this.setState({card})
+    })
+    this.props.updateConfig(card)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  render() {
+    const { card } = this.state
+
+    return (
+      <div className="menu-prop-carousel-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">
+            <Icon className="plus" title="娣诲姞鍗$墖" onClick={this.addCard} type="plus" />
+            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <CopyComponent type="propcard" card={card}/>
+            <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
+            <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>
+        {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}/>))}
+        </Carousel> : null}
+      </div>
+    )
+  }
+}
+
+export default PropCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/carousel/prop-card/index.scss b/src/menu/components/carousel/prop-card/index.scss
new file mode 100644
index 0000000..a21b98f
--- /dev/null
+++ b/src/menu/components/carousel/prop-card/index.scss
@@ -0,0 +1,92 @@
+.menu-prop-carousel-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);
+  }
+
+  .card-item {
+    overflow: hidden;
+    position: relative;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+    min-height: 20px;
+    height: 100%;
+  }
+  .ant-carousel:not(.ant-carousel-vertical) {
+    .slick-dots li button {
+      height: 8px;
+    }
+  }
+  .ant-carousel.ant-carousel-vertical {
+    .slick-dots li button {
+      width: 8px;
+    }
+  }
+  .ant-carousel {
+    .slick-dots li button {
+      background: #1890ff;
+    }
+  }
+  
+  .card-item:hover {
+    box-shadow: 0px 0px 2px #1890ff;
+  }
+
+  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
+    position: absolute;
+    right: -30px;
+    font-size: 16px;
+  }
+  .model-menu-action-list {
+    line-height: 40px;
+    .ant-row > .anticon-plus {
+      position: absolute;
+      right: -30px;
+      font-size: 16px;
+    }
+  }
+  .card-add-button {
+    text-align: right;
+    clear: left;
+    .anticon-plus {
+      font-size: 20px;
+      color: #26C281;
+      padding: 5px;
+      margin-right: 10px;
+    }
+  }
+}
+.menu-prop-card-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.menu-prop-card-edit-box:hover {
+  z-index: 1;
+  box-shadow: 0px 0px 4px #1890ff;
+}
diff --git a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
index 3a2ded3..29d0d30 100644
--- a/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -1,17 +1,32 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟
  * @param {object} card       // 鍥捐〃瀵硅薄
  */
 export function getBaseForm (card) {
-  let menulist = sessionStorage.getItem('fstMenuList')
+  let isApp = sessionStorage.getItem('appType') === 'pc'
+  let menulist = null
+
+  if (isApp) {
+    menulist = sessionStorage.getItem('appMenus')
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+  }
+
   if (menulist) {
     try {
       menulist = JSON.parse(menulist)
+      if (isApp) {
+        menulist = menulist.map(item => {
+          item.value = item.MenuID
+          item.text = item.MenuName
+          return item
+        })
+      }
     } catch {
       menulist = []
     }
@@ -83,7 +98,30 @@
       initVal: card.linkmenu || [],
       tooltip: '鍦ㄤ娇鐢ㄦ煴褰㈠浘涓旀湭鍚敤鑷畾涔夎缃椂鏈夋晥銆�',
       required: false,
+      forbid: isApp,
       options: menulist
+    },
+    {
+      type: 'select',
+      key: 'linkmenu',
+      label: '鍏宠仈鑿滃崟',
+      initVal: card.linkmenu || '',
+      tooltip: '鍙屽嚮楗煎浘锛屼細鎵撳紑鍏宠仈鐨勮彍鍗曘��',
+      required: false,
+      forbid: !isApp,
+      options: menulist
+    },
+    {
+      type: 'radio',
+      key: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initVal: card.open || 'blank',
+      required: false,
+      forbid: !isApp,
+      options: [
+        { value: 'blank', text: '鏂扮獥鍙�' },
+        { value: 'self', text: '褰撳墠绐楀彛' }
+      ]
     }
   ]
 }
diff --git a/src/menu/components/chart/antv-bar/index.jsx b/src/menu/components/chart/antv-bar/index.jsx
index 25074e0..40cfcf1 100644
--- a/src/menu/components/chart/antv-bar/index.jsx
+++ b/src/menu/components/chart/antv-bar/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, notification } from 'antd'
 import { Chart } from '@antv/g2'
@@ -24,6 +23,7 @@
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const ActionComponent = asyncComponent(() => import('@/menu/components/share/actioncomponent'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
 
 class antvBarLineChart extends Component {
   static propTpyes = {
@@ -33,7 +33,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     eventListener: null
   }
@@ -127,7 +127,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -838,12 +838,7 @@
 
     let _card = {...card, style}
 
-    // this.setState({
-    //   card: _card
-    // })
-    
     this.updateComponent(_card)
-    // this.props.updateConfig(_card)
   }
 
   handleLog = (type, logs, item) => {
@@ -894,6 +889,7 @@
             <PasteComponent config={card} options={['action', 'search', 'form']} updateConfig={this.updateComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
+            <ClockComponent config={card} updateConfig={this.updateComponent}/>
             <UserComponent config={card}/>
             <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
             <SettingComponent config={card} updateConfig={this.updateComponent}/>
@@ -912,14 +908,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(antvBarLineChart)
\ No newline at end of file
+export default antvBarLineChart
\ No newline at end of file
diff --git a/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx b/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
index 7a79ef7..6c4e322 100644
--- a/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
+++ b/src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
@@ -1,17 +1,32 @@
 // import zhCN from '@/locales/zh-CN/model.js'
 // import enUS from '@/locales/en-US/model.js'
 
-// const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+// const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鍥捐〃瑙嗗浘閰嶇疆琛ㄥ崟
  * @param {object} card       // 鍥捐〃瀵硅薄
  */
 export function getBaseForm (card) {
-  let menulist = sessionStorage.getItem('fstMenuList')
+  let isApp = sessionStorage.getItem('appType') === 'pc'
+  let menulist = null
+
+  if (isApp) {
+    menulist = sessionStorage.getItem('appMenus')
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+  }
+
   if (menulist) {
     try {
       menulist = JSON.parse(menulist)
+      if (isApp) {
+        menulist = menulist.map(item => {
+          item.value = item.MenuID
+          item.text = item.MenuName
+          return item
+        })
+      }
     } catch {
       menulist = []
     }
@@ -83,7 +98,30 @@
       initVal: card.linkmenu || [],
       tooltip: '鍙屽嚮楗煎浘锛屼細鎵撳紑鍏宠仈鐨勮彍鍗曘��',
       required: false,
+      forbid: isApp,
       options: menulist
+    },
+    {
+      type: 'select',
+      key: 'linkmenu',
+      label: '鍏宠仈鑿滃崟',
+      initVal: card.linkmenu || '',
+      tooltip: '鍙屽嚮楗煎浘锛屼細鎵撳紑鍏宠仈鐨勮彍鍗曘��',
+      required: false,
+      forbid: !isApp,
+      options: menulist
+    },
+    {
+      type: 'radio',
+      key: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initVal: card.open || 'blank',
+      required: false,
+      forbid: !isApp,
+      options: [
+        { value: 'blank', text: '鏂扮獥鍙�' },
+        { value: 'self', text: '褰撳墠绐楀彛' }
+      ]
     }
   ]
 }
diff --git a/src/menu/components/chart/antv-pie/index.jsx b/src/menu/components/chart/antv-pie/index.jsx
index 4745360..374720a 100644
--- a/src/menu/components/chart/antv-pie/index.jsx
+++ b/src/menu/components/chart/antv-pie/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, notification } from 'antd'
 import { Chart } from '@antv/g2'
@@ -22,6 +21,7 @@
 const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
 const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
 const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
+const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
 
 class antvBarLineChart extends Component {
   static propTpyes = {
@@ -31,7 +31,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     eventListener: null
   }
@@ -111,7 +111,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -457,6 +457,7 @@
             <PasteComponent config={card} options={['search', 'form']} updateConfig={this.updateComponent} />
             <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
             <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
+            <ClockComponent config={card} updateConfig={this.updateComponent}/>
             <UserComponent config={card}/>
             <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
             <SettingComponent config={card} updateConfig={this.updateComponent}/>
@@ -470,14 +471,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(antvBarLineChart)
\ No newline at end of file
+export default antvBarLineChart
\ No newline at end of file
diff --git a/src/menu/components/code/sandbox/editorcode/index.jsx b/src/menu/components/code/sandbox/editorcode/index.jsx
index ff65601..5a51a66 100644
--- a/src/menu/components/code/sandbox/editorcode/index.jsx
+++ b/src/menu/components/code/sandbox/editorcode/index.jsx
@@ -18,7 +18,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     html: '',
     css: '',
diff --git a/src/menu/components/code/sandbox/index.jsx b/src/menu/components/code/sandbox/index.jsx
index 4b8097d..7e1fb66 100644
--- a/src/menu/components/code/sandbox/index.jsx
+++ b/src/menu/components/code/sandbox/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover } from 'antd'
 
@@ -28,7 +27,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -87,7 +86,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -175,14 +174,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(CodeSandBox)
\ No newline at end of file
+export default CodeSandBox
\ 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
index c949c28..bccbfd8 100644
--- a/src/menu/components/code/sandbox/wrapsetting/index.jsx
+++ b/src/menu/components/code/sandbox/wrapsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     wrap: null
   }
diff --git a/src/menu/components/editor/braft-editor/editorcontent/index.jsx b/src/menu/components/editor/braft-editor/editorcontent/index.jsx
index fd1f8a4..d1d288b 100644
--- a/src/menu/components/editor/braft-editor/editorcontent/index.jsx
+++ b/src/menu/components/editor/braft-editor/editorcontent/index.jsx
@@ -17,7 +17,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     html: null
   }
diff --git a/src/menu/components/editor/braft-editor/index.jsx b/src/menu/components/editor/braft-editor/index.jsx
index cac96be..cf83121 100644
--- a/src/menu/components/editor/braft-editor/index.jsx
+++ b/src/menu/components/editor/braft-editor/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover } from 'antd'
 
@@ -29,7 +28,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -86,7 +85,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -183,14 +182,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(BraftEditorComponent)
\ No newline at end of file
+export default BraftEditorComponent
\ 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
index c949c28..bccbfd8 100644
--- a/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
+++ b/src/menu/components/editor/braft-editor/wrapsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     wrap: null
   }
diff --git a/src/menu/components/form/dragtitle/card.jsx b/src/menu/components/form/dragtitle/card.jsx
new file mode 100644
index 0000000..48e07f4
--- /dev/null
+++ b/src/menu/components/form/dragtitle/card.jsx
@@ -0,0 +1,59 @@
+import React from 'react'
+import { useDrag, useDrop } from 'react-dnd'
+import { Icon, Popover } from 'antd'
+
+import './index.scss'
+
+const Card = ({ id, card, active, moveCard, findCard, editCard, closeCard, selectCard }) => {
+  const originalIndex = findCard(id).index
+  const [{ isDragging }, drag] = useDrag({
+    item: { type: 'form', id, originalIndex },
+    collect: monitor => ({
+      isDragging: monitor.isDragging(),
+    }),
+  })
+  const [, drop] = useDrop({
+    accept: 'form',
+    canDrop: () => true,
+    drop: (item) => {
+      const { id: draggedId, originalIndex } = item
+
+      if (originalIndex === undefined) {
+        item.dropTargetId = id
+      } else if (draggedId && draggedId !== id) {
+        const { index: overIndex } = findCard(id)
+        moveCard(draggedId, overIndex)
+      }
+    }
+  })
+  const opacity = isDragging ? 0 : 1
+
+  const edit = () => {
+    editCard(id)
+  }
+
+  const close = () => {
+    closeCard(id)
+  }
+
+  const select = () => {
+    selectCard(id)
+  }
+
+  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} />
+        <Icon className="close" type="close" onClick={close} />
+      </div>
+    } trigger="hover">
+      <div className={'page-card ' + (active ? 'active' : '')} onClick={select} style={{ opacity: opacity}}>
+        <div ref={node => drag(drop(node))}>
+          <span className="form-sort">{card.sort}</span>
+          {card.setting.title}
+        </div>
+      </div>
+    </Popover>
+  )
+}
+export default Card
diff --git a/src/menu/components/form/dragtitle/index.jsx b/src/menu/components/form/dragtitle/index.jsx
new file mode 100644
index 0000000..69ce1a4
--- /dev/null
+++ b/src/menu/components/form/dragtitle/index.jsx
@@ -0,0 +1,64 @@
+import React, { useState } from 'react'
+import { is, fromJS } from 'immutable'
+import update from 'immutability-helper'
+import Card from './card'
+import './index.scss'
+
+const Container = ({list, selectId, handleList, handleGroup, closeGroup, selectGroup}) => {
+  const [cards, setCards] = useState(list)
+  const moveCard = (id, atIndex) => {
+    const { card, index } = findCard(id)
+
+    if (!card) return
+
+    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
+
+    handleList(_cards)
+  }
+
+  if (!is(fromJS(cards), fromJS(list))) {
+    setCards(list)
+  }
+
+  const findCard = id => {
+    const card = cards.filter(c => `${c.uuid}` === id)[0]
+    return {
+      card,
+      index: cards.indexOf(card),
+    }
+  }
+
+  const editCard = id => {
+    const { card } = findCard(id)
+    handleGroup(card)
+  }
+
+  const closeCard = id => {
+    const { card } = findCard(id)
+    closeGroup(card)
+  }
+
+  const selectCard = id => {
+    const { card } = findCard(id)
+    selectGroup(card)
+  }
+
+  return (
+    <div className="normal-form-titles" >
+      {cards.map(card => (
+        <Card
+          id={card.uuid}
+          key={card.uuid}
+          active={card.uuid === selectId}
+          card={card}
+          moveCard={moveCard}
+          editCard={editCard}
+          closeCard={closeCard}
+          findCard={findCard}
+          selectCard={selectCard}
+        />
+      ))}
+    </div>
+  )
+}
+export default Container
diff --git a/src/menu/components/form/dragtitle/index.scss b/src/menu/components/form/dragtitle/index.scss
new file mode 100644
index 0000000..100acac
--- /dev/null
+++ b/src/menu/components/form/dragtitle/index.scss
@@ -0,0 +1,39 @@
+.normal-form-titles {
+  display: flex;
+  line-height: 30px;
+  min-height: 50px;
+  .page-card {
+    position: relative;
+    flex: 1;
+    text-align: center;
+    cursor: move;
+    .form-sort {
+      background: #d8d8d8;
+      display: block;
+      width: 20px;
+      height: 20px;
+      line-height: 20px;
+      border-radius: 20px;
+      text-align: center;
+      color: #ffffff;
+      margin: 10px auto 0px;
+      position: relative;
+      z-index: 1;
+    }
+  }
+  .page-card:not(:first-child)::before {
+    position: absolute;
+    content: ' ';
+    display: inline-block;
+    width: 100%;
+    height: 2px;
+    background: #d8d8d8;
+    left: -50%;
+    top: 18px;
+  }
+  .page-card.active {
+    .form-sort {
+      background: #1890ff;
+    }
+  }
+}
\ 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
new file mode 100644
index 0000000..454a16e
--- /dev/null
+++ b/src/menu/components/form/formaction/actionform/index.jsx
@@ -0,0 +1,367 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+// import { fromJS } from 'immutable'
+import { Form, Row, Col, Input, Select, Icon, Radio, Tooltip, InputNumber, Cascader } from 'antd'
+import { formRule } from '@/utils/option.js'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class ActionForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    formlist: PropTypes.any,     // 琛ㄥ崟淇℃伅
+    card: PropTypes.any,         // 鎸夐挳淇℃伅
+    inputSubmit: PropTypes.any   // 鍥炶溅鎻愪氦浜嬩欢
+  }
+
+  state = {
+    formlist: null,  // 琛ㄥ崟淇℃伅
+    interType: null, // 鎺ュ彛绫诲瀷锛氬唴閮ㄣ�佸閮�
+    procMode: null,  // 鍙傛暟鏂瑰紡
+  }
+
+  
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    let _intertype = card.intertype || 'system'  // 鎺ュ彛绫诲瀷
+    let _procMode = card.procMode || 'system'    // 鍙傛暟璇锋眰鏂瑰紡
+
+    let _options = this.getOptions(_intertype, _procMode)
+
+    this.setState({
+      interType: _intertype,
+      procMode: _procMode,
+      formlist: this.props.formlist.map(item => {
+        if (item.key === 'innerFunc' && _procMode === 'inner') {
+          item.required = true
+        }
+
+        item.hidden = !_options.includes(item.key)
+        return item
+      })
+    })
+  }
+
+  getOptions = (_intertype, _procMode) => {
+    const { card } = this.props
+
+    if (card.type === 'prev') {
+      return ['type', 'label']
+    } else if (card.type === 'next') {
+      return ['type', 'label', 'enable']
+    }
+    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open'] // 閫夐」鍒楄〃
+    
+    if (_intertype === 'custom') {
+      _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
+      if (_procMode === 'system') {
+        _options.push('sql', 'sqlType')
+      } else {
+        _options.push('innerFunc')
+      }
+    } else if (_intertype === 'outer') {
+      _options.push('innerFunc', 'sysInterface', 'interface', 'proInterface', 'outerFunc', 'callbackFunc')
+    } else if (_intertype === 'inner') {
+      _options.push('innerFunc')
+    } else {
+      _options.push('sql', 'sqlType')
+    }
+
+    return _options
+  }
+
+  /**
+   * @description 涓嬫媺鍒囨崲
+   * 1銆佹墦寮�鏂瑰紡鍒囨崲锛岄噸缃彲瑙佽〃鍗曞拰琛ㄥ崟鍊�
+   * 2銆佹樉绀轰綅缃垏鎹紝閲嶇疆閫夋嫨琛�
+   * 3銆佸垏鎹㈡爣绛剧被鍨嬶紝閲嶇疆鍙�夋爣绛�
+   */
+  optionChange = (key, value) => {
+    const { procMode } = this.state
+
+    if (key === 'intertype') {
+      let _options = this.getOptions(value, procMode)
+
+      this.setState({
+        interType: value,
+        formlist: this.state.formlist.map(item => {
+          item.hidden = !_options.includes(item.key)
+
+          if (item.key === 'interface') {
+            item.readonly = false
+          } else if (item.key === 'sysInterface') {
+            item.initVal = 'false'
+          }
+          return item
+        })
+      })
+    } else if (key === 'procMode') {
+      let _options = this.getOptions(this.state.interType, value)
+
+      this.setState({
+        procMode: value,
+        formlist: this.state.formlist.map(item => {
+          item.hidden = !_options.includes(item.key)
+
+          if (item.key === 'innerFunc') {
+            item.required = true
+          }
+          return item
+        })
+      })
+    } else if (key === 'sysInterface') {
+      if (value === 'true') {
+        this.props.form.setFieldsValue({
+          interface: window.GLOB.mainSystemApi || ''
+        })
+      }
+      this.setState({
+        formlist: this.state.formlist.map(item => {
+          if (item.key === 'interface' && value === 'true') {
+            item.readonly = true
+          } else if (item.key === 'interface') {
+            item.readonly = false
+          }
+
+          return item
+        })
+      })
+    }
+  }
+
+  handleSubmit = (e) => {
+    e.preventDefault()
+
+    if (this.props.inputSubmit) {
+      this.props.inputSubmit()
+    }
+  }
+
+  getFields() {
+    const { getFieldDecorator } = this.props.form
+    const fields = []
+
+    this.state.formlist.forEach((item, index) => {
+      if (item.hidden || item.forbid) return
+
+      if (item.type === 'text') { // 鏂囨湰鎼滅储
+        let _rules = []
+        if (item.key === 'innerFunc') {
+          let str = '^(' + item.fields.join('|') + ')'
+          let _patten = new RegExp(str + formRule.func.innerPattern + '$', 'g')
+          _rules = [{
+            pattern: _patten,
+            message: formRule.func.innerMessage
+          }, {
+            max: formRule.func.max,
+            message: formRule.func.maxMessage
+          }]
+        } else if (item.key === 'outerFunc' || item.key === 'callbackFunc') {
+          _rules = [{
+            pattern: formRule.func.pattern,
+            message: formRule.func.message
+          }, {
+            max: formRule.func.max,
+            message: formRule.func.maxMessage
+          }]
+        } else {
+          _rules = [{
+            max: formRule.input.max,
+            message: formRule.input.message
+          }]
+        }
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  },
+                  ..._rules
+                ]
+              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'tip') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.label}>
+              {item.initVal}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'number') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<InputNumber min={0} max={10000} precision={0} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'select') { // 涓嬫媺鎼滅储
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} 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 + '!'
+                  }
+                ]
+              })(
+                <Select
+                  showSearch
+                  filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  onChange={(value) => {this.optionChange(item.key, value)}}
+                  getPopupContainer={() => document.getElementById('winter')}
+                >
+                  {item.options.map((option, index) =>
+                    <Select.Option id={`${index}`} title={option.text} key={`${index}`} value={option.value}>
+                      {option.text}
+                    </Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'radio') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
+              })(
+                <Radio.Group onChange={(e) => {this.optionChange(item.key, e.target.value)}} disabled={item.readonly}>
+                  {
+                    item.options.map(option => {
+                      return (
+                        <Radio key={option.value} value={option.value}>{option.text}</Radio>
+                      )
+                    })
+                  }
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'textarea') {
+        fields.push(
+          <Col span={24} key={index}>
+            <Form.Item label={item.label} className="textarea">
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal,
+                rules: [
+                  {
+                    required: item.readonly ? false : !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={2} readOnly={item.readonly}/>)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'cascader') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} 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 + '!'
+                  }
+                ]
+              })(
+                <Cascader options={item.options || []} expandTrigger="hover" placeholder=""/>
+              )}
+            </Form.Item>
+          </Col>
+        )
+      }
+    })
+    return fields
+  }
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  render() {
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 7 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 17 }
+      }
+    }
+    return (
+      <Form {...formItemLayout} className="menu-action-list-form" id="winter">
+        <Row gutter={24}>{this.getFields()}</Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(ActionForm)
\ No newline at end of file
diff --git a/src/menu/components/form/formaction/actionform/index.scss b/src/menu/components/form/formaction/actionform/index.scss
new file mode 100644
index 0000000..535dd29
--- /dev/null
+++ b/src/menu/components/form/formaction/actionform/index.scss
@@ -0,0 +1,47 @@
+.menu-action-list-form {
+  min-height: 190px;
+  .superconfig {
+    color: #1890ff;
+    cursor: pointer;
+  }
+  .textarea {
+    .ant-col-sm-7 {
+      width: 14%;
+    }
+    .ant-col-sm-17 {
+      width: 86%;
+    }
+  }
+  .ant-radio-group {
+    white-space: nowrap;
+    .ant-radio-wrapper {
+      margin-right: 4px;
+    }
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .anticon-question-circle {
+    color: #c49f47;
+    position: relative;
+    left: -3px;
+  }
+  .with-button {
+    .ant-form-item-control-wrapper {
+      padding-right: 63px;
+    }
+    .ant-btn {
+      position: absolute;
+      right: 12px;
+      top: 4.5px;
+    }
+  }
+  .ant-input:read-only {
+    background: #fafafa;
+    resize: none;
+  }
+  .ant-input:read-only:hover, .ant-input:read-only:focus {
+    border-color: #d9d9d9;
+    box-shadow: none;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/form/formaction/formconfig.jsx b/src/menu/components/form/formaction/formconfig.jsx
new file mode 100644
index 0000000..1a84804
--- /dev/null
+++ b/src/menu/components/form/formaction/formconfig.jsx
@@ -0,0 +1,292 @@
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+
+/**
+ * @description 鑾峰彇琛ㄥ崟鎸夐挳閰嶇疆淇℃伅
+ * @param {*} card           缂栬緫鎸夐挳
+ * @param {*} type           鎸夐挳绫诲瀷锛岀敤浜庡尯鍒嗗彲閫夌殑鎵撳紑鏂瑰紡
+ */
+
+export function getActionForm (card, functip, tableName, usefulFields, modules) {
+  const isApp = sessionStorage.getItem('appType') === 'pc'
+  let _type = '鎻愪氦'
+  if (card.type === 'prev') {
+    _type = '涓婁竴姝�'
+  } else if (card.type === 'next') {
+    _type = '涓嬩竴姝�'
+  }
+
+  let menulist = []
+  if (isApp) {
+    menulist = sessionStorage.getItem('appMenus')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+        menulist = menulist.map(item => ({value: item.MenuID, text: item.MenuName}))
+      } catch {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+    menulist.unshift({value: '', text: '鏃�'})
+  } else {
+    menulist = sessionStorage.getItem('fstMenuList')
+    if (menulist) {
+      try {
+        menulist = JSON.parse(menulist)
+      } catch {
+        menulist = []
+      }
+    } else {
+      menulist = []
+    }
+  }
+
+  return [
+    {
+      type: 'tip',
+      key: 'type',
+      label: '鎸夐挳绫诲瀷',
+      initVal: _type
+    },
+    {
+      type: 'text',
+      key: 'label',
+      label: '鎸夐挳鍚嶇О',
+      initVal: card.label,
+      required: true,
+      readonly: false
+    },
+    {
+      type: 'radio',
+      key: 'intertype',
+      label: Formdict['header.form.intertype'],
+      initVal: card.intertype || 'system',
+      required: true,
+      options: [{
+        value: 'system',
+        text: Formdict['model.interface.system']
+      }, {
+        value: 'inner',
+        text: Formdict['model.interface.inner']
+      }, {
+        value: 'outer',
+        text: Formdict['model.interface.outer']
+      }, {
+        value: 'custom',
+        text: '鑷畾涔�'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'procMode',
+      label: '鍙傛暟澶勭悊',
+      initVal: card.procMode || 'system',
+      required: true,
+      options: [{
+        value: 'system',
+        text: '绯荤粺鍑芥暟'
+      }, {
+        value: 'inner',
+        text: '鍐呴儴鍑芥暟'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'sqlType',
+      label: Formdict['header.form.action.type'],
+      initVal: card.sqlType || 'update',
+      required: true,
+      options: [{
+        value: 'insert',
+        text: Formdict['header.form.action.insert']
+      }, {
+        value: 'update',
+        text: Formdict['header.form.action.update']
+      }, {
+        value: 'audit',
+        text: Formdict['header.form.action.audit']
+      }]
+    },
+    {
+      type: 'text',
+      key: 'sql',
+      label: Formdict['model.form.tablename'],
+      initVal: card.sql || tableName || '',
+      required: true
+    },
+    {
+      type: 'text',
+      key: 'innerFunc',
+      label: Formdict['header.form.innerFunc'],
+      initVal: card.innerFunc || '',
+      tooltip: functip,
+      fields: usefulFields,
+      tooltipClass: 'middle',
+      required: card.intertype === 'inner',
+      readonly: false
+    },
+    {
+      type: 'select',
+      key: 'linkTab',
+      label: '鍏宠仈鏍囩',
+      initVal: card.linkTab || '',
+      required: false,
+      options: []
+    },
+    {
+      type: 'text',
+      key: 'url',
+      label: Formdict['model.pageUrl'],
+      initVal: card.url || '',
+      required: true
+    },
+    {
+      type: 'radio',
+      key: 'sysInterface',
+      label: Formdict['header.form.sysInterface'],
+      initVal: card.sysInterface || 'false',
+      required: true,
+      options: [{
+        value: 'true',
+        text: Formdict['model.true']
+      }, {
+        value: 'false',
+        text: Formdict['model.false']
+      }]
+    },
+    {
+      type: 'text',
+      key: 'outerFunc',
+      label: Formdict['header.form.outerFunc'],
+      initVal: card.outerFunc || '',
+      required: false,
+      readonly: false
+    },
+    {
+      type: 'textarea',
+      key: 'interface',
+      label: '娴嬭瘯鍦板潃',
+      initVal: card.sysInterface === 'true' ? (window.GLOB.mainSystemApi || '') : (card.interface || ''),
+      required: true,
+      readonly: card.sysInterface === 'true'
+    },
+    {
+      type: 'textarea',
+      key: 'proInterface',
+      label: '姝e紡鍦板潃',
+      initVal: card.proInterface || '',
+      tooltip: '姝e紡绯荤粺鎵�浣跨敤鐨勬帴鍙e湴鍧�銆�',
+      required: false
+    },
+    {
+      type: 'radio',
+      key: 'method',
+      label: '璇锋眰鏂瑰紡',
+      initVal: card.method || 'post',
+      required: true,
+      options: [{
+        value: 'get',
+        text: 'GET'
+      }, {
+        value: 'post',
+        text: 'POST'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'cross',
+      label: '璺ㄥ煙璇锋眰',
+      initVal: card.cross || 'true',
+      tooltip: '濡傛灉鑷畾涔夋帴鍙d笉鏀寔璺ㄥ煙璇锋眰锛屼細閫氳繃褰撳墠绯荤粺杞彂銆�',
+      required: false,
+      options: [{
+        value: 'true',
+        text: '鏀寔'
+      }, {
+        value: 'false',
+        text: '涓嶆敮鎸�'
+      }]
+    },
+    {
+      type: 'radio',
+      key: 'callbackType',
+      label: '鍥炶皟鏂瑰紡',
+      initVal: card.callbackType || 'script',
+      tooltip: '浣跨敤鍚庡彴鑴氭湰鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
+      required: true,
+      options: [{
+        value: 'script',
+        text: '鑷畾涔夎剼鏈�'
+      }, {
+        value: 'default',
+        text: '鍚庡彴鑴氭湰'
+      }]
+    },
+    {
+      type: 'text',
+      key: 'cbTable',
+      label: '鍥炶皟琛ㄥ悕',
+      initVal: card.cbTable || '',
+      required: true
+    },
+    {
+      type: 'text',
+      key: 'callbackFunc',
+      label: Formdict['header.form.callbackFunc'],
+      initVal: card.callbackFunc || '',
+      required: false,
+      readonly: false
+    },
+    {
+      type: isApp ? 'select' : 'cascader',
+      key: 'linkmenu',
+      label: '鎵撳紑鑿滃崟',
+      tooltip: '鎵ц鎴愬姛鍚庨渶瑕佹墦寮�鐨勮彍鍗曘��',
+      initVal: card.linkmenu,
+      required: false,
+      options: menulist
+    },
+    {
+      type: 'radio',
+      key: 'open',
+      label: '鎵撳紑鏂瑰紡',
+      initVal: card.open || 'blank',
+      required: false,
+      forbid: !isApp,
+      options: [{
+        value: 'blank',
+        text: '鏂扮獥鍙�'
+      }, {
+        value: 'self',
+        text: '褰撳墠绐楀彛'
+      }]
+    },
+    {
+      type: 'cascader',
+      key: 'syncComponent',
+      label: '鍚屾鍒锋柊',
+      initVal: card.syncComponent,
+      tooltip: '鎵ц鎴愬姛鍚庨渶瑕佸埛鏂扮殑缁勪欢銆�',
+      required: false,
+      options: modules
+    },
+    {
+      type: 'radio',
+      key: 'enable',
+      label: '鏄惁鏄剧ず',
+      initVal: card.enable || 'false',
+      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
new file mode 100644
index 0000000..fcbdb30
--- /dev/null
+++ b/src/menu/components/form/formaction/index.jsx
@@ -0,0 +1,253 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Modal, Button, Popover, Icon } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import asyncComponent from '@/utils/asyncComponent'
+import { getActionForm } from './formconfig'
+
+import MKEmitter from '@/utils/events.js'
+import MenuUtils from '@/utils/utils-custom.js'
+import './index.scss'
+
+const ActionForm = asyncComponent(() => import('./actionform'))
+const VerifyCard = asyncComponent(() => import('@/templates/zshare/verifycard'))
+
+class CardCellComponent extends Component {
+  static propTpyes = {
+    group: PropTypes.object,         // 鍒嗙粍淇℃伅
+    updateconfig: PropTypes.func     // 鑿滃崟閰嶇疆鏇存柊
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    card: null,          // 缂栬緫涓厓绱�
+    formlist: null,      // 琛ㄥ崟淇℃伅
+    visible: false,      // 妯℃�佹鎺у埗
+    profVisible: false,  // 楠岃瘉淇℃伅缂栬緫
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props.group), fromJS(nextProps.group)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+  }
+
+  handleStyle = (element) => {
+    const { group } = this.props
+
+    let _style = element.style ? fromJS(element.style).toJS() : {}
+    let options = ['font', 'border', 'padding', 'margin', 'backgroundColor']
+
+    this.setState({
+      card: element
+    })
+
+    MKEmitter.emit('changeStyle', [group.uuid, element.type], options, _style)
+  }
+
+  getStyle = (comIds, style) => {
+    let group = fromJS(this.props.group).toJS()
+
+    if (comIds.length !== 2 || comIds[0] !== group.uuid) return
+
+    if (comIds[1] === 'prev') {
+      group.prevButton.style = style
+    } else if (comIds[1] === 'submit') {
+      group.subButton.style = style
+    } else if (comIds[1] === 'next') {
+      group.nextButton.style = style
+    }
+
+    this.props.updateconfig(group)
+  }
+
+  /**
+   * @description 鎸夐挳缂栬緫锛岃幏鍙栨寜閽〃鍗曚俊鎭�
+   */
+  handleAction = (card) => {
+    const { config } = this.props
+
+    let usefulFields = sessionStorage.getItem('permFuncField')
+    if (usefulFields) {
+      try {
+        usefulFields = JSON.parse(usefulFields)
+      } catch {
+        usefulFields = []
+      }
+    } else {
+      usefulFields = []
+    }
+
+    let ableField = usefulFields.join(', ')
+    let functip = <div>
+      <p style={{marginBottom: '5px'}}>{this.state.dict['model.tooltip.func.innerface'].replace('@ableField', ableField)}</p>
+    </div>
+
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid)
+
+    this.setState({
+      visible: true,
+      card: card,
+      formlist: getActionForm(card, functip, config.setting.tableName, usefulFields, modules)
+    })
+  }
+
+  /**
+   * @description 鍙栨秷淇濆瓨锛屽鏋滃厓绱犱负鏂版坊鍏冪礌锛屽垯浠庡簭鍒椾腑鍒犻櫎
+   */
+  editModalCancel = () => {
+    this.setState({
+      card: null,
+      visible: false
+    })
+  }
+
+  /**
+   * @description 鍏冪礌淇敼鍚庢彁浜や繚瀛�
+   */
+  handleActionSubmit = () => {
+    const { card } = this.state
+
+    this.actionFormRef.handleConfirm().then(res => {
+      res.type = card.type
+      res.style = card.style || null
+      if (card.verify) {
+        res.verify = card.verify
+      }
+
+      let group = fromJS(this.props.group).toJS()
+
+      if (res.type === 'prev') {
+        group.prevButton = res
+      } else if (res.type === 'submit') {
+        group.subButton = res
+      } else if (res.type === 'next') {
+        group.nextButton = res
+      }
+
+      this.setState({
+        visible: false
+      })
+      this.props.updateconfig(group)
+    })
+  }
+
+  /**
+   * @description 楠岃瘉淇℃伅閰嶇疆
+   */
+  profileAction = () => {
+    this.setState({
+      profVisible: true
+    })
+  }
+
+  /**
+   * @description 楠岃瘉淇℃伅淇濆瓨
+   */
+  verifySubmit = () => {
+    this.verifyRef.handleConfirm().then(res => {
+      let group = fromJS(this.props.group).toJS()
+
+      group.subButton.verify = res
+
+      this.setState({
+        profVisible: false
+      })
+      this.props.updateconfig(group)
+    })
+  }
+
+  render() {
+    const { group, config } = this.props
+    const { visible, profVisible, card, dict } = this.state
+
+    return (
+      <div className="mk-form-action">
+        {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" style={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">
+            <Icon className="edit" title="缂栬緫" type="edit" onClick={() => this.handleAction(group.subButton)} />
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={() => this.handleStyle(group.subButton)} type="font-colors" />
+            <Icon className="profile" title="setting" type="profile" onClick={() => this.profileAction()} />
+          </div>
+        } trigger="hover">
+          <Button type="link" className="submit mk-primary" style={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={
+          <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={group.nextButton.style}>{group.nextButton.label}</Button>
+        </Popover> : null}
+        {/* 缂栬緫鎸夐挳锛氬鍒躲�佺紪杈� */}
+        <Modal
+          title={dict['model.edit']}
+          visible={visible}
+          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}
+            card={card}
+            setting={config.setting}
+            formlist={this.state.formlist}
+            inputSubmit={this.handleActionSubmit}
+            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
+        >
+          <VerifyCard
+            card={{...group.subButton, modal: {fields: group.fields}}}
+            dict={dict}
+            config={config}
+            columns={config.columns}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default CardCellComponent
\ No newline at end of file
diff --git a/src/menu/components/form/formaction/index.scss b/src/menu/components/form/formaction/index.scss
new file mode 100644
index 0000000..1fa18b5
--- /dev/null
+++ b/src/menu/components/form/formaction/index.scss
@@ -0,0 +1,21 @@
+.mk-form-action {
+  position: relative;
+  text-align: center;
+  padding-bottom: 10px;
+
+  .prev {
+    margin-right: 15px;
+  }
+  .submit {
+    border: none;
+  }
+  .skip {
+    position: absolute;
+    right: 5px;
+  }
+  .skip:not(.true) {
+    span {
+      text-decoration: line-through;
+    }
+  }
+}
diff --git a/src/menu/components/form/normal-form/groupform/index.jsx b/src/menu/components/form/normal-form/groupform/index.jsx
new file mode 100644
index 0000000..c1611f9
--- /dev/null
+++ b/src/menu/components/form/normal-form/groupform/index.jsx
@@ -0,0 +1,144 @@
+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
+    let fields = []
+
+    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 } = 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>
+          <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>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/templates/modalconfig/groupform/index.scss b/src/menu/components/form/normal-form/groupform/index.scss
similarity index 71%
rename from src/templates/modalconfig/groupform/index.scss
rename to src/menu/components/form/normal-form/groupform/index.scss
index 091801f..9a74987 100644
--- a/src/templates/modalconfig/groupform/index.scss
+++ b/src/menu/components/form/normal-form/groupform/index.scss
@@ -10,4 +10,9 @@
   .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
new file mode 100644
index 0000000..1610711
--- /dev/null
+++ b/src/menu/components/form/normal-form/index.jsx
@@ -0,0 +1,704 @@
+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 ModalForm from '@/templates/zshare/modalform'
+import MKEmitter from '@/utils/events.js'
+import Utils from '@/utils/utils.js'
+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('@/menu/components/form/wrapsetting'))
+const CardComponent = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
+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'))
+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,
+    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', color: '#1890ff' },
+        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
+        columns: [],
+        scripts: [],
+        subcards: [{
+          uuid: Utils.getuuid(),
+          setting: {title: '绗竴姝�', align: 'left_right'},
+          sort: 1,
+          style: {},
+          fields: [],
+          prevButton: {label: '涓婁竴姝�', type: 'prev'},
+          subButton: {label: '鎻愪氦', type: 'submit', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
+          nextButton: {label: '璺宠繃', type: 'next', enable: 'false'}
+        }]
+      }
+
+      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)
+  }
+
+  /**
+   * @description 鍗曚釜鍗$墖淇℃伅鏇存柊
+   */
+  deleteCard = (cell) => {
+    let card = fromJS(this.state.card).toJS()
+    let _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎琛ㄥ崟鍚楋紵',
+      onOk() {
+        card.subcards = card.subcards.filter(item => item.uuid !== cell.uuid)
+        
+        let uuids = []
+        cell.elements && cell.elements.forEach(c => {
+          if (c.eleType === 'button') {
+            uuids.push(c.uuid)
+          }
+        })
+        MKEmitter.emit('delButtons', uuids)
+
+        _this.setState({card})
+        _this.props.updateConfig(card)
+      },
+      onCancel() {}
+    })
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], 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: [],
+      prevButton: {label: '涓婁竴姝�', type: 'prev'},
+      subButton: {label: '鎻愪氦', type: 'submit', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
+      nextButton: {label: '璺宠繃', type: 'next', enable: 'false'}
+    }
+
+    card.subcards.push(newcard)
+    
+    this.setState({
+      card,
+      group: newcard,
+      groupvisible: true
+    })
+    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
+    })
+  }
+
+  changeGroup = (item) => {
+    this.setState({
+      group: item,
+      groupvisible: true
+    })
+  }
+
+  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)
+  }
+
+  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()
+    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) => {
+    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})
+    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 = () => {
+    let group = fromJS(this.state.group).toJS()
+    let lastItem = group.fields[group.fields.length - 1]
+    let span = lastItem ? lastItem.span : 12
+
+    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})
+  }
+
+  /**
+   * @description 琛ㄥ崟缂栬緫
+   */
+  handleForm = (_item) => {
+    const { card, group } = 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')
+    _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 (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') 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 (!_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 ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && 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)
+      }
+    })
+  }
+
+  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 } = this.state
+
+    return (
+      <div className="menu-normal-form-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">
+            <Icon className="plus" title="娣诲姞鍒嗙粍" onClick={this.addCard} type="plus" />
+            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <CopyComponent type="propcard" card={card}/>
+            <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
+            <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}
+          selectId={group ? group.uuid : ''}
+          handleList={this.changecards}
+          handleGroup={this.changeGroup}
+          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})} />
+          <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1鍒�</Button>
+          <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2鍒�</Button>
+          <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3鍒�</Button>
+          <Button className="mk-cols-change" onClick={() => this.changecols(4)}>4鍒�</Button>
+          <div style={{clear: 'both'}}></div>
+          <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}
+          />
+          <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}
+          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/normal-form/index.scss b/src/menu/components/form/normal-form/index.scss
new file mode 100644
index 0000000..0fb6111
--- /dev/null
+++ b/src/menu/components/form/normal-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
new file mode 100644
index 0000000..c159968
--- /dev/null
+++ b/src/menu/components/form/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+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
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/menu/components/form/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.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
new file mode 100644
index 0000000..78d7589
--- /dev/null
+++ b/src/menu/components/form/wrapsetting/settingform/index.jsx
@@ -0,0 +1,188 @@
+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('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
new file mode 100644
index 0000000..a85d1ac
--- /dev/null
+++ b/src/menu/components/form/wrapsetting/settingform/index.scss
@@ -0,0 +1,14 @@
+.model-menu-setting-form {
+  position: relative;
+
+  .anticon-question-circle {
+    color: #c49f47;
+    margin-right: 3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .color-sketch-block {
+    margin-top: 6px;
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/group/groupsetting/index.jsx b/src/menu/components/group/groupsetting/index.jsx
index 7cb078c..fc8bca0 100644
--- a/src/menu/components/group/groupsetting/index.jsx
+++ b/src/menu/components/group/groupsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     setting: null
   }
diff --git a/src/menu/components/group/normal-group/index.jsx b/src/menu/components/group/normal-group/index.jsx
index 460e3a2..c6021fa 100644
--- a/src/menu/components/group/normal-group/index.jsx
+++ b/src/menu/components/group/normal-group/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, Button } from 'antd'
 
@@ -25,7 +24,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     group: null,
     editab: null,
   }
@@ -183,14 +182,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(NormalGroup)
\ No newline at end of file
+export default NormalGroup
\ No newline at end of file
diff --git a/src/menu/components/search/main-search/index.jsx b/src/menu/components/search/main-search/index.jsx
index b876a49..0ab9015 100644
--- a/src/menu/components/search/main-search/index.jsx
+++ b/src/menu/components/search/main-search/index.jsx
@@ -1,6 +1,5 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Modal, notification, Popover, Icon } from 'antd'
 import moment from 'moment'
@@ -31,7 +30,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     searchlist: null,    // 鎼滅储鏉′欢闆�
     sqlVerifing: false,  // sql楠岃瘉涓�
     visible: false,      // 妯℃�佹鎺у埗
@@ -394,14 +393,5 @@
     )
   }
 }
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
 
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MainSearchComponent)
\ No newline at end of file
+export default MainSearchComponent
\ No newline at end of file
diff --git a/src/menu/components/search/main-search/wrapsetting/index.jsx b/src/menu/components/search/main-search/wrapsetting/index.jsx
index 08c3f72..d8bbaa6 100644
--- a/src/menu/components/search/main-search/wrapsetting/index.jsx
+++ b/src/menu/components/search/main-search/wrapsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     wrap: null
   }
diff --git a/src/menu/components/share/actioncomponent/actionform/index.jsx b/src/menu/components/share/actioncomponent/actionform/index.jsx
index e584aea..f7494bf 100644
--- a/src/menu/components/share/actioncomponent/actionform/index.jsx
+++ b/src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -15,11 +15,11 @@
   excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent', 'resetPageIndex', 'pagination', 'search', 'width'],
   popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'popClose', 'resetPageIndex', 'width'],
   tab: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'linkmenu', 'width'],
-  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'icon', 'class', 'width'],
+  innerpage: ['label', 'Ot', 'OpenType', 'pageTemplate', 'show', 'icon', 'class', 'width', 'open'],
   funcbutton: ['label', 'OpenType', 'funcType', 'show', 'icon', 'class', 'width']
 }
 
-class MainSearch extends Component {
+class ActionForm extends Component {
   static propTpyes = {
     dict: PropTypes.object,      // 瀛楀吀椤�
     type: PropTypes.any,         // type涓�"card"鏃讹紝鍙彲閫夊崟琛屾垨涓嶉�夎
@@ -89,8 +89,8 @@
 
     let _opentype = card.OpenType                // 鎵撳紑鏂瑰紡
     let _intertype = card.intertype || 'system'  // 鎺ュ彛绫诲瀷
-    let _funcType = card.funcType || 'print'     // 鍔熻兘鎸夐挳榛樿绫诲瀷
-    let _procMode = card.procMode || 'system'     // 鍙傛暟璇锋眰鏂瑰紡
+    let _funcType = card.funcType || ''          // 鍔熻兘鎸夐挳榛樿绫诲瀷
+    let _procMode = card.procMode || 'system'    // 鍙傛暟璇锋眰鏂瑰紡
 
     let _options = this.getOptions(_opentype, _intertype, _funcType, card.pageTemplate, _procMode)
 
@@ -114,7 +114,7 @@
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
           } else if (card.pageTemplate === 'pay') { // 琛岀骇鎸夐挳銆佹敮浠樻寜閽紝鍙兘閫夊崟琛�
             item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
-          } else if (['innerpage', 'blank', 'tab', 'popview', 'excelIn'].includes(_opentype)) {
+          } else if (['innerpage', 'tab', 'popview', 'excelIn'].includes(_opentype)) {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
           } else if (card.sqlType === 'insert') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
@@ -143,6 +143,10 @@
     if (_opentype === 'innerpage') {         // 鏂伴〉闈紝鍙�夋ā鏉�(鑷畾涔夋椂锛屽彲濉叆澶栭儴閾炬帴)
       if (_pageTemplate === 'custom') {
         _options.push('url', 'joint')
+      } else if (_pageTemplate === 'page') {
+        _options.push('copyMenuId', 'joint')
+      } else if (_pageTemplate === 'linkpage') {
+        _options.push('linkmenu', 'joint')
       }
     } else if (_opentype === 'excelOut') {    // 瀵煎叆瀵煎嚭
       if (_intertype === 'outer') {
@@ -167,7 +171,7 @@
       }
     } else if (_opentype !== 'popview' && _opentype !== 'tab') {
       if (_intertype === 'custom') {
-        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method')
+        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
         if (_procMode === 'system') {
           _options.push('sql', 'sqlType')
         } else {
@@ -228,7 +232,7 @@
         } else if (item.key === 'Ot') {
           if (type === 'card') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
-          } else if (['innerpage', 'blank', 'tab', 'popview'].includes(value)) {
+          } else if (['innerpage', 'tab', 'popview'].includes(value)) {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
             _fieldval.Ot = 'requiredSgl'
           } else if (value === 'excelIn') {
@@ -266,7 +270,7 @@
         this.props.form.setFieldsValue(_fieldval)
       })
     } else if (key === 'funcType') {
-      let _options = this.getOptions(this.state.openType, this.state.interType, value, card.pageTemplate, procMode)
+      let _options = this.getOptions(openType, this.state.interType, value, card.pageTemplate, procMode)
       let _fieldval = {}
 
       this.setState({
@@ -330,10 +334,9 @@
       })
     } else if (key === 'pageTemplate') {
       let _fieldval = {}
-      let _options = this.getOptions(this.state.openType, this.state.interType, this.state.funcType, value, procMode)
+      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, value, procMode)
 
       this.setState({
-        openType: value,
         formlist: this.state.formlist.map(item => {
           item.hidden = !_options.includes(item.key)
 
@@ -471,6 +474,14 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'tip') {
+        fields.push(
+          <Col span={12} key={index}>
+            <Form.Item label={item.label}>
+              {item}
+            </Form.Item>
+          </Col>
+        )
       } else if (item.type === 'number') {
         fields.push(
           <Col span={12} key={index}>
@@ -529,7 +540,12 @@
       } else if (item.type === 'radio') {
         fields.push(
           <Col span={12} key={index}>
-            <Form.Item label={item.label}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
               {getFieldDecorator(item.key, {
                 initialValue: item.initVal,
                 rules: [
@@ -604,13 +620,13 @@
   }
 
   handleConfirm = () => {
-    const { setting } = this.props
+    const { setting, card } = this.props
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     return new Promise((resolve, reject) => {
       this.props.form.validateFieldsAndScroll((err, values) => {
         if (!err) {
-          values.uuid = this.props.card.uuid
-          values.verify = this.props.card.verify || null
+          values.uuid = card.uuid
+          values.verify = card.verify || null
 
           if (values.show === 'icon' && !values.icon) {
             notification.warning({
@@ -631,8 +647,15 @@
             
             values.Ot = 'notRequired'
           } else if (['pop', 'prompt', 'exec'].includes(values.OpenType) && values.verify) {
-            if ((values.Ot === 'requiredOnce' || this.props.card.Ot === 'requiredOnce') && this.props.card.Ot !== values.Ot) {
+            if ((values.Ot === 'requiredOnce' || card.Ot === 'requiredOnce') && card.Ot !== values.Ot) {
               values.verify.uniques = []
+            }
+            if (card.Ot !== values.Ot) {
+              if (values.Ot === 'notRequired') {
+                values.verify.invalid = 'false'
+              } else if (card.Ot === 'notRequired' && values.Ot !== 'notRequired') {
+                values.verify.invalid = 'true'
+              }
             }
           }
           
@@ -663,4 +686,4 @@
   }
 }
 
-export default Form.create()(MainSearch)
\ No newline at end of file
+export default Form.create()(ActionForm)
\ No newline at end of file
diff --git a/src/menu/components/share/actioncomponent/formconfig.jsx b/src/menu/components/share/actioncomponent/formconfig.jsx
index ce7f6d2..6d9fab1 100644
--- a/src/menu/components/share/actioncomponent/formconfig.jsx
+++ b/src/menu/components/share/actioncomponent/formconfig.jsx
@@ -1,7 +1,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鎸夐挳琛ㄥ崟閰嶇疆淇℃伅
@@ -12,6 +12,7 @@
  * @param {*} type           鎸夐挳绫诲瀷锛岀敤浜庡尯鍒嗗彲閫夌殑鎵撳紑鏂瑰紡
  */
 export function getActionForm (card, functip, setting, usefulFields, type, menulist = [], modules = []) {
+  let appMenus = []
   let opentypes = [
     {
       value: 'pop',
@@ -43,6 +44,43 @@
     }
   ]
 
+  let pageTemps = [
+    { value: 'billprint', text: '鍗曟嵁鎵撳嵃' },
+    { value: 'pay', text: Formdict['model.pay'] },
+    { value: 'custom', text: Formdict['header.form.custom'] }
+  ]
+  const isApp = sessionStorage.getItem('appType') === 'pc'
+
+  let funTypes = [
+    { value: 'changeuser', text: Formdict['header.form.func.changeuser'] },
+    { value: 'print', text: '鏍囩鎵撳嵃' }
+  ]
+  
+  if (isApp) {
+    opentypes = opentypes.filter(item => item.value !== 'tab')
+    pageTemps = [
+      { value: 'page', text: '鑿滃崟' },
+      { value: 'linkpage', text: '鍏宠仈鑿滃崟' },
+      { value: 'billprint', text: '鍗曟嵁鎵撳嵃' },
+      { value: 'pay', text: Formdict['model.pay'] },
+      { value: 'custom', text: '閾炬帴' }
+    ]
+    funTypes = [
+      { value: 'changeuser', text: Formdict['header.form.func.changeuser'] },
+    ]
+    appMenus = sessionStorage.getItem('appMenus')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+        appMenus = appMenus.map(item => ({value: item.MenuID, text: item.MenuName}))
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
+  }
+  
   if (type === 'chart') {
     opentypes = opentypes.filter(item => item.value === 'excelIn' || item.value === 'excelOut')
   }
@@ -69,15 +107,9 @@
       type: 'radio',
       key: 'funcType',
       label: Formdict['header.form.funcType'],
-      initVal: card.funcType || 'print',
+      initVal: card.funcType || (isApp ? 'changeuser' : ''),
       required: true,
-      options: [{
-        value: 'changeuser',
-        text: Formdict['header.form.func.changeuser']
-      }, {
-        value: 'print',
-        text: '鏍囩鎵撳嵃'
-      }]
+      options: funTypes
     },
     {
       type: 'select',
@@ -158,21 +190,43 @@
       label: Formdict['model.form.newpage.type'],
       initVal: card.pageTemplate || '',
       required: true,
+      options: pageTemps
+    },
+    {
+      type: 'radio',
+      key: 'open',
+      label: '閾炬帴鏂瑰紡',
+      initVal: card.open || 'blank',
+      required: true,
+      forbid: !isApp,
       options: [{
-        value: 'billprint',
-        text: '鍗曟嵁鎵撳嵃'
+        value: 'blank',
+        text: '鏂扮獥鍙�'
       }, {
-        value: 'pay',
-        text: Formdict['model.pay']
-      }, {
-        value: 'custom',
-        text: Formdict['header.form.custom']
+        value: 'self',
+        text: '褰撳墠绐楀彛'
       }]
     },
     {
-      type: 'text',
+      type: 'select',
+      key: 'linkmenu',
+      label: '鍏宠仈鑿滃崟',
+      initVal: card.linkmenu || '',
+      required: true,
+      options: appMenus
+    },
+    {
+      type: 'select',
+      key: 'copyMenuId',
+      label: '澶嶅埗鑿滃崟',
+      initVal: card.copyMenuId || '',
+      required: false,
+      options: appMenus
+    },
+    {
+      type: 'textarea',
       key: 'url',
-      label: Formdict['model.form.newpage.url'],
+      label: Formdict['model.pageUrl'],
       initVal: card.url || '',
       required: true
     },
@@ -230,17 +284,32 @@
     },
     {
       type: 'radio',
+      key: 'cross',
+      label: '璺ㄥ煙璇锋眰',
+      initVal: card.cross || 'true',
+      tooltip: '濡傛灉鑷畾涔夋帴鍙d笉鏀寔璺ㄥ煙璇锋眰锛屼細閫氳繃褰撳墠绯荤粺杞彂銆�',
+      required: false,
+      options: [{
+        value: 'true',
+        text: '鏀寔'
+      }, {
+        value: 'false',
+        text: '涓嶆敮鎸�'
+      }]
+    },
+    {
+      type: 'radio',
       key: 'callbackType',
       label: '鍥炶皟鏂瑰紡',
       initVal: card.callbackType || 'script',
-      tooltip: '浣跨敤榛樿鏂瑰紡鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
+      tooltip: '浣跨敤鍚庡彴鑴氭湰鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
       required: true,
       options: [{
-        value: 'default',
-        text: '榛樿鑴氭湰'
-      }, {
         value: 'script',
         text: '鑷畾涔夎剼鏈�'
+      }, {
+        value: 'default',
+        text: '鍚庡彴鑴氭湰'
       }]
     },
     {
@@ -273,6 +342,7 @@
       label: Formdict['model.form.linkmenu'],
       initVal: card.linkmenu || [],
       required: true,
+      forbid: isApp,
       options: menulist
     },
     {
diff --git a/src/menu/components/share/actioncomponent/index.jsx b/src/menu/components/share/actioncomponent/index.jsx
index 36e5f09..c6964ec 100644
--- a/src/menu/components/share/actioncomponent/index.jsx
+++ b/src/menu/components/share/actioncomponent/index.jsx
@@ -1,11 +1,9 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Modal, notification, Button } from 'antd'
 
-import Utils from '@/utils/utils.js'
-import DevUtils from '@/utils/devutils.js'
+import Utils, { FuncUtils } from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import { getActionForm } from './formconfig'
@@ -13,7 +11,7 @@
 
 import MKEmitter from '@/utils/events.js'
 import ActionForm from './actionform'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import CreateFunc from '@/templates/zshare/createfunc'
 import DragElement from './dragaction'
 import './index.scss'
@@ -33,7 +31,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,          // 缂栬緫涓厓绱�
     formlist: null,      // 琛ㄥ崟淇℃伅
     actionlist: null,    // 鎸夐挳缁�
@@ -143,7 +141,7 @@
    * @description 鎸夐挳缂栬緫锛岃幏鍙栨寜閽〃鍗曚俊鎭�
    */
   handleAction = (card) => {
-    const { menu, config } = this.props
+    const { config } = this.props
 
     let usefulFields = sessionStorage.getItem('permFuncField')
     if (usefulFields) {
@@ -172,7 +170,7 @@
       menulist = []
     }
     
-    let modules = MenuUtils.getSubModules(menu.components, config.uuid)
+    let modules = MenuUtils.getSubModules(window.GLOB.customMenu.components, config.uuid)
 
     this.setState({
       visible: true,
@@ -334,7 +332,7 @@
    * @description 鍒涘缓鎸夐挳瀛樺偍杩囩▼
    */
   creatFunc = () => {
-    const { menu } = this.props
+    const menu = window.GLOB.customMenu
     let _config = fromJS(this.props.config).toJS()
 
     this.actionFormRef.handleConfirm().then(res => {
@@ -353,37 +351,28 @@
       }
 
       if (btn.OpenType === 'pop') {
-        let fields = []
-        if (btn.groups.length > 0) {
-          btn.groups.forEach(group => {
-            fields = [...fields, ...group.sublist]
-          })
-        } else {
-          fields = btn.fields
-        }
-
         let _param = {
           funcName: btn.innerFunc,
           name: _config.setting.tableName || '',
-          fields: fields,
+          fields: btn.fields,
           menuNo: menu.MenuNo
         }
-        newLText = Utils.formatOptions(DevUtils.getfunc(_param, btn, menu, _config))
-        DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+        newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
+        DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
       } else if (btn.OpenType === 'excelIn') {
         let _param = {
           funcName: btn.innerFunc,
           menuNo: menu.MenuNo
         }
-        newLText = Utils.formatOptions(DevUtils.getexcelInfunc(_param, btn, menu))
-        DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+        newLText = Utils.formatOptions(FuncUtils.getexcelInfunc(_param, btn, menu))
+        DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
       } else if (btn.OpenType === 'excelOut') {
         let _param = {
           innerFunc: btn.innerFunc
         }
 
-        newLText = Utils.formatOptions(DevUtils.getTableFunc(_param, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
-        DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+        newLText = Utils.formatOptions(FuncUtils.getTableFunc(_param, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
+        DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
       } else {
         let _param = {
           funcName: btn.innerFunc,
@@ -391,8 +380,8 @@
           fields: '',
           menuNo: menu.MenuNo
         }
-        newLText = Utils.formatOptions(DevUtils.getfunc(_param, btn, menu, _config))
-        DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+        newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
+        DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
       }
 
       this.refs.btnCreatFunc.exec(btn.innerFunc, newLText, DelText)
@@ -405,8 +394,12 @@
   btnDoubleClick = (element) => {
     if (sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') !== 'false') return
     
-    if (element.OpenType === 'pop' || element.OpenType === 'popview') {
+    if (element.OpenType === 'pop' || element.OpenType === 'popview' || element.execMode === 'pop') {
       this.props.setSubConfig(element)
+    } else if (element.OpenType === 'innerpage' && element.pageTemplate === 'page') {
+      MKEmitter.emit('changeEditMenu', {MenuID: element.uuid, copyMenuId: element.copyMenuId})
+    } else if (element.OpenType === 'innerpage' && element.pageTemplate === 'linkpage') {
+      MKEmitter.emit('changeEditMenu', {MenuID: element.linkmenu})
     } else {
       notification.warning({
         top: 92,
@@ -491,7 +484,6 @@
           visible={profVisible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.verifySubmit}
           onCancel={() => { this.setState({ profVisible: false }) }}
@@ -536,14 +528,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(ActionComponent)
\ No newline at end of file
+export default ActionComponent
\ No newline at end of file
diff --git a/src/menu/components/share/actioncomponent/index.scss b/src/menu/components/share/actioncomponent/index.scss
index 0453e00..79f0b8a 100644
--- a/src/menu/components/share/actioncomponent/index.scss
+++ b/src/menu/components/share/actioncomponent/index.scss
@@ -42,7 +42,7 @@
       min-height: calc(100vh - 300px);
       overflow-y: auto;
       .ant-empty {
-        margin: 15vh 8px;
+        margin: 5vh 8px;
       }
     }
     .ant-modal-body::-webkit-scrollbar {
diff --git a/src/menu/components/share/clockcomponent/index.jsx b/src/menu/components/share/clockcomponent/index.jsx
new file mode 100644
index 0000000..65549fc
--- /dev/null
+++ b/src/menu/components/share/clockcomponent/index.jsx
@@ -0,0 +1,70 @@
+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 ClockForm from './settingform'
+import './index.scss'
+
+class ClockComponent extends Component {
+  static propTpyes = {
+    btnlog: PropTypes.array,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+    timer: '',
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  trigger = () => {
+    const { config } = this.props
+
+    this.setState({
+      visible: true,
+      timer: config.timer || ''
+    })
+  }
+
+  submit = () => {
+    const { config } = this.props
+
+    this.verifyRef.handleConfirm().then(res => {
+      this.setState({
+        visible: false
+      })
+      this.props.updateConfig({...config, timer: res.timer})
+    })
+  }
+
+  render () {
+    const { visible, loading, timer } = this.state
+
+    return (
+      <div className="clock-component-wrap">
+        <Icon type="clock-circle" title="瀹氭椂鍣�" onClick={this.trigger} />
+        <Modal
+          title="瀹氭椂鍣ㄨ缃�"
+          visible={visible}
+          width={500}
+          maskClosable={false}
+          confirmLoading={loading}
+          onOk={this.submit}
+          onCancel={() => this.setState({ visible: false })}
+          destroyOnClose
+        >
+          <ClockForm timer={timer} inputSubmit={this.submit} wrappedComponentRef={(inst) => this.verifyRef = inst}/>
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default ClockComponent
\ No newline at end of file
diff --git a/src/menu/components/share/clockcomponent/index.scss b/src/menu/components/share/clockcomponent/index.scss
new file mode 100644
index 0000000..997bba1
--- /dev/null
+++ b/src/menu/components/share/clockcomponent/index.scss
@@ -0,0 +1,7 @@
+.clock-component-wrap {
+  display: inline-block;
+
+  >.anticon-clock-circle {
+    color: rgb(38, 194, 129);
+  }
+}
\ No newline at end of file
diff --git a/src/menu/components/share/clockcomponent/settingform/index.jsx b/src/menu/components/share/clockcomponent/settingform/index.jsx
new file mode 100644
index 0000000..bac1266
--- /dev/null
+++ b/src/menu/components/share/clockcomponent/settingform/index.jsx
@@ -0,0 +1,72 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Select } from 'antd'
+
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    timer: PropTypes.string,      // 缁勪欢鍚嶇О
+    inputSubmit: PropTypes.func   // 鍥炶溅浜嬩欢
+  }
+
+  state = {}
+
+  handleConfirm = () => {
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <div>
+        <Form {...formItemLayout}>
+          <Row gutter={24}>
+            <Col span={22}>
+              <Form.Item label="瀹氭椂鍣�">
+                {getFieldDecorator('timer', {
+                  initialValue: this.props.timer || ''
+                })(
+                  <Select>
+                    <Select.Option value=""> 鏃� </Select.Option>
+                    <Select.Option value="15s"> 15绉� </Select.Option>
+                    <Select.Option value="30s"> 30绉� </Select.Option>
+                    <Select.Option value="1min"> 1鍒嗛挓 </Select.Option>
+                    <Select.Option value="5min"> 5鍒嗛挓 </Select.Option>
+                    <Select.Option value="10min"> 10鍒嗛挓 </Select.Option>
+                    <Select.Option value="15min"> 15鍒嗛挓 </Select.Option>
+                    <Select.Option value="30min"> 30鍒嗛挓 </Select.Option>
+                    <Select.Option value="1hour"> 1灏忔椂 </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/datasource/verifycard/columnform/index.scss b/src/menu/components/share/clockcomponent/settingform/index.scss
similarity index 100%
copy from src/mob/datasource/verifycard/columnform/index.scss
copy to src/menu/components/share/clockcomponent/settingform/index.scss
diff --git a/src/menu/components/share/logcomponent/index.jsx b/src/menu/components/share/logcomponent/index.jsx
index 573aced..570e93d 100644
--- a/src/menu/components/share/logcomponent/index.jsx
+++ b/src/menu/components/share/logcomponent/index.jsx
@@ -17,7 +17,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     data: [],
     columns: [
diff --git a/src/menu/components/share/markcomponent/index.jsx b/src/menu/components/share/markcomponent/index.jsx
index 03dd41d..af08d0c 100644
--- a/src/menu/components/share/markcomponent/index.jsx
+++ b/src/menu/components/share/markcomponent/index.jsx
@@ -22,7 +22,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     marks: null,
     columns: null,
     visible: false,
@@ -313,7 +313,6 @@
           visible={visible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.markSubmit}
           onCancel={() => { this.setState({ visible: false }) }}
diff --git a/src/menu/components/share/searchcomponent/index.jsx b/src/menu/components/share/searchcomponent/index.jsx
index 93b09e4..c123995 100644
--- a/src/menu/components/share/searchcomponent/index.jsx
+++ b/src/menu/components/share/searchcomponent/index.jsx
@@ -1,7 +1,6 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
-import {connect} from 'react-redux'
 import { Modal, notification } from 'antd'
 import moment from 'moment'
 
@@ -25,7 +24,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     searchlist: null,    // 鎼滅储鏉′欢闆�
     sqlVerifing: false,  // sql楠岃瘉涓�
     visible: false,      // 妯℃�佹鎺у埗
@@ -319,14 +318,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(SearchComponent)
\ No newline at end of file
+export default SearchComponent
\ No newline at end of file
diff --git a/src/menu/components/share/usercomponent/index.jsx b/src/menu/components/share/usercomponent/index.jsx
index 528006d..30caa31 100644
--- a/src/menu/components/share/usercomponent/index.jsx
+++ b/src/menu/components/share/usercomponent/index.jsx
@@ -13,14 +13,13 @@
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
-class DataSource extends Component {
+class UserComponent extends Component {
   static propTpyes = {
-    btnlog: PropTypes.array,
-    handlelog: PropTypes.func
+    btnlog: PropTypes.array
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     loading: false,
     name: '',
@@ -228,4 +227,4 @@
   }
 }
 
-export default DataSource
\ No newline at end of file
+export default UserComponent
\ No newline at end of file
diff --git a/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx b/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
index 4126c3c..46484ca 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
+++ b/src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
@@ -1,7 +1,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鏄剧ず鍒楄〃鍗曢厤缃俊鎭�
@@ -48,6 +48,10 @@
       value: 'action',
       text: '鎿嶄綔'
     })
+  }
+
+  if (!card.linkurl && (!card.linkmenu || card.linkmenu.length === 0)) {
+    card.perspective = ''
   }
 
   return [
@@ -295,8 +299,11 @@
       type: 'radio',
       key: 'perspective',
       label: '瀛楁閫忚',
-      initVal: card.perspective || 'linkmenu',
+      initVal: card.perspective || '',
       options: [{
+        value: '',
+        text: '鏃�'
+      }, {
         value: 'linkmenu',
         text: '鑿滃崟'
       }, {
@@ -309,15 +316,23 @@
       key: 'linkmenu',
       label: Formdict['model.menu'],
       initVal: card.linkmenu || [],
-      required: false,
+      required: true,
       options: menulist
     },
     {
-      type: 'text',
+      type: 'textarea',
       key: 'linkurl',
       label: '閾炬帴鍦板潃',
       initVal: card.linkurl || '',
-      required: false
+      required: true
+    },
+    {
+      type: 'multiselect',
+      key: 'linkfields',
+      label: '鍏宠仈瀛楁',
+      initVal: card.linkfields || [],
+      required: false,
+      options: fields
     },
     {
       type: 'multiselect',
diff --git a/src/menu/components/table/normal-table/columns/editColumn/index.jsx b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
index 4d355d9..43c63ac 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/index.jsx
+++ b/src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -7,6 +7,7 @@
 import { formRule } from '@/utils/option.js'
 import './index.scss'
 
+const { TextArea } = Input
 const columnTypeOptions = {
   text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'blacklist', 'perspective', 'rowspan'],
   number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
@@ -54,10 +55,10 @@
     let formlist = getColumnForm(column, menulist, this.props.fields)
     let _options = fromJS(columnTypeOptions[column.type]).toJS()
     if (column.type === 'text' || column.type === 'number') {
-      if (column.perspective !== 'linkurl') {
-        _options.push('linkmenu')
-      } else {
-        _options.push('linkurl')
+      if (column.perspective === 'linkmenu') {
+        _options.push('linkmenu', 'linkfields')
+      } else if (column.perspective === 'linkurl') {
+        _options.push('linkurl', 'linkfields')
       }
     }
 
@@ -85,10 +86,6 @@
     if (key === 'type') {
       let _options = fromJS(columnTypeOptions[value]).toJS()
 
-      if (value === 'text' || value === 'number') {
-        _options.push('linkmenu')
-      }
-
       this.setState({
         type: value,
         formlist: this.state.formlist.map(item => {
@@ -100,7 +97,7 @@
         if (value === 'link' || value === 'textarea' || value === 'picture') {
           this.props.form.setFieldsValue({IsSort: 'false'})
         } else if (value === 'text' || value === 'number') {
-          this.props.form.setFieldsValue({perspective: 'linkmenu'})
+          this.props.form.setFieldsValue({perspective: ''})
         } else if (value === 'action' || value === 'colspan') {
           this.props.form.setFieldsValue({Align: 'center'})
         }
@@ -123,10 +120,10 @@
     if (key === 'perspective') {
       let _options = fromJS(columnTypeOptions[this.state.type]).toJS()
 
-      if (value !== 'linkurl') {
-        _options.push('linkmenu')
-      } else {
-        _options.push('linkurl')
+      if (value === 'linkmenu') {
+        _options.push('linkmenu', 'linkfields')
+      } else if (value === 'linkurl') {
+        _options.push('linkurl', 'linkfields')
       }
 
       this.setState({
@@ -273,7 +270,7 @@
                   filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                 >
                   {item.options.map((option, i) =>
-                    <Select.Option id={i} key={i} value={option.value}>{option.text}</Select.Option>
+                    <Select.Option id={i} key={i} value={option.value || option.field}>{option.text || option.label}</Select.Option>
                   )}
                 </Select>
               )}
@@ -285,7 +282,13 @@
           <Col span={12} key={index}>
             <Form.Item label={item.label}>
               {getFieldDecorator(item.key, {
-                initialValue: item.initVal || []
+                initialValue: item.initVal || [],
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
               })(
                 <Cascader
                   options={item.options}
@@ -296,6 +299,27 @@
             </Form.Item>
           </Col>
         )
+      } else if (item.type === 'textarea') { // 鏂囨湰鎼滅储
+        fields.push(
+          <Col span={24} key={index} className="textarea">
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={2} disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
       }
     })
     return fields
diff --git a/src/menu/components/table/normal-table/columns/editColumn/index.scss b/src/menu/components/table/normal-table/columns/editColumn/index.scss
index fcf59ac..e450762 100644
--- a/src/menu/components/table/normal-table/columns/editColumn/index.scss
+++ b/src/menu/components/table/normal-table/columns/editColumn/index.scss
@@ -16,4 +16,12 @@
       padding-right: 3px;
     }
   }
+  .textarea {
+    .ant-form-item-label {
+      width: 12%;
+    }
+    .ant-form-item-control-wrapper {
+      width: 88%;
+    }
+  }
 }
diff --git a/src/menu/components/table/normal-table/columns/index.jsx b/src/menu/components/table/normal-table/columns/index.jsx
index d779374..2e38088 100644
--- a/src/menu/components/table/normal-table/columns/index.jsx
+++ b/src/menu/components/table/normal-table/columns/index.jsx
@@ -175,7 +175,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     tableId: '',
     data: [{uuid: Utils.getuuid()}],
     refresh: false,    // 寮哄埗鍒锋柊
diff --git a/src/menu/components/table/normal-table/index.jsx b/src/menu/components/table/normal-table/index.jsx
index d6989a2..cd1a336 100644
--- a/src/menu/components/table/normal-table/index.jsx
+++ b/src/menu/components/table/normal-table/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Popover, Modal, notification } from 'antd'
 
@@ -35,7 +34,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     back: false
   }
@@ -134,7 +133,7 @@
   }
 
   shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState)) || (!this.props.menu && nextProps.menu)
+    return !is(fromJS(this.state), fromJS(nextState))
   }
 
   /**
@@ -295,7 +294,7 @@
     const { card } = this.state
     let btn = fromJS(item).toJS()
 
-    if (btn.OpenType === 'pop') {
+    if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
       if (!btn.modal) {
         btn.modal = {
           setting: { title: btn.label, width: 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
@@ -409,14 +408,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(TableCardEditComponent)
\ No newline at end of file
+export default TableCardEditComponent
\ No newline at end of file
diff --git a/src/menu/components/table/normal-table/wrapsetting/index.jsx b/src/menu/components/table/normal-table/wrapsetting/index.jsx
index 4a1fbd1..be1ea1b 100644
--- a/src/menu/components/table/normal-table/wrapsetting/index.jsx
+++ b/src/menu/components/table/normal-table/wrapsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     wrap: null
   }
diff --git a/src/menu/components/tabs/antv-tabs/index.jsx b/src/menu/components/tabs/antv-tabs/index.jsx
index c95ff93..3879bea 100644
--- a/src/menu/components/tabs/antv-tabs/index.jsx
+++ b/src/menu/components/tabs/antv-tabs/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Tabs, Icon, Popover, Modal } from 'antd'
 
@@ -31,7 +30,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     tabs: null,
     editab: null,
     labelvisible: false
@@ -216,6 +215,7 @@
     this.tabLabelRef.handleConfirm().then(res => {
       editab.label = res.label
       editab.icon = res.icon
+      editab.blacklist = res.blacklist
 
       if (editab.uuid) {
         tabs.subtabs = tabs.subtabs.map(t => {
@@ -353,14 +353,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(antvTabs)
\ No newline at end of file
+export default antvTabs
\ 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 416ca65..ca9c1fe 100644
--- a/src/menu/components/tabs/tabcomponents/card.jsx
+++ b/src/menu/components/tabs/tabcomponents/card.jsx
@@ -10,6 +10,8 @@
 const AntvTabs = asyncComponent(() => import('@/menu/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'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
 const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
 const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
 const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
@@ -61,6 +63,10 @@
       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') {
diff --git a/src/menu/components/tabs/tabcomponents/index.jsx b/src/menu/components/tabs/tabcomponents/index.jsx
index 6bf8943..7a913bc 100644
--- a/src/menu/components/tabs/tabcomponents/index.jsx
+++ b/src/menu/components/tabs/tabcomponents/index.jsx
@@ -6,7 +6,7 @@
 
 import Utils from '@/utils/utils.js'
 import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -97,6 +97,7 @@
         table: '琛ㄦ牸',
         group: '鍒嗙粍',
         editor: '瀵屾枃鏈�',
+        carousel: '杞挱',
         card: '鍗$墖'
       }
       let i = 1
diff --git a/src/menu/components/tabs/tablabelform/index.jsx b/src/menu/components/tabs/tablabelform/index.jsx
index 3a63a0d..784a9f9 100644
--- a/src/menu/components/tabs/tablabelform/index.jsx
+++ b/src/menu/components/tabs/tablabelform/index.jsx
@@ -12,10 +12,21 @@
     inputSubmit: PropTypes.func    // 鍥炶溅浜嬩欢
   }
 
-  state = {}
+  state = {roleList: []}
 
   UNSAFE_componentWillMount () {
+    let roleList = sessionStorage.getItem('sysRoles')
+    if (roleList) {
+      try {
+        roleList = JSON.parse(roleList)
+      } catch {
+        roleList = []
+      }
+    } else {
+      roleList = []
+    }
 
+    this.setState({roleList})
   }
 
   handleConfirm = () => {
@@ -42,6 +53,7 @@
   render() {
     const { tab } = this.props
     const { getFieldDecorator } = this.props.form
+    const { roleList } = this.state
 
     const formItemLayout = {
       labelCol: {
@@ -85,6 +97,23 @@
               )}
             </Form.Item>
           </Col>
+          <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>
     )
diff --git a/src/menu/components/tabs/tabsetting/index.jsx b/src/menu/components/tabs/tabsetting/index.jsx
index b913fc8..3b61f0f 100644
--- a/src/menu/components/tabs/tabsetting/index.jsx
+++ b/src/menu/components/tabs/tabsetting/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: false,
     setting: null
   }
diff --git a/src/menu/datasource/index.jsx b/src/menu/datasource/index.jsx
index 1869282..9732b5a 100644
--- a/src/menu/datasource/index.jsx
+++ b/src/menu/datasource/index.jsx
@@ -1,6 +1,5 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { Icon, Modal } from 'antd'
 
@@ -16,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     sourcelist: [],
     mainSearch: [],
     visible: false,
@@ -35,7 +34,7 @@
   }
 
   editDataSource = () => {
-    const { config, menu } = this.props
+    const { config } = this.props
 
     let search = []
     let parents = []
@@ -50,7 +49,7 @@
             _conf = item
 
             if (_conf.parentId && _conf.tabId) {
-              getParents(menu)
+              getParents(tab)
             }
           } else {
             getParents(tab)
@@ -60,10 +59,10 @@
     }
 
     if (config.parentId && config.tabId) {
-      getParents(menu)
+      getParents(window.GLOB.customMenu)
     }
 
-    parents.unshift(menu)
+    parents.unshift(window.GLOB.customMenu)
 
     parents.forEach(parent => {
       parent.components.forEach(item => {
@@ -110,7 +109,7 @@
   }
 
   render () {
-    const { config, menu } = this.props
+    const { config } = this.props
     const { visible, dict, loading, mainSearch } = this.state
 
     return (
@@ -122,7 +121,6 @@
           visible={visible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.verifySubmit}
           confirmLoading={loading}
@@ -131,7 +129,7 @@
         >
           <VerifyCard
             dict={dict}
-            menu={menu}
+            menu={window.GLOB.customMenu}
             mainSearch={mainSearch}
             config={config}
             wrappedComponentRef={(inst) => this.verifyRef = inst}
@@ -142,14 +140,4 @@
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(DataSource)
\ No newline at end of file
+export default DataSource
\ No newline at end of file
diff --git a/src/menu/datasource/verifycard/customscript/index.jsx b/src/menu/datasource/verifycard/customscript/index.jsx
index 64d25c7..24e92d2 100644
--- a/src/menu/datasource/verifycard/customscript/index.jsx
+++ b/src/menu/datasource/verifycard/customscript/index.jsx
@@ -220,7 +220,7 @@
           </Col>
           <Col span={24} className="sqlfield">
             <Form.Item label={'鍙敤瀛楁'}>
-              id, bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}
+              id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}
             </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 1febad6..18aba05 100644
--- a/src/menu/datasource/verifycard/index.jsx
+++ b/src/menu/datasource/verifycard/index.jsx
@@ -403,7 +403,7 @@
       let _loading = false
       if (this.scriptsForm && this.scriptsForm.state.editItem) {
         _loading = true
-      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && this.scriptsForm.props.form.getFieldValue('sql') !== ' ') {
+      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
         _loading = true
       }
 
@@ -542,7 +542,7 @@
         let _loading = false
         if (this.scriptsForm && this.scriptsForm.state.editItem) {
           _loading = true
-        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && this.scriptsForm.props.form.getFieldValue('sql') !== ' ') {
+        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
           _loading = true
         }
 
@@ -655,7 +655,6 @@
             <FieldsComponent
               config={{...config, columns}}
               type="fields"
-              tableFields={menu.tableFields}
               updatefield={this.updatefields}
             />
             <EditTable actions={['edit', 'move', 'copy', 'del']} type="datasourcefield" data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
diff --git a/src/menu/datasource/verifycard/settingform/index.jsx b/src/menu/datasource/verifycard/settingform/index.jsx
index da631aa..c114793 100644
--- a/src/menu/datasource/verifycard/settingform/index.jsx
+++ b/src/menu/datasource/verifycard/settingform/index.jsx
@@ -4,7 +4,7 @@
 
 import { formRule } from '@/utils/option.js'
 import Utils from '@/utils/utils.js'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import CodeMirror from '@/templates/zshare/codemirror'
 import './index.scss'
 
@@ -315,7 +315,7 @@
             {config.format === 'array' ? <Col span={8}>
               <Form.Item label="榛樿鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || '',
+                  initialValue: setting.order || 'ID desc',
                   rules: [
                     {
                       required: true,
@@ -408,11 +408,11 @@
               <Form.Item label={
                 <Tooltip placement="topLeft" title={'浼樺厛浣跨敤鍚岀骇鐨勬悳绱㈡潯浠剁粍浠讹紝鍚岀骇鎼滅储涓嶅瓨鍦ㄦ椂锛屼緷娆″悜涓婇�夊彇锛屼笌褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵竴鍚岀敤浣滄暟鎹繃婊わ紙褰撳墠缁勪欢鐨勬悳绱㈡潯浠朵紭鍏堬級銆�'}>
                   <Icon type="question-circle" />
-                  澶栧眰鎼滅储
+                  澶栭儴鎼滅储
                 </Tooltip>
               }>
                 {getFieldDecorator('useMSearch', {
-                  initialValue: setting.useMSearch || 'false'
+                  initialValue: setting.useMSearch || 'true'
                 })(
                   <Radio.Group onChange={(e) => this.setState({useMSearch: e.target.value})}>
                     <Radio value="true">浣跨敤</Radio>
diff --git a/src/menu/datasource/verifycard/settingform/index.scss b/src/menu/datasource/verifycard/settingform/index.scss
index d24bedd..5bd14b5 100644
--- a/src/menu/datasource/verifycard/settingform/index.scss
+++ b/src/menu/datasource/verifycard/settingform/index.scss
@@ -21,4 +21,7 @@
   .ant-input-number {
     width: 100%;
   }
+  .ant-radio-group {
+    white-space: nowrap;
+  }
 }
\ No newline at end of file
diff --git a/src/menu/datasource/verifycard/utils.jsx b/src/menu/datasource/verifycard/utils.jsx
index e51952b..9bb5840 100644
--- a/src/menu/datasource/verifycard/utils.jsx
+++ b/src/menu/datasource/verifycard/utils.jsx
@@ -30,7 +30,7 @@
     _customScript = _customScript.replace(/@\$|\$@/ig, '')
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
@@ -40,6 +40,15 @@
       _dataresource = _dataresource.replace(/@db@/ig, window.GLOB.externalDatabase)
       _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
     }
+
+    if (window.GLOB.urlFields) {
+      window.GLOB.urlFields.forEach(field => {
+        let reg = new RegExp('@' + field + '@', 'ig')
+        _dataresource = _dataresource.replace(reg, '0')
+        _customScript = _customScript.replace(reg, '0')
+      })
+    }
+    
     
     // 姝e垯鏇挎崲
     let _regoptions = []
@@ -108,30 +117,24 @@
 
     if (_customScript) {
       _regoptions.push({
-        var: new RegExp('@orderBy', 'ig'),
+        reg: new RegExp('@login_city@', 'ig'),
+      }, {
         reg: new RegExp('@orderBy@', 'ig'),
-      })
-      _regoptions.push({
-        var: new RegExp('@UserName', 'ig'),
+      }, {
         reg: new RegExp('@UserName@', 'ig'),
-      })
-      _regoptions.push({
-        var: new RegExp('@FullName', 'ig'),
+      }, {
         reg: new RegExp('@FullName@', 'ig'),
       })
       if (setting.laypage !== 'false') {
         _regoptions.push({
-          var: new RegExp('@pageSize', 'ig'),
           reg: new RegExp('@pageSize@', 'ig'),
         }, {
-          var: new RegExp('@pageIndex', 'ig'),
           reg: new RegExp('@pageIndex@', 'ig'),
         })
       }
       _regoptions.forEach(item => {
         _customScript = _customScript.replace(item.reg, '0')
         originscript = originscript.replace(item.reg, '0')
-        originscript = originscript.replace(item.var, '0')
       })
 
       if (setting.varMark) {
diff --git a/src/menu/menushell/card.jsx b/src/menu/menushell/card.jsx
index 1413357..b89e539 100644
--- a/src/menu/menushell/card.jsx
+++ b/src/menu/menushell/card.jsx
@@ -10,8 +10,11 @@
 const AntvTabs = asyncComponent(() => import('@/menu/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'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
 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 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'))
@@ -56,12 +59,18 @@
       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 === 'form') {
+      return (<NormalForm 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') {
diff --git a/src/menu/menushell/index.jsx b/src/menu/menushell/index.jsx
index cb51df3..6ac5ace 100644
--- a/src/menu/menushell/index.jsx
+++ b/src/menu/menushell/index.jsx
@@ -1,12 +1,11 @@
 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 '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import Card from './card'
 import './index.scss'
 
@@ -18,10 +17,7 @@
     const { card, index } = findCard(id)
     const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
     handleList({...menu, components: _cards})
-  }
-
-  if (!is(fromJS(cards), fromJS(menu.components))) {
-    setCards(menu.components)
+    setCards(_cards)
   }
   
   const findCard = id => {
@@ -33,7 +29,9 @@
   }
 
   const updateConfig = (element) => {
-    handleList({...menu, components: cards.map(item => item.uuid === element.uuid ? element : item)})
+    const _cards = cards.map(item => item.uuid === element.uuid ? element : item)
+    handleList({...menu, components: _cards})
+    setCards(_cards)
   }
 
   const deleteCard = (id) => {
@@ -54,8 +52,10 @@
       title: `纭畾鍒犻櫎銆�${card.name}銆嬪悧锛焋,
       content: hasComponent ? '褰撳墠缁勪欢涓惈鏈夊瓙缁勪欢锛�' : '',
       onOk() {
+        const _cards = cards.filter(item => item.uuid !== card.uuid)
         MKEmitter.emit('delButtons', uuids)
-        handleList({...menu, components: cards.filter(item => item.uuid !== card.uuid)})
+        handleList({...menu, components: _cards})
+        setCards(_cards)
       },
       onCancel() {}
     })
@@ -91,6 +91,8 @@
         group: '鍒嗙粍',
         editor: '瀵屾枃鏈�',
         code: '鑷畾涔�',
+        carousel: '杞挱',
+        form: '琛ㄥ崟',
         card: '鍗$墖'
       }
       let i = 1
@@ -128,6 +130,7 @@
       const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
 
       handleList({...menu, components: _cards})
+      setCards(_cards)
     }
   })
 
diff --git a/src/menu/modalconfig/index.jsx b/src/menu/modalconfig/index.jsx
index 1625ac1..6f3cd48 100644
--- a/src/menu/modalconfig/index.jsx
+++ b/src/menu/modalconfig/index.jsx
@@ -1,11 +1,10 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
-import {connect} from 'react-redux'
 import { is, fromJS } from 'immutable'
 import { DndProvider } from 'react-dnd'
 import HTML5Backend from 'react-dnd-html5-backend'
 import moment from 'moment'
-import { Button, Card, Modal, Collapse, notification, Icon, Empty, Popover } from 'antd'
+import { Button, Card, Modal, Collapse, notification, Icon, Switch } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -14,19 +13,18 @@
 import { getModalForm } from '@/templates/zshare/formconfig'
 
 import ModalForm from '@/templates/zshare/modalform'
-import DragElement from '@/templates/modalconfig/dragelement'
 import SourceElement from '@/templates/modalconfig/dragelement/source'
 import SettingForm from '@/templates/modalconfig/settingform'
-import GroupForm from '@/templates/modalconfig/groupform'
-import EditCard from '@/templates/modalconfig/editcard'
 import asyncComponent from '@/utils/asyncComponent'
 import { SearchItems } from '@/templates/modalconfig/source'
 import './index.scss'
 
 const { Panel } = Collapse
 const { confirm } = Modal
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
+const DragElement = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
+const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 
 class ComModalConfig extends Component {
   static propTpyes = {
@@ -36,7 +34,6 @@
   }
 
   state = {
-    menu: null,            // 涓婄骇鑿滃崟锛屼笁绾ц彍鍗曟垨鏍囩
     dict: CommonDict,      // 瀛楀吀
     config: null,          // 椤甸潰閰嶇疆锛屽寘鎷ā鏉跨被鍨嬨�佹ā鎬佹璁剧疆銆佹坊鍔犺〃鍚嶃�佽〃鍗曞垪琛�
     visible: false,        // 琛ㄥ崟缂栬緫妯℃�佹锛屾樉绀烘帶鍒�
@@ -51,11 +48,11 @@
     tables: [],            // 鍙敤琛ㄥ悕
     selectedTables: [],    // 宸查�夎〃鍚�
     originConfig: null,    // 鍘熷鑿滃崟
-    groupVisible: false,   // 鍏ㄥ眬閰嶇疆妯℃�佹
-    curgroup: null,        // 褰撳墠缁勶紝鏂板缓鎴栫紪杈�
     sources: null,         // 琛ㄥ崟绫诲瀷
     sqlVerifing: false,    // sql楠岃瘉
-    openEdition: ''        // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
+    openEdition: '',       // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
+    showField: false,      // 鏄剧ず琛ㄥ崟瀛楁鍊�
+    standardform: null
   }
 
   /**
@@ -65,20 +62,12 @@
     const { btn } = this.props
 
     let _config = btn.modal
+    _config.version = '1.0'
 
     this.setState({
       config: _config,
       originConfig: fromJS(_config).toJS()
     })
-  }
-
-  /**
-   * @description 鑾峰彇鏁版嵁琛ㄤ俊鎭�
-   * 1銆佽幏鍙栫郴缁熶腑鍏ㄩ儴琛ㄥ悕
-   * 2銆佹牴鎹凡閫夎〃鍚嶏紝鑾峰彇琛ㄦ牸瀛楁鍒楄〃
-   */
-  componentDidMount () {
-    
   }
 
   /**
@@ -96,78 +85,20 @@
    * 2銆佽〃鍗曠Щ鍔ㄥ悗锛屼繚瀛樼Щ鍔ㄥ悗鐨勯『搴�
    * 3銆佹柊澧炶〃鍗曟椂锛岀洿鎺ユ墦寮�缂栬緫妗�
    */
-  handleList = (list, group, elementId, newcard) => {
+  handleList = (list, newcard) => {
     let _config = fromJS(this.state.config).toJS()
 
-    if (!group && !elementId) {
-      // 娌℃湁鍒嗙粍鏃讹紙鎷栨嫿娣诲姞锛�
-      if (list.length > _config.fields.length) {
-        _config.fields = list.filter(item => !item.origin)
-  
-        this.setState({
-          config: _config
-        }, () => {
-          this.handleForm(newcard)
-        })
-      } else {
-        _config.fields = list
-        this.setState({config: _config})
-      }
-    } else if (group && !elementId) {
-      // 瀛樺湪鍒嗙粍鏃讹紝鎷栨嫿娣诲姞
-      if (list.length > group.sublist.length) {
-        group.sublist = list
-        _config.groups = _config.groups.map(item => {
-          if (item.uuid === group.uuid) {
-            return group
-          } else {
-            return item
-          }
-        })
-  
-        this.setState({
-          config: _config
-        }, () => {
-          this.handleForm(newcard)
-        })
-      } else {
-        group.sublist = list
-        _config.groups = _config.groups.map(item => {
-          if (item.uuid === group.uuid) {
-            return group
-          } else {
-            return item
-          }
-        })
-        this.setState({config: _config})
-      }
-    } else if (group && elementId) {
-      // 淇敼宸叉湁鍏冪礌鐨勫垎缁�
-      let element = null
-      _config.groups.forEach(item => {
-        item.sublist = item.sublist.filter(cell => {
-          if (cell.uuid !== elementId) {
-            return true
-          } else {
-            element = cell
-            return false
-          }
-        })
-      })
-
-      group.sublist.push(element)
-
-      _config.groups = _config.groups.map(item => {
-        if (item.uuid === group.uuid) {
-          return group
-        } else {
-          return item
-        }
-      })
+    if (list.length > _config.fields.length) {
+      _config.fields = list.filter(item => !item.origin)
 
       this.setState({
         config: _config
+      }, () => {
+        this.handleForm(newcard)
       })
+    } else {
+      _config.fields = list
+      this.setState({config: _config})
     }
   }
 
@@ -188,24 +119,19 @@
       value: '',
       text: '绌�'
     }]
-    let _formfields = []
+    let standardform = null
 
-    // 璁剧疆涓嬫媺鑿滃崟鍙叧鑱斿瓧娈�(涓婄骇涓庝笅绾�)
-    if (config.groups.length > 0) {
-      config.groups.forEach(group => {
-        _formfields = [..._formfields, ...group.sublist]
-      })
-    } else {
-      _formfields = config.fields
-    }
-
-    _inputfields = _formfields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
-    _tabfields = _formfields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
+    _inputfields = config.fields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
+    _tabfields = config.fields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
     _tabfields.unshift({field: '', text: '鍘熻〃鍗�'})
 
     let uniq = new Map()
     uniq.set(card.field, true)
-    _formfields.forEach(item => {
+    let index = null
+    config.fields.forEach((item, i) => {
+      if (card.uuid === item.uuid) {
+        index = i
+      }
       if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
       if (item.field && !uniq.has(item.field)) {
         uniq.set(item.field, true)
@@ -220,6 +146,13 @@
         })
       }
     })
+    if (index !== null) {
+      if (index === 0) {
+        standardform = config.fields[index + 1] || null
+      } else {
+        standardform = config.fields[index - 1] || null
+      }
+    }
 
     componentConfig.columns.forEach(col => {
       if (col.field && !uniq.has(col.field)) {
@@ -237,7 +170,13 @@
       card.linkSubField = card.linkSubField.filter(item => fields.includes(item))
     }
 
+    if (!card.span && standardform && standardform.span) {
+      card.span = standardform.span
+      card.labelwidth = standardform.labelwidth
+    }
+
     this.setState({
+      standardform,
       visible: true,
       card: card,
       formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, !!this.props.editTab)
@@ -256,37 +195,19 @@
       let fieldrepet = false // 瀛楁閲嶅
       let labelrepet = false // 鎻愮ず鏂囧瓧閲嶅
 
-      if (_config.groups.length > 0) {
-        _config.groups.forEach(group => {
-          group.sublist = group.sublist.map(item => {
-            if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
-              fieldrepet = true
-            } else if (item.uuid !== res.uuid && item.label === res.label) {
-              labelrepet = true
-            }
+      _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
-            }
-          })
-        })
-      } else {
-        _config.fields = _config.fields.map(item => {
-          if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
-            fieldrepet = true
-          } else if (item.uuid !== res.uuid && item.label === res.label) {
-            labelrepet = true
-          }
-
-          if (item.uuid === res.uuid) {
-            return res
-          } else {
-            return item
-          }
-        })
-      }
+        if (item.uuid === res.uuid) {
+          return res
+        } else {
+          return item
+        }
+      })
 
       if (fieldrepet) {
         notification.warning({
@@ -360,17 +281,10 @@
     let _this = this
 
     confirm({
-      content: `纭畾鍒犻櫎<<${card.label}>>鍚楋紵`,
+      content: `纭畾鍒犻櫎${card.label ? `<<${card.label}>>` : ''}鍚楋紵`,
       onOk() {
         let _config = fromJS(_this.state.config).toJS()
-
-        if (_config.groups.length > 0) {
-          _config.groups.forEach(group => {
-            group.sublist = group.sublist.filter(item => !(item.uuid === card.uuid))
-          })
-        } else {
-          _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
-        }
+        _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
 
         _this.setState({
           config: _config,
@@ -399,174 +313,6 @@
   }
 
   /**
-   * @description 閫氳繃琛ㄥ瓧娈垫坊鍔犺〃鍗�
-   * 1銆佹鏌ユ槸鍚﹀凡閫夎〃鍚嶏紝涓洪�夋椂璀﹀憡鎻愮ず
-   * 2銆佽〃瀛楁鍚嶉�氳繃map鍘婚噸
-   * 3銆佹鏌ヨ〃鍗曚腑鐨勫凡閫夊瓧娈碉紝骞舵爣璁板凡閫�
-   */
-  queryField = () => {
-    const { menu } = this.props
-    const { config } = this.state
-
-    if (menu.tables.length === 0) {
-      notification.warning({
-        top: 92,
-        message: '璇烽�夋嫨琛ㄥ悕锛�',
-        duration: 10
-      })
-      return
-    }
-
-    let columns = new Map()
-    menu.tableFields.forEach(table => {
-      table.columns.forEach(column => {
-        columns.set(column.field, column)
-      })
-    })
-
-    if (config.groups.length > 1) {
-      config.groups.forEach(group => {
-        group.sublist.forEach(item => {
-          if (columns.has(item.field)) {
-            columns.set(item.field, {...item, selected: true})
-          }
-        })
-      })
-    } else {
-      config.fields.forEach(item => {
-        if (columns.has(item.field)) {
-          columns.set(item.field, {...item, selected: true})
-        }
-      })
-    }
-
-    this.setState({
-      tableVisible: true,
-      fields: [...columns.values()]
-    })
-  }
-
-  /**
-   * @description 閫夋嫨瀛楁鍚庢彁浜�
-   * 1銆佹病鏈夊彲閫夊瓧娈垫椂锛岀洿鎺ュ叧闂�
-   * 2銆佽幏鍙栧凡閫夊瓧娈�
-   * 3銆佷笌宸叉湁瀛楁瀵规瘮
-   * 4銆佹坊鍔犳柊澧炲瓧娈�
-   */
-  addFieldSubmit = () => {
-    if (!this.state.fields || this.state.fields.length === 0) {
-      this.setState({
-        tableVisible: false
-      })
-    }
-
-    let _config = fromJS(this.state.config).toJS()
-
-    let cards = this.refs.searchcard.state.selectCards
-    let columns = new Map()
-    cards.forEach(card => {
-      columns.set(card.field, card)
-    })
-
-    if (_config.groups.length > 1) {
-      _config.groups.forEach(group => {
-        let items = []
-        group.sublist.forEach(item => {
-          if (columns.has(item.field)) {
-            let cell = columns.get(item.field)
-    
-            if (cell.selected && cell.type === item.type) { // 鏁版嵁閫夋嫨鐘舵�佸強绫诲瀷鏈慨鏀规椂锛岀洿鎺ユ坊鍔�
-              items.push(item)
-            } else if (cell.selected) {                     // 鏁版嵁绫诲瀷淇敼鏃讹紝閲嶇疆绫诲瀷鍙婂垵濮嬪��
-              item.type = cell.type
-              item.initval = ''
-              items.push(item)
-            }
-            columns.delete(item.field)
-          } else if (!item.origin) {                        // 杩囨护绀轰緥椤�
-            items.push(item)
-          }
-        })
-        group.sublist = items
-      })
-      
-      let _columns = [...columns.values()]
-  
-      let _additems = _columns.map(item => { // 寰幆娣诲姞鏂板瀛楁
-        return {
-          uuid: Utils.getuuid(),
-          label: item.label,
-          field: item.field,
-          initval: '',
-          type: item.type,
-          resourceType: '0',
-          setAll: 'false',
-          options: [],
-          orderType: 'asc',
-          decimal: 0,
-          min: '',
-          max: '',
-          readonly: 'false',
-          required: 'true'
-        }
-      })
-      _config.groups[_config.groups.length - 1].sublist = [..._config.groups.slice(-1)[0].sublist, ..._additems]
-
-    } else {
-      let items = []
-      _config.fields.forEach(item => {
-        if (columns.has(item.field)) {
-          let cell = columns.get(item.field)
-  
-          if (cell.selected && cell.type === item.type) { // 鏁版嵁閫夋嫨鐘舵�佸強绫诲瀷鏈慨鏀规椂锛岀洿鎺ユ坊鍔�
-            items.push(item)
-          } else if (cell.selected) {                     // 鏁版嵁绫诲瀷淇敼鏃讹紝閲嶇疆绫诲瀷鍙婂垵濮嬪��
-            item.type = cell.type
-            item.initval = ''
-            items.push(item)
-          }
-          columns.delete(item.field)
-        } else if (!item.origin) {                        // 杩囨护绀轰緥椤�
-          items.push(item)
-        }
-      })
-  
-      let _columns = [...columns.values()]
-  
-      _columns.forEach(item => { // 寰幆娣诲姞鏂板瀛楁
-        if (item.selected) {
-          let newcard = {
-            uuid: Utils.getuuid(),
-            label: item.label,
-            field: item.field,
-            initval: '',
-            type: item.type,
-            resourceType: '0',
-            setAll: 'false',
-            options: [],
-            orderType: 'asc',
-            readonly: 'false',
-            required: 'true'
-          }
-  
-          items.push(newcard)
-        }
-      })
-  
-      _config.fields = items
-    }
-
-    this.setState({
-      config: _config
-    })
-    notification.success({
-      top: 92,
-      message: '娣诲姞鎴愬姛',
-      duration: 2
-    })
-  }
-
-  /**
    * @description 鍏ㄥ眬璁剧疆妯℃�佹
    */
   changeSetting = () => {
@@ -588,115 +334,12 @@
     })
   }
 
-  handleGroup = (group) => {
-    let curgroup = ''
-
-    if (group) {
-      curgroup = group
-    } else {
-      curgroup = {
-        isnew: true,
-        label: '',
-        default: false,
-        uuid: Utils.getuuid(),
-        sublist: []
-      }
-    }
-
-    this.setState({
-      groupVisible: true,
-      curgroup: curgroup
-    })
-  }
-
-  closeGroup = (group) => {
-    let _this = this
-
-    confirm({
-      content: `纭畾鍒犻櫎鍒嗙粍<<${group.label}>>鍚楋紵`,
-      onOk() {
-        let _config = fromJS(_this.state.config).toJS()
-        _config.groups = _config.groups.filter(item => !(item.uuid === group.uuid))
-        let _length = _config.groups.length
-        
-        if (_length === 1) {
-          _config.fields = [...group.sublist, ..._config.groups[0].sublist]
-          _config.groups = []
-        } else {
-          _config.groups[_length - 1].sublist = [...group.sublist, ..._config.groups[_length - 1].sublist]
-        }
-
-        _this.setState({
-          config: _config
-        })
-      },
-      onCancel() {}
-    })
-  }
-
-  handleGroupSave = () => {
-    let _group = fromJS(this.state.curgroup).toJS()
-    let config = fromJS(this.state.config).toJS()
-
-    this.groupRef.handleConfirm().then(res => {
-      _group = {..._group, ...res.target}
-
-      if (_group.isnew) {
-        delete _group.isnew
-        config.groups.unshift(_group)
-
-        if (config.groups.length > 1) {
-          config.groups = config.groups.map(item => {
-            if (item.default) {
-              return res.default
-            } else {
-              return item
-            }
-          })
-        } else {
-          config.groups.push(res.default)
-        }
-      } else {
-        config.groups = config.groups.map(item => {
-          if (item.uuid === _group.uuid) {
-            return _group
-          } else if (item.default) {
-            return res.default
-          } else {
-            return item
-          }
-        })
-      }
-
-      config.fields = []
-
-      config.groups = config.groups.sort((a, b) => {
-        return a.sort - b.sort
-      })
-      
-      this.setState({
-        groupVisible: false,
-        curgroup: '',
-        config: config
-      })
-    })
-  }
-
   editModalCancel = () => {
     const { config, card } = this.state
 
     if (card.focus) {
-      let _config = null
-      if (config.groups.length > 0) {
-        let _groups = config.groups.map(group => {
-          group.sublist = group.sublist.filter(item => item.uuid !== card.uuid)
-          return group
-        })
-        _config = {...config, groups: _groups}
-      } else {
-        let _fields = config.fields.filter(item => item.uuid !== card.uuid)
-        _config = {...config, fields: _fields}
-      }
+      let _fields = config.fields.filter(item => item.uuid !== card.uuid)
+      let _config = {...config, fields: _fields}
 
       this.setState({
         card: null,
@@ -712,91 +355,99 @@
   }
 
   /**
-   * @description 缂栬緫鍔熻兘瀹屾垚鏇存柊锛屽寘鎷В鍐绘寜閽�佺矘璐淬�佹浛鎹㈢瓑
+   * @description 鏇存柊
    */
-  updateConfig = (res) => {
-    if (res.type === 'paste') {
-      this.setState({
-        config: res.config
-      })
-    }
+  updateConfig = (config) => {
+    this.setState({
+      config
+    })
+  }
+
+  changecols = (type) => {
+    let config = fromJS(this.state.config).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() {
+        _this.setState({config})
+      },
+      onCancel() {}
+    })
   }
 
   render () {
-    const { config } = this.state
+    const { config, dict } = this.state
 
     return (
       <div className="modal-form-board">
         <DndProvider backend={HTML5Backend}>
           <div className="tools">
             <Collapse accordion defaultActiveKey="1" bordered={false}>
-              <Panel header={this.state.dict['header.menu.form']} key="1">
+              <Panel header={dict['header.menu.form']} key="1">
                 <div className="search-element">
                   {SearchItems.map((item, index) => {
                     return (<SourceElement key={index} content={item}/>)
                   })}
                 </div>
-                <Button type="primary" block onClick={() => this.queryField()}>{this.state.dict['model.batchAdd']}</Button>
-                <Button type="primary" block onClick={() => this.handleGroup()}>{this.state.dict['header.menu.group.add']}</Button>
+                <FieldsComponent
+                  config={config}
+                  type="form"
+                  updatefield={this.updateConfig}
+                />
               </Panel>
             </Collapse>
           </div>
           <div className="setting">
-            <Card title={this.state.dict['header.menu.form.configurable']} bordered={false} extra={
+            <Card title={dict['header.menu.form.configurable']} bordered={false} extra={
               <div>
-                <EditComponent dict={this.state.dict} options={['form']} config={this.state.config} refresh={this.updateConfig}/>
-                <Button type="primary" onClick={this.submitConfig}>{this.state.dict['model.confirm']}</Button>
-                <Button onClick={this.cancelConfig}>{this.state.dict['model.cancel']}</Button>
+                <EditComponent dict={dict} options={['form']} config={this.state.config} refresh={(res) => this.updateConfig(res.config)}/>
+                <Button type="primary" onClick={this.submitConfig}>{dict['model.confirm']}</Button>
+                <Button onClick={this.cancelConfig}>{dict['model.cancel']}</Button>
               </div>
             } style={{ width: '100%' }}>
               <Icon type="setting" onClick={this.changeSetting} />
               <div className="ant-modal-content" style={{width: config.setting.width + '%'}}>
-                <button type="button" className="ant-modal-close">
-                  <span className="ant-modal-close-x"><Icon type="close"/></span>
-                </button>
                 <div className="ant-modal-header">
                   <div className="ant-modal-title">{config.setting.title}</div>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(4)}>4鍒�</Button>
+                  <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
                 </div>
                 <div className="ant-modal-body">
                   <div className="modal-form">
-                    {config.groups.length > 0 &&
-                      config.groups.map(group => {
-                        return (
-                          <div key={group.uuid}>
-                            <div className="group-title">
-                              {!group.default ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-                                <div className="mk-popover-control">
-                                  <Icon className="edit" type="edit" onClick={() => {this.handleGroup(group)}} />
-                                  <Icon className="edit close" type="close" onClick={() => {this.closeGroup(group)}} />
-                                </div>
-                              } trigger="hover">
-                                <span>{group.label}</span>
-                              </Popover> : null}
-                              {group.default ? <span style={{color: '#bcbcbc'}}>{group.label}</span> : null}
-                            </div>
-                            <DragElement
-                              group={group}
-                              list={group.sublist}
-                              setting={config.setting}
-                              placeholder={this.state.dict['header.form.modal.placeholder']}
-                              handleList={this.handleList}
-                              handleForm={this.handleForm}
-                              closeForm={this.closeForm}
-                            />
-                          </div>
-                        )
-                      })
-                    }
-                    {config.groups.length === 0 ?
-                      <DragElement
-                        list={config.fields}
-                        setting={config.setting}
-                        placeholder={this.state.dict['header.form.modal.placeholder']}
-                        handleList={this.handleList}
-                        handleForm={this.handleForm}
-                        closeForm={this.closeForm}
-                      /> : null
-                    }
+                    <DragElement
+                      list={config.fields}
+                      setting={config.setting}
+                      showField={this.state.showField}
+                      placeholder={this.state.dict['header.form.modal.placeholder']}
+                      handleList={this.handleList}
+                      handleForm={this.handleForm}
+                      closeForm={this.closeForm}
+                    />
                   </div>
                 </div>
                 <div className="ant-modal-footer">
@@ -828,27 +479,9 @@
             card={this.state.card}
             formlist={this.state.formlist}
             inputSubmit={this.handleSubmit}
+            standardform={this.state.standardform}
             wrappedComponentRef={(inst) => this.formRef = inst}
           />
-        </Modal>
-        <Modal
-          wrapClassName="modal-fields"
-          title={this.state.dict['model.edit']}
-          visible={this.state.tableVisible}
-          width={'65vw'}
-          maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
-          onOk={this.addFieldSubmit}
-          cancelText={this.state.dict['model.close']}
-          onCancel={() => { this.setState({ tableVisible: false }) }}
-          destroyOnClose
-        >
-          {this.state.fields && this.state.fields.length > 0 ?
-            <EditCard data={this.state.fields} ref="searchcard" type="search" /> : null
-          }
-          {(!this.state.fields || this.state.fields.length === 0) &&
-            <Empty />
-          }
         </Modal>
         <Modal
           title={this.state.dict['model.edit']}
@@ -882,36 +515,9 @@
         >
           {this.state.dict['header.menu.config.placeholder']}
         </Modal>
-        <Modal
-          title={this.state.dict['header.menu.group.manage']}
-          visible={this.state.groupVisible}
-          width={700}
-          maskClosable={false}
-          onOk={this.handleGroupSave}
-          onCancel={() => { this.setState({ groupVisible: false }) }}
-          destroyOnClose
-        >
-          <GroupForm
-            config={config}
-            dict={this.state.dict}
-            group={this.state.curgroup}
-            inputSubmit={this.handleGroupSave}
-            wrappedComponentRef={(inst) => this.groupRef = inst}
-          />
-        </Modal>
       </div>
     )
   }
 }
 
-const mapStateToProps = (state) => {
-  return {
-    menu: state.customMenu
-  }
-}
-
-const mapDispatchToProps = () => {
-  return {}
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(ComModalConfig)
\ No newline at end of file
+export default ComModalConfig
\ No newline at end of file
diff --git a/src/menu/modalconfig/index.scss b/src/menu/modalconfig/index.scss
index 7dcf66e..163f1e6 100644
--- a/src/menu/modalconfig/index.scss
+++ b/src/menu/modalconfig/index.scss
@@ -136,6 +136,20 @@
           z-index: 10;
           background: transparent;
           min-height: 50px;
+          padding-right: 75px;
+          .ant-modal-title {
+            display: inline-block;
+          }
+          .ant-switch {
+            position: absolute;
+            top: 15px;
+            right: 10px;
+          }
+          .mk-cols-change {
+            float: right;
+            height: 25px;
+            margin-right: 10px;
+          }
         }
         .ant-modal-close {
           opacity: 0.3;
@@ -216,6 +230,10 @@
                 width: 100%;
                 margin-top: 4px;
               }
+              .normal-braft-editor {
+                border: 1px solid #d9d9d9;
+                border-radius: 4px;
+              }
             }
             .ant-form-item-control-wrapper::after {
               content: '';
diff --git a/src/menu/modulesource/option.jsx b/src/menu/modulesource/option.jsx
index 9c0a333..e88ceca 100644
--- a/src/menu/modulesource/option.jsx
+++ b/src/menu/modulesource/option.jsx
@@ -14,6 +14,9 @@
 import Pie1 from '@/assets/mobimg/ring.png'
 import Pie2 from '@/assets/mobimg/nightingale.png'
 import Mainsearch from '@/assets/mobimg/mainsearch.png'
+import Carousel from '@/assets/mobimg/carousel.png'
+import Carousel1 from '@/assets/mobimg/carousel1.png'
+import form from '@/assets/mobimg/form.png'
 
 // 缁勪欢閰嶇疆淇℃伅
 export const menuOptions = [
@@ -21,6 +24,9 @@
   { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '鎼滅储鏉′欢', width: 24, forbid: ['billPrint'] },
   { 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: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', 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 },
   { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '琛ㄦ牸', width: 12 },
   { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
diff --git a/src/menu/padcontroller/index.jsx b/src/menu/padcontroller/index.jsx
index 589dd25..8bca837 100644
--- a/src/menu/padcontroller/index.jsx
+++ b/src/menu/padcontroller/index.jsx
@@ -15,7 +15,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     paddingTop: '',
     paddingBottom: '',
     paddingLeft: '',
diff --git a/src/menu/picturecontroller/editform/index.jsx b/src/menu/picturecontroller/editform/index.jsx
index 0d4db6b..e12d46d 100644
--- a/src/menu/picturecontroller/editform/index.jsx
+++ b/src/menu/picturecontroller/editform/index.jsx
@@ -31,8 +31,22 @@
               duration: 5
             })
             return
-          } else if (values.urls && values.urls[0]) {
+          } else if (values.urls && values.urls[0] && values.urls[0].status !== 'done') {
+            notification.warning({
+              top: 92,
+              message: '鏂囦欢涓婁紶涓紝璇风◢鍚庯紒',
+              duration: 5
+            })
+            return
+          } else if (values.urls && values.urls[0] && values.urls[0].response) {
             values.linkurl = values.urls[0].response
+          } else {
+            notification.warning({
+              top: 92,
+              message: '鏈幏鍙栧埌鏂囦欢璺緞锛�',
+              duration: 5
+            })
+            return
           }
           resolve(values)
         } else {
@@ -141,7 +155,7 @@
           <Col span={24}>
             <Form.Item label="澶囨敞">
               {getFieldDecorator('remark', {
-                initialValue: card.remark,
+                initialValue: card.remark || '',
                 rules: [
                   {
                     max: 50,
diff --git a/src/menu/picturecontroller/index.jsx b/src/menu/picturecontroller/index.jsx
index 7d887b7..cb45a35 100644
--- a/src/menu/picturecontroller/index.jsx
+++ b/src/menu/picturecontroller/index.jsx
@@ -248,7 +248,7 @@
                 </Col>
               </Row>
               <Row gutter={16} style={{height: '340px'}}>
-                {piclist.length && piclist.map(item => (
+                {piclist.length > 0 && piclist.map(item => (
                   <Col span={4} key={item.id}>
                     <div className="image-video-box">
                       <div className="image-video-box-body">
@@ -279,7 +279,7 @@
                 </Col>
               </Row>
               <Row gutter={16} style={{height: '340px'}}>
-                {vidlist.length && vidlist.map(item => (
+                {vidlist.length > 0 && vidlist.map(item => (
                   <Col span={4} key={item.id}>
                     <div className="image-video-box">
                       <div className="image-video-box-body">
diff --git a/src/menu/popview/index.jsx b/src/menu/popview/index.jsx
index 18d2fb6..59da6e1 100644
--- a/src/menu/popview/index.jsx
+++ b/src/menu/popview/index.jsx
@@ -1,6 +1,5 @@
 import React, { Component } from 'react'
 import PropTypes from 'prop-types'
-import { connect } from 'react-redux'
 import { DndProvider } from 'react-dnd'
 import { is, fromJS } from 'immutable'
 import moment from 'moment'
@@ -13,7 +12,6 @@
 import enUS from '@/locales/en-US/mob.js'
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
-import { modifyCustomMenu } from '@/store/action'
 
 import './index.scss'
 
@@ -38,16 +36,14 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     MenuType: '',
     MenuId: '',
     MenuNo: '',
-    tableFields: [],
     delButtons: [],
     activeKey: 'basedata',
     menuloading: false,
     oriConfig: null,
-    openEdition: '',
     config: null,
     customComponents: []
   }
@@ -153,11 +149,9 @@
       return
     }
 
-    let _config = fromJS(config).toJS()
-    delete _config.tableFields
     const _this = this
 
-    if (!is(fromJS(oriConfig), fromJS(_config))) {
+    if (!is(fromJS(oriConfig), fromJS(config))) {
       confirm({
         title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
         content: '',
@@ -214,13 +208,14 @@
           config.Template = 'CustomPage'
         }
 
+        config.open_edition = result.open_edition || ''
+
         this.setState({
           oriConfig: config,
-          config: fromJS(config).toJS(),
-          openEdition: result.open_edition || '',
+          config: fromJS(config).toJS()
         })
 
-        this.props.modifyCustomMenu(config)
+        window.GLOB.customMenu = config
       } else {
         notification.warning({
           top: 92,
@@ -332,7 +327,7 @@
 
   submitConfig = () => {
     const { btn } = this.props
-    const { openEdition, delButtons } = this.state
+    const { delButtons } = this.state
     let config = fromJS(this.state.config).toJS()
 
     if ((config.cacheUseful === 'true' && !config.cacheTime) || !config.MenuNo || !config.MenuName) {
@@ -355,31 +350,25 @@
         config.enabled = false
       }
 
-      let _config = fromJS(config).toJS()
-      delete _config.tableFields
-
       let _name = (btn.component.name ? btn.component.name + '-' : '') + btn.label
 
       let param = {
         func: 'sPC_ButtonParam_AddUpt',
         ParentID: btn.config.uuid,
-        MenuID: _config.uuid,
-        MenuNo: _config.MenuNo || '',
+        MenuID: config.uuid,
+        MenuNo: config.MenuNo || '',
         Template: 'CustomPage',
         MenuName: _name,
         PageParam: JSON.stringify({Template: 'CustomPage'}),
-        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
-      }
-
-      if (openEdition) { // 鐗堟湰绠$悊
-        param.open_edition = openEdition
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
+        open_edition: config.open_edition
       }
 
       let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
         func: 'sPC_Button_AddUpt',
         Type: 60,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
-        ParentID: _config.uuid,
-        MenuNo: _config.MenuNo,
+        ParentID: config.uuid,
+        MenuNo: config.MenuNo,
         Template: 'CustomPage',
         PageParam: '',
         LongParam: '',
@@ -427,9 +416,10 @@
         if (!res) return
 
         if (res.status) {
+          config.open_edition = res.open_edition || ''
+
           this.setState({
-            oriConfig: fromJS(_config).toJS(),
-            openEdition: res.open_edition || ''
+            oriConfig: fromJS(config).toJS()
           })
 
           if (btnParam.LText) {
@@ -534,23 +524,16 @@
       config: config
     })
 
-    this.props.modifyCustomMenu(config)
+    window.GLOB.customMenu = config
   }
 
   /**
    * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
    */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
+  updatetable = (config) => {
+    this.setState({ config })
 
-    config.tableFields = fields ? fields : tableFields
-
-    this.setState({
-      tableFields: fields ? fields : tableFields,
-      config
-    })
-
-    this.props.modifyCustomMenu(config)
+    window.GLOB.customMenu = config
   }
 
   insert = (item) => {
@@ -559,7 +542,7 @@
     config.components.push(item)
 
     this.setState({config})
-    this.props.modifyCustomMenu(config)
+    window.GLOB.customMenu = config
   }
 
   render () {
@@ -614,14 +597,4 @@
   }
 }
 
-const mapStateToProps = () => {
-  return {}
-}
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    modifyCustomMenu: (customMenu) => dispatch(modifyCustomMenu(customMenu))
-  }
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MenuDesign)
\ No newline at end of file
+export default MenuDesign
\ No newline at end of file
diff --git a/src/menu/stylecombcontroller/index.jsx b/src/menu/stylecombcontroller/index.jsx
index 88f39f9..6a9d18a 100644
--- a/src/menu/stylecombcontroller/index.jsx
+++ b/src/menu/stylecombcontroller/index.jsx
@@ -20,7 +20,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     options: [],
     style: {},
     borposition: 'outer'
@@ -260,7 +260,7 @@
                     label={<Icon title="楂樺害" type="column-height" />}
                     labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                   >
-                    <StyleInput defaultValue={''} options={['px']} onChange={this.changeHeight}/>
+                    <StyleInput defaultValue={''} options={['px', 'vh', 'vw']} onChange={this.changeHeight}/>
                   </Form.Item>
                 </Col>
               </Panel> : null}
diff --git a/src/menu/stylecontroller/index.jsx b/src/menu/stylecontroller/index.jsx
index ee460b5..595e8ff 100644
--- a/src/menu/stylecontroller/index.jsx
+++ b/src/menu/stylecontroller/index.jsx
@@ -22,7 +22,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,
     comIds: [],
     backgroundImage: '',
@@ -268,6 +268,15 @@
     this.updateStyle(_style)
   }
 
+  changeWidth = (val) => {
+    let _val = val
+    if (_val === '0px') {
+      _val = 'auto'
+    }
+
+    this.updateStyle({width: _val})
+  }
+
   changeHeight = (val) => {
     let _val = val
     if (_val === '0px') {
@@ -313,6 +322,17 @@
         <div className="menu-style-controller">
           <Form {...formItemLayout}>
             {card ? <Collapse expandIconPosition="right" destroyInactivePanel={true} defaultActiveKey={options[0]}>
+              {options.includes('width') ? <Panel header="瀹藉害" key="width">
+                <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.width || ''} options={['px', 'vh', 'vw']} onChange={this.changeWidth}/>
+                  </Form.Item>
+                </Col>
+              </Panel> : null}
               {options.includes('height') ? <Panel header="楂樺害" key="height">
                 <Col span={24}>
                   <Form.Item
@@ -320,7 +340,7 @@
                     label={<Icon title="楂樺害" type="column-height" />}
                     labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                   >
-                    <StyleInput defaultValue={card.height || ''} options={['px']} onChange={this.changeHeight}/>
+                    <StyleInput defaultValue={card.height || ''} options={['px', 'vh', 'vw']} onChange={this.changeHeight}/>
                   </Form.Item>
                 </Col>
               </Panel> : null}
diff --git a/src/menu/stylecontroller/styleInput/index.jsx b/src/menu/stylecontroller/styleInput/index.jsx
index 7426b2d..eadf6ef 100644
--- a/src/menu/stylecontroller/styleInput/index.jsx
+++ b/src/menu/stylecontroller/styleInput/index.jsx
@@ -51,7 +51,7 @@
       }
     }
 
-    let _val = parseInt(val)
+    let _val = parseFloat(val)
 
     if (isNaN(_val)) {
       _val = ''
@@ -81,7 +81,7 @@
         }
       }
 
-      let _val = parseInt(val)
+      let _val = parseFloat(val)
 
       if (isNaN(_val)) {
         _val = ''
@@ -105,7 +105,14 @@
   changeValue = (e) => {
     const { unit } = this.state
     let val = e.target.value
-    let _val = parseInt(val)
+
+    if (/\d+\.$/.test(val)) {
+      this.setState({
+        value: val
+      })
+      return
+    }
+    let _val = parseFloat(val)
     
     if (isNaN(_val)) {
       _val = ''
diff --git a/src/menu/sysinterface/settingform/baseform/index.jsx b/src/menu/sysinterface/settingform/baseform/index.jsx
index 1c4c9b8..fbb22d6 100644
--- a/src/menu/sysinterface/settingform/baseform/index.jsx
+++ b/src/menu/sysinterface/settingform/baseform/index.jsx
@@ -206,6 +206,22 @@
               </Form.Item>
             </Col>
             <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={'濡傛灉鑷畾涔夋帴鍙d笉鏀寔璺ㄥ煙璇锋眰锛屼細閫氳繃褰撳墠绯荤粺杞彂銆�'}>
+                  <Icon type="question-circle" />
+                  璺ㄥ煙璇锋眰
+                </Tooltip>
+              }>
+                {getFieldDecorator('cross', {
+                  initialValue: setting.cross || 'true'
+                })(
+                <Radio.Group>
+                  <Radio value="true">鏀寔</Radio>
+                  <Radio value="false">涓嶆敮鎸�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
               <Form.Item label="鍥炶皟鏂瑰紡">
                 {getFieldDecorator('callbackType', {
                   initialValue: setting.callbackType || 'script'
diff --git a/src/menu/sysinterface/settingform/index.jsx b/src/menu/sysinterface/settingform/index.jsx
index 94d9a80..2f6e4d0 100644
--- a/src/menu/sysinterface/settingform/index.jsx
+++ b/src/menu/sysinterface/settingform/index.jsx
@@ -18,7 +18,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     formlist: [],
     btnloading: false,
     activeKey: 'setting',
@@ -47,9 +47,9 @@
     const { activeKey, setting, preScripts, cbScripts } = this.state
 
     let _loading = false
-    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.preScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
     }
 
@@ -83,9 +83,9 @@
     const { activeKey } = this.state
 
     let _loading = false
-    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+    if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.preScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
     }
 
diff --git a/src/menu/sysinterface/settingform/simplescript/index.jsx b/src/menu/sysinterface/settingform/simplescript/index.jsx
index 0617137..c0d7832 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, appkey, time_id
+                bid, loginuid, sessionuid, userid, username, fullname, 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 2c7ddce..3cd33d8 100644
--- a/src/menu/sysinterface/settingform/utils.jsx
+++ b/src/menu/sysinterface/settingform/utils.jsx
@@ -17,13 +17,13 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
 
     _customScript = _customScript.replace(/@\$|\$@/ig, '')
-    _customScript = _customScript.replace(/@userName@|@fullName@/ig, `''`)
+    _customScript = _customScript.replace(/@userName@|@fullName@|@login_city@/ig, `''`)
     // 澶栬仈鏁版嵁搴撴浛鎹�
     if (window.GLOB.externalDatabase !== null) {
       _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
diff --git a/src/menu/urlfieldcomponent/index.jsx b/src/menu/urlfieldcomponent/index.jsx
new file mode 100644
index 0000000..ac0aecc
--- /dev/null
+++ b/src/menu/urlfieldcomponent/index.jsx
@@ -0,0 +1,135 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Modal, Tooltip, notification } from 'antd'
+
+import SettingForm from './settingform'
+import './index.scss'
+
+const { confirm } = Modal
+
+class UrlFieldComponent extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    visible: false,
+    urlFields: this.props.config.urlFields || []
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  editDataSource = () => {
+    this.setState({
+      visible: true
+    })
+  }
+
+  verifySubmit = () => {
+    const { urlFields } = this.state
+    const { config } = this.props
+
+    this.verifyRef.handleConfirm().then(res => {
+      if (urlFields.filter(field => field === res.field).length > 0) {
+        notification.warning({
+          top: 92,
+          message: '瀛楁宸插瓨鍦紒',
+          duration: 5
+        })
+        return
+      }
+
+      let _urlFields = [...urlFields, res.field]
+
+      this.setState({
+        visible: false,
+        urlFields: _urlFields
+      })
+
+      if (window.GLOB.urlFields) {
+        window.GLOB.urlFields = _urlFields
+      }
+
+      this.props.updateConfig({...config, urlFields: _urlFields})
+    })
+  }
+
+  deleteField = (field) => {
+    let config = JSON.stringify(this.props.config)
+    const _this = this
+
+    if (new RegExp(field, 'ig').test(config)) {
+      confirm({
+        title: `閰嶇疆涓瓨鍦ˊ${field}@锛岀‘瀹氬垹闄ゅ悧锛焋,
+        content: '',
+        onOk() {
+          _this.execDelete(field)
+        },
+        onCancel() {}
+      })
+    } else {
+      this.execDelete(field)
+    }
+  }
+
+  execDelete = (_field) => {
+    const { config } = this.props
+    let _urlFields = this.state.urlFields.filter(field => field !== _field)
+
+    this.setState({
+      urlFields: _urlFields
+    })
+
+    if (window.GLOB.urlFields) {
+      window.GLOB.urlFields = _urlFields
+    }
+    
+    this.props.updateConfig({...config, urlFields: _urlFields})
+  }
+
+  render () {
+    const { visible, urlFields } = this.state
+
+    return (
+      <div className="url-field-component">
+        <div className="field-plus">
+          <Tooltip placement="topLeft" title="椤甸潰鍙帴鏀剁殑鍙傛暟瀛楁锛屽湪鏌ヨ鏁版嵁婧愭垨鑷畾涔夎剼鏈腑浣跨敤 @瀛楁@ 鎺ユ敹銆�">
+            <Icon type="question-circle" />
+            url鍙橀噺
+          </Tooltip>
+          <Icon type="plus" title="娣诲姞" onClick={() => this.editDataSource()} />
+        </div>
+        <div>
+          {urlFields.map((field, index) => {
+            return (
+              <div className="field-item" key={index}>
+                <Icon type="close" title="鍒犻櫎" onClick={() => this.deleteField(field)} />
+                {field}
+              </div>
+            )
+          })}
+        </div>
+        <Modal
+          title="瀛楁娣诲姞"
+          visible={visible}
+          width={500}
+          maskClosable={false}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <SettingForm
+            inputSubmit={this.verifySubmit}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default UrlFieldComponent
\ No newline at end of file
diff --git a/src/menu/urlfieldcomponent/index.scss b/src/menu/urlfieldcomponent/index.scss
new file mode 100644
index 0000000..467a30c
--- /dev/null
+++ b/src/menu/urlfieldcomponent/index.scss
@@ -0,0 +1,28 @@
+.url-field-component {
+  margin-bottom: 15px;
+  .field-plus {
+    line-height: 35px;
+    >.anticon-plus {
+      color: #26C281;
+      padding: 2px 5px;
+      margin-left: 5px;
+    }
+    .anticon-question-circle {
+      color: #c49f47;
+      margin-right: 3px;
+    }
+  }
+  .field-item {
+    position: relative;
+    border: 1px solid #e8e8e8;
+    padding: 5px 10px;
+    border-radius: 4px;
+
+    >.anticon-close {
+      position: absolute;
+      right: 5px;
+      top: 3px;
+      font-size: 13px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/menu/urlfieldcomponent/settingform/index.jsx b/src/menu/urlfieldcomponent/settingform/index.jsx
new file mode 100644
index 0000000..6dd6c93
--- /dev/null
+++ b/src/menu/urlfieldcomponent/settingform/index.jsx
@@ -0,0 +1,82 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Input } from 'antd'
+
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  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()
+    }
+  }
+
+  componentDidMount() {
+    try {
+      let _input = document.getElementById('field')
+      if (_input.focus) {
+        _input.focus()
+      }
+    } catch {
+      console.warn('focus error锛�')
+    }
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 14 }
+      }
+    }
+
+    return (
+      <div className="url-field-form">
+        <Form {...formItemLayout}>
+          <Form.Item label="瀛楁鍚�">
+            {getFieldDecorator('field', {
+              initialValue: '',
+              rules: [
+                {
+                  required: true,
+                  message: '璇疯緭鍏ュ瓧娈靛悕!'
+                },
+                {
+                  pattern: /^[a-zA-Z0-9_]*$/ig,
+                  message: '瀛楁鍙娇鐢ㄨ嫳鏂囥�佹暟瀛楁垨_'
+                }
+              ]
+            })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+          </Form.Item>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/columnform/index.scss b/src/menu/urlfieldcomponent/settingform/index.scss
similarity index 100%
rename from src/mob/datasource/verifycard/columnform/index.scss
rename to src/menu/urlfieldcomponent/settingform/index.scss
diff --git a/src/mob/components/login/mob-login-1/index.jsx b/src/mob/components/login/mob-login-1/index.jsx
index 3904ae5..e0b2891 100644
--- a/src/mob/components/login/mob-login-1/index.jsx
+++ b/src/mob/components/login/mob-login-1/index.jsx
@@ -23,7 +23,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     rember: true,
     param: {
       type: 'login',
diff --git a/src/mob/components/login/mob-login-2/index.jsx b/src/mob/components/login/mob-login-2/index.jsx
index 37e1952..e9aa20c 100644
--- a/src/mob/components/login/mob-login-2/index.jsx
+++ b/src/mob/components/login/mob-login-2/index.jsx
@@ -21,7 +21,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     view: 'account',
     param: {
       type: 'login',
diff --git a/src/mob/contdelete/index.jsx b/src/mob/contdelete/index.jsx
deleted file mode 100644
index 05c09bc..0000000
--- a/src/mob/contdelete/index.jsx
+++ /dev/null
@@ -1,57 +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/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import './index.scss'
-
-const { confirm } = Modal
-
-class ContentDelete extends Component {
-  static propTpyes = {
-    element: PropTypes.object,
-    list: PropTypes.array,
-    updateContent: PropTypes.func
-  }
-
-  state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    images: [],
-    visible: false
-  }
-
-  UNSAFE_componentWillMount () {
-
-  }
-
-  // shouldComponentUpdate (nextProps, nextState) {
-  //   return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  // }
-
-  deleteElement = () => {
-    const { list, element } = this.props
-    const _this = this
-
-    confirm({
-      title: '纭畾鍒犻櫎鍏冪礌鍚楋紵',
-      onOk() {
-        _this.props.updateContent({...list, subItems: list.subItems.filter(item => item.uuid !== element.uuid)})
-      },
-      onCancel() {}
-    })
-  }
-
-  
-
-  render () {
-    return (
-      <div className="mob-content-list-delete">
-        <Icon type="close" onClick={this.deleteElement} />
-      </div>
-    )
-  }
-}
-
-export default ContentDelete
\ No newline at end of file
diff --git a/src/mob/contdelete/index.scss b/src/mob/contdelete/index.scss
deleted file mode 100644
index a36b973..0000000
--- a/src/mob/contdelete/index.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.mob-content-list-delete {
-  position: absolute;
-  top: -10px;
-  left: -10px;
-  border-radius: 2px;
-  font-size: 14px;
-  display: none;
-  line-height: 1.5;
-  z-index: 1;
-
-  i {
-    padding: 2px 5px;
-    cursor: pointer;
-  }
-  .anticon-close {
-    color: #ff4d4f;
-  }
-}
-.deletable-item {
-  position: relative;
-}
-.deletable-item:hover .mob-content-list-delete {
-  display: inline-block;
-}
-
diff --git a/src/mob/controller/index.jsx b/src/mob/controller/index.jsx
deleted file mode 100644
index 603c459..0000000
--- a/src/mob/controller/index.jsx
+++ /dev/null
@@ -1,480 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Collapse, Form, Input, Col, Icon, InputNumber, Select, Radio, Popover, Menu } from 'antd'
-
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import ColorSketch from '@/mob/colorsketch'
-import FileUpload from '@/tabviews/zshare/fileupload'
-import './index.scss'
-
-const { Panel } = Collapse
-const { Option } = Select
-
-class MobController extends Component {
-  static propTpyes = {
-    editElem: PropTypes.any,
-    updateStyle: PropTypes.func,
-  }
-
-  state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    card: null,
-    fontColor: '#000000',
-    backgroundColor: '#ffffff',
-    bgimages: [],
-    marginTop: '',
-    marginTopVal: '',
-    marginBottom: '',
-    marginBottomVal: '',
-  }
-
-  UNSAFE_componentWillReceiveProps (nextProps) {
-    if (!is(fromJS(this.props.editElem), fromJS(nextProps.editElem))) {
-      this.setState({
-        card: null
-      }, () => {
-        if (!nextProps.editElem) return
-        let _card = fromJS(nextProps.editElem).toJS()
-        let bgImg = _card.backgroundImage || ''
-
-        if (bgImg && /^linear-gradient/.test(bgImg)) {
-          bgImg = bgImg.replace('linear-gradient(', '')
-          bgImg = bgImg.replace(')', '')
-        } else if (bgImg && /^url/.test(bgImg)) {
-          bgImg = bgImg.replace('url(', '')
-          bgImg = bgImg.replace(')', '')
-        }
-
-        this.setState({
-          card: _card,
-          fontColor: _card.color || '#000000',
-          backgroundColor: _card.backgroundColor || '#ffffff',
-          backgroundImage: bgImg,
-          marginTop: _card.marginTop ? _card.marginTop : '',
-          marginTopVal: _card.marginTop ? parseInt(_card.marginTop) : '',
-          marginBottom: _card.marginBottom ? _card.marginBottom : '',
-          marginBottomVal: _card.marginBottomVal ? parseInt(_card.marginBottomVal) : ''
-        })
-      })
-    }
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  updateStyle = (style) => {
-    const { card } = this.state
-
-    this.props.updateStyle({componentId: card.componentId, classId: card.classId, uuid: card.uuid, style})
-  }
-
-  /**
-   * @description 瀛椾綋澶у皬鍒囨崲锛岃秴鍑鸿寖鍥村拷鐣�
-   */
-  changeFontSize = (val) => {
-    let value = parseInt(val)
-
-    if (isNaN(value) || value < 12 || value > 100) return
-
-    this.updateStyle({fontSize: `${value}px`})
-  }
-
-  /**
-   * @description 淇敼琛岄棿璺濓紝瓒呭嚭鑼冨洿蹇界暐
-   */
-  changeLineHeight = (val) => {
-    let value = parseFloat(val)
-
-    if (isNaN(value) || value < 1 || value > 10) return
-
-    this.updateStyle({lineHeight: value})
-  }
-
-  /**
-   * @description 瀛椾綋闂磋窛淇敼锛岃秴鍑鸿寖鍥村拷鐣�
-   */
-  changeLetterSpacing = (val) => {
-    let value = parseFloat(val)
-
-    if (isNaN(value) || value < 0 || value > 100) return
-
-    this.updateStyle({letterSpacing: `${value}px`})
-  }
-
-  /**
-   * @description 淇敼瀛椾綋绮楃粏
-   */
-  boldChange = (val) => {
-    this.updateStyle({fontWeight: val})
-  }
-
-  /**
-   * @description 淇敼瀛椾綋棰滆壊 锛岄鑹叉帶浠�
-   */
-  changeFontColor = (val) => {
-    this.setState({
-      fontColor: val
-    })
-    this.updateStyle({color: val})
-  }
-
-  /**
-   * @description 淇敼瀛椾綋棰滆壊 锛屾墜鍔ㄨ緭鍏�
-   */
-  changeFontColorInput = (e) => {
-    this.setState({
-      fontColor: e.target.value
-    })
-  }
-
-  /**
-   * @description 瀛椾綋瀵归綈
-   */
-  changeTextAlign = (e) => {
-    this.updateStyle({textAlign: e.target.value})
-  }
-
-  /**
-   * @description 瀛椾綋鏍峰紡锛屽�炬枩
-   */
-  changeFontStyle = (e) => {
-    this.updateStyle({fontStyle: e.target.value})
-  }
-
-  /**
-   * @description 瀛椾綋瑁呴グ锛屼笅鍒掔嚎銆佽疮绌跨嚎銆佷笂鍒掔嚎
-   */
-  changeTextDecoration = (e) => {
-    this.updateStyle({textDecoration: e.target.value})
-  }
-
-  /**
-   * @description 淇敼鑳屾櫙棰滆壊 锛岄鑹叉帶浠�
-   */
-  changeBackgroundColor = (val) => {
-    this.setState({
-      backgroundColor: val
-    })
-    this.updateStyle({backgroundColor: val})
-  }
-
-  /**
-   * @description 淇敼瀛椾綋棰滆壊 锛屾墜鍔ㄨ緭鍏�
-   */
-  changeBackgroundColorInput = (e) => {
-    this.setState({
-      backgroundColor: e.target.value
-    })
-  }
-
-  imgChange = (list) => {
-    if (list[0] && list[0].response) {
-      this.setState({
-        bgimages: [],
-        backgroundImage: list[0].response
-      })
-      this.updateStyle({backgroundImage: `url(${list[0].response})`})
-    } else {
-      this.setState({bgimages: list})
-    }
-  }
-
-  changeBackgroundImageInput = (e) => {
-    this.setState({
-      backgroundImage: e.target.value
-    })
-  }
-
-  submitBackgroundImage = (e) => {
-    let val = e.target.value
-    val = val.replace(/^\s*|\s*$/ig, '')
-
-    if (/^http|^\/\//.test(val)) {
-      val = `url(${val})`
-    } else if (/^#|,/ig.test(val)) {
-      val = `linear-gradient(${val})`
-    }
-
-    this.updateStyle({backgroundImage: val})
-  }
-
-  submitBorder = (val, type) => {
-    this.updateStyle({[type]: val})
-  }
-
-  changeBorderRadius = (val) => {
-    let value = parseFloat(val)
-
-    if (isNaN(value) || value < 0 || value > 500) return
-
-    this.updateStyle({borderRadius: `${value}px`})
-  }
-
-  changeMarginTop = (e) => {
-    let val = e.target.value
-    let _val = parseInt(val)
-
-    this.setState({
-      marginTop: val
-    })
-
-    if (isNaN(_val)) return
-
-    this.setState({
-      marginTopVal: _val
-    })
-  }
-
-  submitMarginTop = (val) => {
-    this.setState({
-      marginTop: val
-    })
-    this.updateStyle({marginTop: val})
-  }
-
-  changeMarginBottom = (e) => {
-    let val = e.target.value
-    let _val = parseInt(val)
-
-    this.setState({
-      marginBottom: val
-    })
-
-    if (isNaN(_val)) return
-
-    this.setState({
-      marginBottomVal: _val
-    })
-  }
-
-  submitMarginBottom = (val) => {
-    this.setState({
-      marginBottom: val
-    })
-    this.updateStyle({marginBottom: val})
-  }
-
-  render () {
-    const { card, backgroundImage, bgimages, marginTop, marginTopVal, marginBottom, marginBottomVal } = this.state
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="mob-controller">
-        <Form {...formItemLayout}>
-          {card ? <Collapse expandIconPosition="right" defaultActiveKey={card.items[0]} accordion={true}>
-            {card.items.includes('font') ? <Panel header="瀛椾綋" key="font">
-              <Col span={12}>
-                <Form.Item colon={false} label={<Icon title="瀛椾綋澶у皬" type="font-size" />}>
-                  <InputNumber defaultValue={card.fontSize || 14} min={12} max={100} precision={0} onChange={this.changeFontSize} />
-                </Form.Item>
-              </Col>
-              <Col span={12}>
-                <Form.Item colon={false} label={<Icon title="瀛椾綋绮楃粏" type="bold" />}>
-                  <Select defaultValue={card.fontWeight || 'normal'} onChange={this.boldChange}>
-                    <Option value="normal">normal</Option>
-                    <Option value="bold">bold</Option>
-                    <Option value="bolder">bolder</Option>
-                    <Option value="lighter">lighter</Option>
-                    <Option value="100">100</Option>
-                    <Option value="200">200</Option>
-                    <Option value="300">300</Option>
-                    <Option value="400">400</Option>
-                    <Option value="500">500</Option>
-                    <Option value="600">600</Option>
-                    <Option value="700">700</Option>
-                    <Option value="800">800</Option>
-                    <Option value="900">900</Option>
-                  </Select>
-                </Form.Item>
-              </Col>
-              <Col span={12}>
-                <Form.Item colon={false} label={<Icon title="琛岄棿璺�" type="line-height" />}>
-                  <InputNumber defaultValue={card.lineHeight || 1.5} min={1} max={10} precision={1} onChange={this.changeLineHeight} />
-                </Form.Item>
-              </Col>
-              <Col span={12}>
-                <Form.Item colon={false} label={<Icon title="瀛楅棿璺�" type="column-width" />}>
-                  <InputNumber defaultValue={card.letterSpacing || 0} min={0} max={100} precision={0} onChange={this.changeLetterSpacing}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="瀛椾綋棰滆壊" type="font-colors" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <ColorSketch value={card.color || '#000000'} onChange={this.changeFontColor} />
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={' '}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Radio.Group defaultValue={card.fontStyle || 'normal'} onChange={this.changeFontStyle}>
-                    <Radio.Button value="normal"><span title="鏍囧噯">N</span></Radio.Button>
-                    <Radio.Button value="italic"><Icon title="鏂滀綋" type="italic" /></Radio.Button>
-                    <Radio.Button value="oblique" style={{fontStyle: 'oblique'}}><span title="鍊炬枩">B</span></Radio.Button>
-                  </Radio.Group>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={' '}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Radio.Group className="text-align" defaultValue={card.textAlign || 'left'} onChange={this.changeTextAlign}>
-                    <Radio.Button value="left"><Icon title="宸﹀榻�" type="align-left" /></Radio.Button>
-                    <Radio.Button value="center"><Icon title="灞呬腑瀵归綈" type="align-center" /></Radio.Button>
-                    <Radio.Button value="right"><Icon title="鍙冲榻�" type="align-right" /></Radio.Button>
-                  </Radio.Group>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={' '}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Radio.Group className="text-decoration" defaultValue={card.textDecoration || 'none'} onChange={this.changeTextDecoration}>
-                    <Radio.Button value="none"><span title="鏍囧噯">N</span></Radio.Button>
-                    <Radio.Button value="underline"><Icon title="涓嬪垝绾�" type="underline" /></Radio.Button>
-                    <Radio.Button value="line-through"><Icon title="涓垝绾�" type="strikethrough" /></Radio.Button>
-                    <Radio.Button value="overline" style={{textDecoration: 'overline'}}><span title="涓婂垝绾�">O</span></Radio.Button>
-                  </Radio.Group>
-                </Form.Item>
-              </Col>
-            </Panel> : null}
-            {card.items.includes('background') ? <Panel header="鑳屾櫙" key="background">
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="鑳屾櫙棰滆壊" type="bg-colors" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <ColorSketch value={card.backgroundColor || '#ffffff'} onChange={this.changeBackgroundColor} />
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="鑳屾櫙鍥剧墖" type="picture" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <FileUpload accept=".jpg,.png,.gif,.svg" value={bgimages} maxFile={2} fileType="text" onChange={this.imgChange}/>
-                  <Input placeholder="" value={backgroundImage} autoComplete="off" onBlur={this.submitBackgroundImage} onPressEnter={this.submitBackgroundImage} onChange={this.changeBackgroundImageInput} />
-                </Form.Item>
-              </Col>
-            </Panel> : null}
-            {card.items.includes('border') ? <Panel header="杈规" key="border">
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="澶栬竟妗�" type="border-outer" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Input placeholder="" defaultValue={card.border || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'border')} onPressEnter={(e) => this.submitBorder(e.target.value, 'border')}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="宸﹁竟妗�" type="border-left" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Input placeholder="" defaultValue={card.borderLeft || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderLeft')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderLeft')}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="鍙宠竟妗�" type="border-right" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Input placeholder="" defaultValue={card.borderRight || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderRight')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderRight')}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="涓婅竟妗�" type="border-top" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Input placeholder="" defaultValue={card.borderTop || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderTop')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderTop')}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="涓嬭竟妗�" type="border-bottom" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <Input placeholder="" defaultValue={card.borderBottom || ''} autoComplete="off" onBlur={(e) => this.submitBorder(e.target.value, 'borderBottom')} onPressEnter={(e) => this.submitBorder(e.target.value, 'borderBottom')}/>
-                </Form.Item>
-              </Col>
-              <Col span={24}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="鍦嗚" type="radius-setting" />}
-                  labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
-                >
-                  <InputNumber defaultValue={card.borderRadius || 0} min={0} max={500} precision={0} onChange={this.changeBorderRadius}/>
-                </Form.Item>
-              </Col>
-            </Panel> : null}
-            {card.items.includes('margin') ? <Panel header="澶栬竟璺�" key="margin">
-              <Col span={12}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="涓婅竟璺�" type="vertical-align-top"/>}
-                >
-                  <Popover placement="bottom" overlayClassName="margin-popover" content={
-                    marginTopVal !== '' ?
-                    <Menu>
-                      <Menu.Item onClick={() => this.submitMarginTop(`${marginTopVal}px`)}>{marginTopVal} px</Menu.Item>
-                      <Menu.Item onClick={() => this.submitMarginTop(`${marginTopVal}vh`)}>{marginTopVal} vh</Menu.Item>
-                    </Menu> : null
-                  } trigger="hover">
-                    <Input value={marginTop} onChange={this.changeMarginTop}/>
-                  </Popover>
-                </Form.Item>
-              </Col>
-              <Col span={12}>
-                <Form.Item
-                  colon={false}
-                  label={<Icon title="涓嬭竟璺�" type="vertical-align-bottom"/>}
-                >
-                  <Popover placement="bottom" overlayClassName="margin-popover" content={
-                    marginBottomVal !== '' ?
-                    <Menu>
-                      <Menu.Item onClick={() => this.submitMarginBottom(`${marginBottomVal}px`)}>{marginBottomVal} px</Menu.Item>
-                      <Menu.Item onClick={() => this.submitMarginBottom(`${marginBottomVal}vh`)}>{marginBottomVal} vh</Menu.Item>
-                    </Menu> : null
-                  } trigger="hover">
-                    <Input value={marginBottom} onChange={this.changeMarginBottom}/>
-                  </Popover>
-                </Form.Item>
-              </Col>
-            </Panel> : null}
-          </Collapse> : null}
-        </Form>
-      </div>
-    )
-  }
-}
-
-export default MobController
\ No newline at end of file
diff --git a/src/mob/controller/index.scss b/src/mob/controller/index.scss
deleted file mode 100644
index 6acbf69..0000000
--- a/src/mob/controller/index.scss
+++ /dev/null
@@ -1,147 +0,0 @@
-.mob-controller {
-  width: 100%;
-  height: 100%;
-  overflow: hidden;
-  >.ant-form >.ant-collapse {
-    border: 0;
-    background: #262E3F;
-
-    > .ant-collapse-item {
-      border-color: #202735;
-      > .ant-collapse-header {
-        padding: 10px 40px 10px 16px;
-        background: #262E3F;
-        color: rgba(255, 255, 255, 0.85);
-      }
-      >.ant-collapse-content {
-        color: rgba(255, 255, 255, 0.85);
-        background-color: #202735;
-        border-top: 1px solid #202735;
-        .ant-input-number {
-          width: 100%;
-        }
-        .ant-form-item {
-          margin-bottom: 5px;
-
-          .ant-form-item-label > label {
-            color: rgba(255, 255, 255, 0.85);
-            .anticon {
-              vertical-align: middle;
-              font-size: 18px;
-            }
-          }
-          .ant-form-item-control-wrapper  {
-            .ant-form-item-control {
-              input {
-                background: transparent;
-                color: rgba(255, 255, 255, 0.65);
-              }
-              .ant-input {
-                height: 28px;
-              }
-              .ant-input-number {
-                height: 28px;
-                background: transparent;
-                .ant-input-number-input {
-                  height: 28px;
-                }
-              }
-              .ant-select-selection {
-                background: transparent;
-                color: rgba(255, 255, 255, 0.65);
-              }
-              .color-sketch-block {
-                position: relative;
-                top: 10px;
-              }
-              .color-sketch-block + .ant-input {
-                float: right;
-                width: 70%;
-                margin-top: 9px;
-                padding-right: 5px;
-                padding-left: 5px;
-              }
-              .ant-select-arrow {
-                color: inherit;
-              }
-              .ant-input-number-handler-wrap {
-                background: transparent;
-                .ant-input-number-handler {
-                  color: rgba(255, 255, 255, 0.65);
-                  .ant-input-number-handler-up-inner, .ant-input-number-handler-down-inner {
-                    color: rgba(255, 255, 255, 0.65);
-                  }
-                }
-                .ant-input-number-handler:active {
-                  background: transparent;
-                }
-              }
-              .ant-radio-group {
-                .ant-radio-button-wrapper {
-                  background: transparent;
-                  color: rgba(255, 255, 255, 0.65);
-                  height: 27px;
-                  line-height: 25px;
-                  span {
-                    font-style: inherit;
-                  }
-                }
-              }
-              .fileupload-form-container {
-                .ant-btn {
-                  height: 28px;
-                }
-                .ant-upload-list-item {
-                  .ant-upload-list-item-info {
-                    background: transparent;
-                    color: rgba(255, 255, 255, 0.85);
-                    i {
-                      color: rgba(255, 255, 255, 0.85);
-                    }
-                  }
-                  .anticon-close {
-                    color: rgba(255, 255, 255, 0.85);
-                  }
-                  .anticon-close:hover {
-                    color: rgba(255, 255, 255, 0.85);
-                  }
-                }
-                .ant-upload-list-item:hover .ant-upload-list-item-info {
-                  background: transparent;
-                }
-              }
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
-.margin-popover {
-  padding-top: 0px;
-  .ant-popover-inner-content {
-    width: 90px;
-    padding: 0px 5px;
-    .ant-menu-root.ant-menu-vertical {
-      border: 0;
-      .ant-menu-item {
-        height: 30px;
-        cursor: pointer;
-        line-height: 30px;
-      }
-      .ant-menu-item:not(:last-child) {
-        margin-bottom: 0px;
-      }
-      .ant-menu-item:first-child {
-        margin-top: 10px;
-      }
-      .ant-menu-item:last-child {
-        margin-bottom: 10px;
-      }
-    }
-  }
-  .ant-popover-arrow {
-    display: none;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/contupdate/index.jsx b/src/mob/contupdate/index.jsx
deleted file mode 100644
index 8ec6f08..0000000
--- a/src/mob/contupdate/index.jsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-// import { is, fromJS } from 'immutable'
-import { Form, Icon, Popover, Input, Modal } from 'antd'
-
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import FileUpload from '@/tabviews/zshare/fileupload'
-import './index.scss'
-
-const { TextArea } = Input
-const { confirm } = Modal
-
-class ContentUpdate extends Component {
-  static propTpyes = {
-    deletable: PropTypes.any,
-    element: PropTypes.object,
-    updateContent: PropTypes.func
-  }
-
-  state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    images: [],
-    visible: false
-  }
-
-  UNSAFE_componentWillMount () {
-
-  }
-
-  // shouldComponentUpdate (nextProps, nextState) {
-  //   return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  // }
-
-  onVisibleChange = (visible) => {
-    const { element } = this.props
-    let val = this.props.form.getFieldValue('content')
-    let _url = this.props.form.getFieldValue('url')
-
-    this.setState({
-      visible: visible
-    })
-
-    if (element.eleType === 'link') {
-      if ((val && element.content !== val) || (_url && element.url !== _url)) {
-        this.props.updateContent({...element, content: val, url: _url})
-      } else {
-        this.props.form.setFieldsValue({content: element.content, url: element.url})
-      }
-    } else {
-      if (val && element.content !== val) {
-        this.props.updateContent({...element, content: val})
-      } else {
-        this.props.form.setFieldsValue({content: element.content})
-      }
-    }
-  }
-
-  handleSubmit = () => {
-    const { element } = this.props
-    let val = this.props.form.getFieldValue('content')
-
-    this.setState({
-      visible: false
-    })
-
-    if (val && element.content !== val) {
-      this.props.updateContent({...element, content: val})
-    } else {
-      this.props.form.setFieldsValue({content: element.content})
-    }
-  }
-
-  deleteElement = () => {
-    const _this = this
-
-    confirm({
-      title: '纭畾鍒犻櫎鍏冪礌鍚楋紵',
-      onOk() {
-        _this.props.updateContent(null)
-      },
-      onCancel() {}
-    })
-  }
-
-  imgChange = (list) => {
-    const { element } = this.props
-
-    this.setState({images: list})
-    if (list && list.length && list[0].response) {
-      this.setState({
-        visible: false,
-        images: []
-      })
-      let val = list[0].response
-      if (val && element.content !== val) {
-        this.props.updateContent({...element, content: val})
-      }
-      this.props.form.setFieldsValue({content: val})
-    }
-  }
-
-  render () {
-    const { element, deletable } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { visible, images } = this.state
-
-    return (
-      <div className="mob-content-update">
-        {deletable !== false ? <Icon type="delete" onClick={this.deleteElement} /> : null}
-        <Popover content={
-          <div>
-            {element.eleType === 'img' ? <FileUpload accept=".jpg,.png,.gif,.svg" value={images} maxFile={1} fileType="text" onChange={this.imgChange}/> : null}
-            {getFieldDecorator('content', {
-              initialValue: element.content
-            })(element.eleType !== 'textarea' ?
-              <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} /> :
-              <TextArea autoSize={{ minRows: 2, maxRows: 3 }} onPressEnter={this.handleSubmit} />
-            )}
-            {element.eleType === 'link' ? <div className="link-url">
-              <p>閾炬帴鍦板潃:</p>
-              {getFieldDecorator('url', {
-                initialValue: element.url
-              })(
-                <Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />
-              )}
-            </div> : null}
-          </div>
-        } overlayClassName="mob-content-update-popover" placement="bottomRight" title="" visible={visible} trigger="click" onVisibleChange={this.onVisibleChange}>
-          <Icon type="edit" />
-        </Popover>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(ContentUpdate)
\ No newline at end of file
diff --git a/src/mob/contupdate/index.scss b/src/mob/contupdate/index.scss
deleted file mode 100644
index 34a4615..0000000
--- a/src/mob/contupdate/index.scss
+++ /dev/null
@@ -1,53 +0,0 @@
-.mob-content-update {
-  position: absolute;
-  top: -22px;
-  right: -1px;
-  border-radius: 2px;
-  color: #ffffff;
-  font-size: 14px;
-  display: none;
-  line-height: 1.5;
-  z-index: 1;
-  white-space: nowrap;
-
-  i {
-    padding: 2px 5px;
-    cursor: pointer;
-    background: #2f54eb;
-  }
-  .anticon-delete {
-    background: #ff4d4f;
-  }
-}
-.editing .mob-content-update {
-  display: inline-block;
-}
-.mob-content-update-popover {
-  .ant-popover-content {
-    margin-top: -5px;
-    .ant-popover-arrow {
-      top: 1px;
-    }
-
-    .fileupload-form-container {
-      display: block;
-      margin-bottom: 10px;
-      .ant-upload-list {
-        width: 250px;
-      }
-    }
-  }
-  input {
-    width: 250px;
-  }
-  textarea {
-    width: 270px;
-  }
-  .link-url {
-    p {
-      margin: 10px 0px 5px 5px;
-      font-size: 14px;
-      color: #959595;
-    }
-  }
-}
diff --git a/src/mob/datasource/index.jsx b/src/mob/datasource/index.jsx
deleted file mode 100644
index 4cc50a0..0000000
--- a/src/mob/datasource/index.jsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { Icon, Modal } from 'antd'
-
-import Utils from '@/utils/utils.js'
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import VerifyCard from './verifycard'
-import './index.scss'
-
-const { confirm } = Modal
-
-class DataSource extends Component {
-  static propTpyes = {
-    config: PropTypes.any,
-    updateConfig: PropTypes.func
-  }
-
-  state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    sourcelist: [],
-    searches: [],
-    visible: false,
-    loading: false,
-    source: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config } = this.props
-
-    this.setState({sourcelist: config.sourcelist || []})
-  }
-
-  UNSAFE_componentWillReceiveProps(nextProps) {
-
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  editDataSource = (item) => {
-    // const { config } = this.props
-
-    if (!item) {
-      item = {
-        uuid: Utils.getuuid(),
-        setting: {},
-        columns: [],
-        scripts: []
-      }
-    }
-
-    this.setState({
-      visible: true,
-      source: item,
-      searches: []
-    })
-  }
-
-  closeDataSource = (item) => {
-    const { config } = this.props
-    let sourcelist = fromJS(this.state.sourcelist).toJS()
-    const _this = this
-
-    sourcelist = sourcelist.filter(cell => cell.uuid !== item.uuid)
-
-    confirm({
-      title: '纭畾鍒犻櫎鏁版嵁婧愬悧锛�',
-      content: '',
-      onOk() {
-        _this.setState({sourcelist})
-        _this.props.updateConfig({...config, sourcelist: fromJS(sourcelist).toJS()})
-      },
-      onCancel() {}
-    })
-  }
-
-  verifySubmit = () => {
-    const { config } = this.props
-    let sourcelist = fromJS(this.state.sourcelist).toJS()
-
-    this.setState({loading: true})
-    this.verifyRef.submitDataSource().then((res) => {
-      let isadd = true
-      sourcelist = sourcelist.map(item => {
-        if (item.uuid === res.uuid) {
-          isadd = false
-          return res
-        } else {
-          return item
-        }
-      })
-
-      if (isadd) {
-        sourcelist.push(res)
-      }
-      
-      this.setState({loading: false, visible: false, sourcelist})
-      this.props.updateConfig({...config, sourcelist: fromJS(sourcelist).toJS()})
-    }, () => {
-      this.setState({loading: false})
-    })
-  }
-
-  render () {
-    const { config } = this.props
-    const { sourcelist, visible, source, dict, searches, loading } = this.state
-
-    let addable = true
-    if (config.components && config.components.length === 1 && config.components[0].type === 'login') {
-      addable = false
-    }
-
-    return (
-      <div className="mob-datasource">
-        {sourcelist.map(item => (
-          <span className="mob-input-group-wrapper" key={item.uuid}>
-            <span className="mob-input-wrapper">
-              <span className="mob-input-value">{item.setting.name}</span>
-              <span className="mob-input-group-addon">
-                <Icon type="setting" onClick={() => this.editDataSource(item)} />
-                <Icon type="close" onClick={() => this.closeDataSource(item)} />
-              </span>
-            </span>
-          </span>
-        ))}
-        {addable ? <span className="mob-input-group-wrapper">
-          <span className="mob-input-wrapper">
-            <span className="mob-input-insert" onClick={() => this.editDataSource()}>
-              <Icon type="plus" />
-            </span>
-          </span>
-        </span> : null}
-        <Modal
-          wrapClassName="mob-datasource-verify-modal popview-modal"
-          title={'鏁版嵁婧愰厤缃�'}
-          visible={visible}
-          width={'75vw'}
-          maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
-          okText={dict['mob.submit']}
-          onOk={this.verifySubmit}
-          confirmLoading={loading}
-          onCancel={() => { this.setState({ visible: false }) }}
-          destroyOnClose
-        >
-          <VerifyCard
-            dict={dict}
-            card={source}
-            menuId={this.props.config.uuid}
-            searches={searches}
-            wrappedComponentRef={(inst) => this.verifyRef = inst}
-          />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default DataSource
\ No newline at end of file
diff --git a/src/mob/datasource/index.scss b/src/mob/datasource/index.scss
deleted file mode 100644
index ea28922..0000000
--- a/src/mob/datasource/index.scss
+++ /dev/null
@@ -1,89 +0,0 @@
-.mob-datasource {
-  width: 100%;
-  height: 100%;
-  overflow: hidden;
-  padding-top: 15px;
-
-  .mob-input-group-wrapper {
-    padding: 0 20px;
-    display: inline-block;
-    width: 100%;
-    text-align: start;
-    vertical-align: top;
-    margin-bottom: 15px;
-
-    .mob-input-wrapper {
-      position: relative;
-      display: table;
-      width: 100%;
-      border-collapse: separate;
-      border-spacing: 0;
-
-      .mob-input-value {
-        display: table-cell;
-        width: 100%;
-        border: 1px solid #d9d9d9;
-        border-radius: 4px 0px 0px 4px;
-        overflow: hidden;
-        text-overflow:ellipsis;
-        white-space: nowrap;
-        padding: 2px 10px;
-        color: #ffffff;
-      }
-
-      .mob-input-group-addon {
-        display: table-cell;
-        width: 1px;
-        position: relative;
-        padding: 0 11px;
-        color: rgba(0, 0, 0, 0.65);
-        font-weight: normal;
-        font-size: 14px;
-        line-height: 1;
-        text-align: center;
-        background-color: #fafafa;
-        border: 1px solid #d9d9d9;
-        border-radius: 0px 4px 4px 0px;
-        white-space: nowrap;
-      }
-
-      .mob-input-insert {
-        display: table-cell;
-        width: 100%;
-        border: 1px dotted #d9d9d9;
-        border-radius: 4px;
-        text-align: center;
-        cursor: pointer;
-
-        .anticon-plus {
-          padding: 6px;
-          font-size: 16px;
-          color: rgb(38, 194, 129);
-        }
-      }
-    }
-    .anticon-setting {
-      margin-right: 5px;
-      padding: 6px;
-      cursor: pointer;
-    }
-    .anticon-setting:hover {
-      color: #1890ff;
-    }
-    .anticon-close {
-      padding: 6px;
-      cursor: pointer;
-    }
-    .anticon-close:hover {
-      color: #ff4d4f;
-    }
-  }
-}
-.mob-datasource-verify-modal {
-  .ant-modal {
-    top: 50px;
-    .ant-modal-body {
-      max-height: calc(100vh - 190px);
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/columnform/index.jsx b/src/mob/datasource/verifycard/columnform/index.jsx
deleted file mode 100644
index 31fbe81..0000000
--- a/src/mob/datasource/verifycard/columnform/index.jsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Select, Button, Input } from 'antd'
-import './index.scss'
-
-
-class UniqueForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,       // 瀛楀吀椤�
-    columnChange: PropTypes.func  // 淇敼鍑芥暟
-  }
-
-  state = {
-    editItem: null // 缂栬緫鍏冪礌
-  }
-
-  edit = (record) => {
-    this.setState({
-      editItem: record
-    })
-
-    this.props.form.setFieldsValue({
-      label: record.label,
-      field: record.field,
-      datatype: record.datatype
-    })
-  }
-
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    this.props.form.validateFieldsAndScroll((err, values) => {
-      if (!err) {
-        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
-
-        this.setState({
-          editItem: null
-        }, () => {
-          this.props.columnChange(values)
-        })
-        this.props.form.setFieldsValue({
-          label: '',
-          field: ''
-        })
-      }
-    })
-  }
-
-  render() {
-    const { getFieldDecorator } = this.props.form
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <Form {...formItemLayout} className="verify-form" id="verifycard1">
-        <Row gutter={24}>
-          <Col span={7}>
-            <Form.Item label={'鍚嶇О'}>
-              {getFieldDecorator('label', {
-                initialValue: '',
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['form.required.input'] + '鍚嶇О!'
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" />)}
-            </Form.Item>
-          </Col>
-          <Col span={7}>
-            <Form.Item label={'瀛楁'}>
-              {getFieldDecorator('field', {
-                initialValue: '',
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['form.required.input'] + '瀛楁!'
-                  }
-                ]
-              })(<Input placeholder="" autoComplete="off" />)}
-            </Form.Item>
-          </Col>
-          <Col span={7}>
-            <Form.Item label={'鏁版嵁绫诲瀷'}>
-              {getFieldDecorator('datatype', {
-                initialValue: '',
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['form.required.select'] + '鏁版嵁绫诲瀷!'
-                  }
-                ]
-              })(
-                <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>
-                  <Select.Option value="Nvarchar(1024)"> Nvarchar(1024) </Select.Option>
-                  <Select.Option value="Nvarchar(2048)"> Nvarchar(2048) </Select.Option>
-                  <Select.Option value="Nvarchar(max)"> Nvarchar(max) </Select.Option>
-                  <Select.Option value="Int"> Int </Select.Option>
-                  <Select.Option value="Decimal(18,0)"> Decimal(18,0) </Select.Option>
-                  <Select.Option value="Decimal(18,1)"> Decimal(18,1) </Select.Option>
-                  <Select.Option value="Decimal(18,2)"> Decimal(18,2) </Select.Option>
-                  <Select.Option value="Decimal(18,3)"> Decimal(18,3) </Select.Option>
-                  <Select.Option value="Decimal(18,4)"> Decimal(18,4) </Select.Option>
-                  <Select.Option value="Decimal(18,5)"> Decimal(18,5) </Select.Option>
-                  <Select.Option value="Decimal(18,6)"> Decimal(18,6) </Select.Option>
-                  <Select.Option value="Decimal(18,7)"> Decimal(18,7) </Select.Option>
-                  <Select.Option value="Decimal(18,8)"> Decimal(18,8) </Select.Option>
-                  <Select.Option value="date"> date </Select.Option>
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-          <Col span={3} className="add">
-            <Button onClick={this.handleConfirm} type="primary" className="mk-green">
-              淇濆瓨
-            </Button>
-          </Col>
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(UniqueForm)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/customscript/index.jsx b/src/mob/datasource/verifycard/customscript/index.jsx
deleted file mode 100644
index 5267ea7..0000000
--- a/src/mob/datasource/verifycard/customscript/index.jsx
+++ /dev/null
@@ -1,229 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Button, notification, Select } from 'antd'
-
-import Utils from '@/utils/utils.js'
-import CodeMirror from '@/templates/zshare/codemirror'
-import './index.scss'
-
-class CustomForm extends Component {
-  static propTpyes = {
-    type: PropTypes.string,         // 鑿滃崟绫诲瀷
-    dict: PropTypes.object,         // 瀛楀吀椤�
-    setting: PropTypes.object,      // 璁剧疆
-    searches: PropTypes.array,      // 鎼滅储鏉′欢
-    swhere: PropTypes.string,       // where鏉′欢
-    arr_field: PropTypes.string,    // 鍒楀瓧娈�
-    regoptions: PropTypes.array,    // 姝e垯鏇挎崲
-    systemScripts: PropTypes.array, // 绯荤粺鑴氭湰
-    scriptSubmit: PropTypes.func,   // 鑴氭湰楠岃瘉鍚庢彁浜�
-    scriptsChange: PropTypes.func   // 鑴氭湰楠岃瘉
-  }
-
-  state = {
-    editItem: null,
-    loading: false,
-    usefulFields: ''
-  }
-
-  UNSAFE_componentWillMount() {
-    const { searches } = this.props
-
-    let _usefulFields = []
-    searches.forEach(item => {
-      if (item.type === 'group') {
-        if (item.transfer === 'true') {
-          _usefulFields.push(item.field)
-        }
-        _usefulFields.push(item.datefield)
-        _usefulFields.push(item.datefield + '1')
-      } else if (['dateweek', 'datemonth', 'daterange'].includes(item.type)) {
-        _usefulFields.push(item.field)
-        _usefulFields.push(item.field + '1')
-      } else if (_usefulFields.includes(item.field)) {
-        _usefulFields.push(item.field + '1')
-      } else {
-        _usefulFields.push(item.field)
-      }
-    })
-
-    this.setState({
-      usefulFields: _usefulFields.join(', ')
-    })
-  }
-
-  edit = (record) => {
-    this.setState({
-      editItem: record
-    })
-
-    this.props.form.setFieldsValue({
-      sql: record.sql
-    })
-  }
-
-  handleCancel = () => {
-    this.setState({
-      editItem: null
-    })
-    this.props.form.setFieldsValue({
-      sql: ''
-    })
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    this.props.form.validateFieldsAndScroll((err, values) => {
-      if (!err) {
-        values.uuid = this.state.editItem ? this.state.editItem.uuid : ''
-
-        let _quot = values.sql.match(/'{1}/g)
-        let _lparen = values.sql.match(/\({1}/g)
-        let _rparen = values.sql.match(/\){1}/g)
-
-        _quot = _quot ? _quot.length : 0
-        _lparen = _lparen ? _lparen.length : 0
-        _rparen = _rparen ? _rparen.length : 0
-
-        if (_quot % 2 !== 0) {
-          notification.warning({
-            top: 92,
-            message: 'sql涓璡'蹇呴』鎴愬鍑虹幇',
-            duration: 5
-          })
-          return
-        } else if (_lparen !== _rparen) {
-          notification.warning({
-            top: 92,
-            message: 'sql涓�()蹇呴』鎴愬鍑虹幇',
-            duration: 5
-          })
-          return
-        } else if (/--/ig.test(values.sql)) {
-          notification.warning({
-            top: 92,
-            message: '鑷畾涔塻ql璇彞涓紝涓嶅彲鍑虹幇瀛楃 -- 锛屾敞閲婅鐢� /*鍐呭*/',
-            duration: 5
-          })
-          return
-        }
-
-        let error = Utils.verifySql(values.sql, 'customscript')
-
-        if (error) {
-          notification.warning({
-            top: 92,
-            message: 'sql涓笉鍙娇鐢�' + error,
-            duration: 5
-          })
-          return
-        }
-
-        this.setState({loading: true})
-        this.props.scriptsChange(values).then(() => {
-          this.setState({
-            editItem: null,
-            loading: false
-          })
-          this.props.form.setFieldsValue({
-            sql: ''
-          })
-          this.props.scriptSubmit(values)
-        })
-      }
-    })
-  }
-
-  selectScript = (value, option) => {
-    let _sql = this.props.form.getFieldValue('sql')
-    if (_sql) {
-      _sql = _sql + ` 
-
-      `
-    }
-
-    _sql = _sql.replace(/\s{6}$/, '')
-    _sql = _sql + `/*${option.props.children}*/
-    `
-    _sql = _sql.replace(/\s{4}$/, '')
-    _sql = _sql + value
-
-    this.props.form.setFieldsValue({
-      sql: _sql
-    })
-  }
-
-  render() {
-    const { systemScripts, setting } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { usefulFields } = this.state
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <Form {...formItemLayout} className="modal-menu-setting-script">
-        <Row gutter={24}>
-          {setting.tableName ? <Col span={8}>
-            <Form.Item label={'琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
-              {setting.tableName}
-            </Form.Item>
-          </Col> : null}
-          <Col span={16}>
-            <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
-              ErrorCode, retmsg
-            </Form.Item>
-          </Col>
-          <Col span={24} className="sqlfield">
-            <Form.Item label={'鍙敤瀛楁'}>
-              id, bid, loginuid, sessionuid, userid, appkey, time_id, orderBy{setting.laypage !== 'false' ? ', pageSize, pageIndex': ''}{usefulFields ? ', ' + usefulFields : ''}
-            </Form.Item>
-          </Col>
-          <Col span={10}>
-            <Form.Item label={'蹇嵎娣诲姞'} style={{marginBottom: 0}}>
-              <Select
-                showSearch
-                filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
-                onChange={this.selectScript}
-              >
-                {systemScripts.map((option, i) =>
-                  <Select.Option style={{whiteSpace: 'normal'}} key={i} value={option.value}>{option.name}</Select.Option>
-                )}
-              </Select>
-            </Form.Item>
-          </Col>
-          <Col span={6} className="add">
-            <Button onClick={this.handleConfirm} loading={this.state.loading} className="mk-green" style={{marginTop: 5, marginBottom: 15, marginLeft: 30}}>
-              淇濆瓨
-            </Button>
-            <Button onClick={this.handleCancel} style={{marginTop: 5, marginBottom: 15, marginLeft: 10}}>
-              鍙栨秷
-            </Button>
-          </Col>
-          <Col span={24} className="sql">
-            <Form.Item label={'sql'}>
-              {getFieldDecorator('sql', {
-                initialValue: '',
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['mob.required.input'] + 'sql!'
-                  }
-                ]
-              })(<CodeMirror />)}
-            </Form.Item>
-          </Col>
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(CustomForm)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/customscript/index.scss b/src/mob/datasource/verifycard/customscript/index.scss
deleted file mode 100644
index 2a1d2d8..0000000
--- a/src/mob/datasource/verifycard/customscript/index.scss
+++ /dev/null
@@ -1,34 +0,0 @@
-.modal-menu-setting-script {
-  .sqlfield {
-    .ant-form-item {
-      margin-bottom: 5px;
-    }
-    .ant-form-item-control {
-      line-height: 24px;
-    }
-    .ant-form-item-label {
-      line-height: 25px;
-    }
-    .ant-form-item-children {
-      line-height: 22px;
-    }
-    .ant-col-sm-8 {
-      width: 10.5%;
-    }
-    .ant-col-sm-16 {
-      width: 89.5%;
-    }
-  }
-  .sql {
-    .ant-col-sm-8 {
-      width: 10.5%;
-    }
-    .ant-col-sm-16 {
-      width: 89.5%;
-      padding-top: 4px;
-    }
-    .CodeMirror {
-      height: 350px;
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/index.jsx b/src/mob/datasource/verifycard/index.jsx
deleted file mode 100644
index d02dff0..0000000
--- a/src/mob/datasource/verifycard/index.jsx
+++ /dev/null
@@ -1,519 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { fromJS } from 'immutable'
-import { Form, Tabs, Table, Popconfirm, Icon, notification, Modal, Typography, Spin } from 'antd'
-import moment from 'moment'
-
-import Api from '@/api'
-import Utils from '@/utils/utils.js'
-
-import ColForm from './columnform'
-import CustomScriptsForm from './customscript'
-import SettingForm from './settingform'
-import SettingUtils from './utils'
-import './index.scss'
-
-const { TabPane } = Tabs
-const { Paragraph } = Typography
-
-class VerifyCard extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,     // 瀛楀吀椤�
-    card: PropTypes.object,     // 鏁版嵁婧愪俊鎭�
-    menuId: PropTypes.string,   // 鑿滃崟Id
-    searches: PropTypes.array,  // 鎼滅储鏉′欢
-  }
-
-  state = {
-    columns: [],
-    activeKey: 'setting',
-    loading: false,
-    initsql: '',          // sql楠岃瘉鏃跺彉閲忓0鏄庡強璧嬪��
-    usefulfields: '',
-    defaultsql: '',         // 榛樿Sql
-    systemScripts: [],
-    colColumns: [
-      {
-        title: '鍚嶇О',
-        dataIndex: 'label',
-        width: '25%'
-      },
-      {
-        title: '瀛楁',
-        dataIndex: 'field',
-        width: '25%'
-      },
-      {
-        title: '鏁版嵁绫诲瀷',
-        dataIndex: 'datatype',
-        width: '25%',
-      },
-      {
-        title: '鎿嶄綔',
-        align: 'center',
-        width: '25%',
-        dataIndex: 'operation',
-        render: (text, record) =>
-          (<div>
-            <span className="operation-btn" title={this.props.dict['mob.edit']} onClick={() => this.handleEdit(record, 'columns')} style={{color: '#1890ff'}}><Icon type="edit" /></span>
-            <Popconfirm
-              overlayClassName="popover-confirm"
-              title={this.props.dict['mob.query.delete']}
-              onConfirm={() => this.deleteColumn(record)
-            }>
-              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
-            </Popconfirm>
-          </div>)
-      }
-    ],
-    scriptsColumns: [
-      {
-        title: 'SQL',
-        dataIndex: 'sql',
-        width: '60%',
-        render: (text) => (
-          <Paragraph copyable ellipsis={{ rows: 5, expandable: true }}>{text}</Paragraph>
-        )
-      },
-      {
-        title: '鐘舵��',
-        dataIndex: 'status',
-        width: '20%',
-        render: (text, record) => record.status === 'false' ?
-          (
-            <div>
-              {this.props.dict['mob.status.forbidden']}
-              <Icon style={{marginLeft: '5px'}} type="stop" theme="twoTone" twoToneColor="#ff4d4f" />
-            </div>
-          ) :
-          (
-            <div>
-              {this.props.dict['mob.status.open']}
-              <Icon style={{marginLeft: '5px'}} type="check-circle" theme="twoTone" twoToneColor="#52c41a" />
-            </div>
-          )
-      },
-      {
-        title: '鎿嶄綔',
-        align: 'center',
-        width: '20%',
-        dataIndex: 'operation',
-        render: (text, record) =>
-          (<div>
-            <span className="operation-btn" title={this.props.dict['model.edit']} onClick={() => this.handleEdit(record, 'scripts')} style={{color: '#1890ff'}}><Icon type="edit" /></span>
-            <span className="operation-btn" onClick={() => this.handleUpDown(record, 'up')} style={{color: '#1890ff'}}><Icon type="arrow-up" /></span>
-            <span className="operation-btn" onClick={() => this.handleUpDown(record, 'down')} style={{color: '#ff4d4f'}}><Icon type="arrow-down" /></span>
-            <span className="operation-btn" title={this.props.dict['mob.status.change']} onClick={() => this.handleStatus(record)} style={{color: '#8E44AD'}}><Icon type="swap" /></span>
-            <Popconfirm
-              overlayClassName="popover-confirm"
-              title={this.props.dict['mob.query.delete']}
-              onConfirm={() => this.deleteScript(record)
-            }>
-              <span className="operation-btn" style={{color: '#ff4d4f'}}><Icon type="delete" /></span>
-            </Popconfirm>
-          </div>)
-      }
-    ]
-  }
-
-  UNSAFE_componentWillMount() {
-    const { card } = this.props
-
-    this.setState({
-      columns: fromJS(card.columns).toJS(),
-      setting: fromJS(card.setting).toJS(),
-      scripts: fromJS(card.scripts).toJS()
-    })
-  }
-
-  getsysScript = () => {
-    let _scriptSql = `Select distinct func+Remark as funcname,longparam, s.Sort from聽 s_custom_script s inner join (select OpenID from sapp where ID=@Appkey@) p on s.openid = case when s.appkey='' then s.openid else p.OpenID end order by s.Sort`
-
-    _scriptSql = Utils.formatOptions(_scriptSql)
-
-    let _sParam = {
-      func: 'sPC_Get_SelectedList',
-      LText: _scriptSql,
-      obj_name: 'data',
-      arr_field: 'funcname,longparam'
-    }
-    
-    _sParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-    _sParam.secretkey = Utils.encrypt(_sParam.LText, _sParam.timestamp)
-
-    _sParam.open_key = Utils.encryptOpenKey(_sParam.secretkey, _sParam.timestamp) // 浜戠鏁版嵁楠岃瘉
-    
-    Api.getSystemConfig(_sParam).then(res => {
-      if (res.status) {
-        let _scripts = []
-
-        res.data.forEach(item => {
-          let _item = {
-            name: item.funcname,
-            value: window.decodeURIComponent(window.atob(item.longparam))
-          }
-
-          _scripts.push(_item)
-        })
-
-        this.setState({
-          systemScripts: _scripts
-        })
-      } else {
-        notification.warning({
-          top: 92,
-          message: res.message,
-          duration: 5
-        })
-      }
-    })
-  }
-
-  columnChange = (values) => {
-    let columns = fromJS(this.state.columns).toJS()
-
-    if (values.uuid) {
-      columns = columns.map(item => {
-        if (item.uuid === values.uuid) {
-          return values
-        } else {
-          return item
-        }
-      })
-    } else {
-      values.uuid = Utils.getuuid()
-      columns.push(values)
-    }
-
-    this.setState({ columns })
-  }
-
-  deleteColumn = (record) => {
-    this.setState({ columns: this.state.columns.filter(item => item.uuid !== record.uuid) })
-  }
-
-  deleteScript = (record) => {
-    this.setState({ scripts: this.state.scripts.filter(item => item.uuid !== record.uuid) })
-  }
-
-  handleEdit = (record, type) => {
-    if (type === 'scripts') {
-      this.scriptsForm.edit(record)
-    } else if (type === 'columns') {
-      this.contrastForm.edit(record)
-    }
-
-    let node = document.getElementById('mob-verify-card-box-tab').parentNode
-
-    if (node && node.scrollTop) {
-      let inter = Math.ceil(node.scrollTop / 10)
-
-      let timer = setInterval(() => {
-        if (node.scrollTop - inter > 0) {
-          node.scrollTop = node.scrollTop - inter
-        } else {
-          node.scrollTop = 0
-          clearInterval(timer)
-        }
-      }, 10)
-    }
-  }
-
-  handleStatus = (record) => {
-    let scripts = fromJS(this.state.scripts).toJS()
-    record.status = record.status === 'false' ? 'true' : 'false'
-
-    scripts = scripts.map(item => {
-      if (item.uuid === record.uuid) {
-        return record
-      } else {
-        return item
-      }
-    })
-
-    this.setState({ scripts })
-  }
-
-  handleUpDown = (record, direction) => {
-    let scripts = fromJS(this.state.scripts).toJS()
-    let index = 0
-
-    scripts = scripts.filter((item, i) => {
-      if (item.uuid === record.uuid) {
-        index = i
-      }
-
-      return item.uuid !== record.uuid
-    })
-    if ((index === 0 && direction === 'up') || (index === scripts.length && direction === 'down')) {
-      return
-    }
-
-    if (direction === 'up') {
-      scripts.splice(index - 1, 0, record)
-    } else {
-      scripts.splice(index + 1, 0, record)
-    }
-
-    this.setState({ scripts })
-  }
-
-  scriptsChange = (values) => {
-    let scripts = fromJS(this.state.scripts).toJS()
-
-    if (values.uuid) {
-      scripts = scripts.map(item => {
-        if (item.uuid === values.uuid) {
-          return values
-        } else {
-          return item
-        }
-      })
-    } else {
-      scripts.push(values)
-    }
-
-    return new Promise((resolve, reject) => {
-      this.sqlverify(resolve, reject, false, scripts)
-    })
-  }
-
-  scriptSubmit = (values) => {
-    let scripts = fromJS(this.state.scripts).toJS()
-
-    if (values.uuid) {
-      scripts = scripts.map(item => {
-        if (item.uuid === values.uuid) {
-          return values
-        } else {
-          return item
-        }
-      })
-    } else {
-      values.uuid = Utils.getuuid()
-      scripts.push(values)
-    }
-
-    this.setState({ scripts })
-  }
-
-  changeTab = (val) => {
-    const { activeKey } = this.state
-
-    this.setState({loading: true})
-    if (activeKey === 'setting') {
-      this.settingForm.handleConfirm().then(res => {
-        this.setState({
-          setting: res
-        }, () => {
-          this.sqlverify(() => { // 楠岃瘉鎴愬姛
-            this.setState({
-              activeKey: val,
-              loading: false
-            })
-          }, () => {             // 楠岃瘉澶辫触
-            this.setState({
-              loading: false
-            })
-          }, true)
-        })
-      }, () => {
-        this.setState({loading: false})
-      })
-    } else if (activeKey === 'columns') {
-      this.sqlverify(() => { // 楠岃瘉鎴愬姛
-        this.setState({
-          activeKey: val,
-          loading: false
-        })
-      }, () => {             // 楠岃瘉澶辫触
-        this.setState({
-          loading: false
-        })
-      }, true)
-    } else if (activeKey === 'scripts') {
-      let _loading = false
-      if (this.scriptsForm && this.scriptsForm.state.editItem) {
-        _loading = true
-      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
-        _loading = true
-      }
-
-      if (_loading) {
-        notification.warning({
-          top: 92,
-          message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
-          duration: 5
-        })
-        this.setState({
-          loading: false
-        })
-        return
-      }
-
-      this.sqlverify(() => { // 楠岃瘉鎴愬姛
-        this.setState({
-          activeKey: val,
-          loading: false
-        })
-      }, () => {             // 楠岃瘉澶辫触
-        this.setState({
-          loading: false
-        })
-      }, true)
-    }
-  }
-
-  submitDataSource = () => {
-    const { card } = this.props
-    const { activeKey, setting, columns, scripts } = this.state
-
-    return new Promise((resolve, reject) => {
-      if (activeKey === 'setting') {
-        this.settingForm.handleConfirm().then(res => {
-          this.setState({
-            setting: res
-          }, () => {
-            this.sqlverify(() => { resolve({uuid: card.uuid, setting: res, columns, scripts }) }, reject, false)
-          })
-        }, () => {
-          reject()
-        })
-      } else if (activeKey === 'columns') {
-        this.sqlverify(() => { resolve({uuid: card.uuid, setting, columns, scripts }) }, reject, false)
-      } else if (activeKey === 'scripts') {
-        let _loading = false
-        if (this.scriptsForm && this.scriptsForm.state.editItem) {
-          _loading = true
-        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
-          _loading = true
-        }
-
-        if (_loading) {
-          notification.warning({
-            top: 92,
-            message: '瀛樺湪鏈繚瀛樿剼鏈紝璇风偣鍑荤‘瀹氫繚瀛橈紝鎴栫偣鍑诲彇娑堟斁寮冧慨鏀癸紒',
-            duration: 5
-          })
-          reject()
-          return
-        }
-
-        this.sqlverify(() => { resolve({uuid: card.uuid, setting, columns, scripts }) }, reject, false)
-      }
-    })
-  }
-
-  sqlverify = (resolve, reject, change = false, testScripts) => {
-    const { searches } = this.props
-    const { columns, setting, scripts } = this.state
-
-    let _scripts = scripts.filter(item => item.status !== 'false')
-
-    if (testScripts) {
-      _scripts = testScripts.filter(item => item.status !== 'false')
-    }
-    if (!change && setting.interType === 'system' && setting.execute === 'false' && _scripts.length === 0) {
-      notification.warning({
-        top: 92,
-        message: '涓嶆墽琛岄粯璁ql鏃讹紝璇锋坊鍔犺嚜瀹氫箟鑴氭湰锛�',
-        duration: 5
-      })
-      reject()
-      return
-    }
-
-    if ((setting.interType === 'system' && setting.execute !== 'false') || _scripts.length > 0) {
-      let param = {
-        func: 's_debug_sql',
-        exec_type: 'y',
-        LText: SettingUtils.getDebugSql(setting, _scripts, columns, searches)
-      }
-      param.LText = Utils.formatOptions(param.LText)
-      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-      param.secretkey = Utils.encrypt('', param.timestamp)
-      
-      Api.getLocalConfig(param).then(result => {
-        if (result.status) {
-          resolve()
-        } else {
-          reject()
-          Modal.error({
-            title: result.message
-          })
-        }
-      })
-    } else {
-      resolve()
-    }
-  }
-
-  /**
-   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
-   */
-  componentWillUnmount () {
-    this.setState = () => {
-      return
-    }
-  }
-
-  render() {
-    const { columns, setting, scripts, colColumns, scriptsColumns, activeKey, loading } = this.state
-
-    return (
-      <div id="mob-verify-card-box-tab">
-        {loading && <Spin size="large" />}
-        <Tabs activeKey={activeKey} className="verify-card-box" onChange={this.changeTab}>
-          <TabPane tab="鏁版嵁婧�" key="setting">
-            <SettingForm
-              menuId={this.props.menuId}
-              dict={this.props.dict}
-              columns={columns}
-              setting={setting}
-              scripts={scripts}
-              wrappedComponentRef={(inst) => this.settingForm = inst}
-            />
-          </TabPane>
-          <TabPane tab="瀛楁闆�" key="columns">
-            <ColForm
-              dict={this.props.dict}
-              columnChange={this.columnChange}
-              wrappedComponentRef={(inst) => this.contrastForm = inst}
-            />
-            <Table
-              bordered
-              rowKey="uuid"
-              className="custom-table"
-              dataSource={columns}
-              columns={colColumns}
-              pagination={false}
-            />
-          </TabPane>
-          <TabPane tab="鑷畾涔夎剼鏈�" key="scripts">
-            <CustomScriptsForm
-              setting={setting}
-              searches={this.props.searches}
-              initsql={this.state.initsql}
-              dict={this.props.dict}
-              customScripts={scripts}
-              systemScripts={this.state.systemScripts}
-              scriptsChange={this.scriptsChange}
-              scriptSubmit={this.scriptSubmit}
-              wrappedComponentRef={(inst) => this.scriptsForm = inst}
-            />
-            <Table
-              bordered
-              rowKey="uuid"
-              className="custom-table"
-              dataSource={scripts}
-              columns={scriptsColumns}
-              pagination={false}
-            />
-          </TabPane>
-        </Tabs>
-      </div>
-    )
-  }
-}
-
-export default Form.create()(VerifyCard)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/index.scss b/src/mob/datasource/verifycard/index.scss
deleted file mode 100644
index 6a7c5e3..0000000
--- a/src/mob/datasource/verifycard/index.scss
+++ /dev/null
@@ -1,74 +0,0 @@
-#mob-verify-card-box-tab {
-  .ant-spin {
-    position: absolute;
-    left: calc(50% - 16px);
-    top: 220px;
-    z-index: 1;
-  }
-  .verify-card-box {
-    .ant-tabs-nav-scroll {
-      text-align: center;
-    }
-    .ant-tabs-content {
-      min-height: 40vh;
-    }
-    table tr td {
-      word-wrap: break-word;
-      word-break: break-word;
-    }
-    .verify-form {
-      .ant-input-number {
-        width: 100%;
-      }
-      .sql {
-        .ant-col-sm-8 {
-          width: 10.5%;
-        }
-        .ant-col-sm-16 {
-          width: 89.5%;
-          padding-top: 4px;
-        }
-      }
-      .sqlfield {
-        .ant-form-item {
-          margin-bottom: 5px;
-        }
-        .ant-form-item-control {
-          line-height: 24px;
-        }
-        .ant-form-item-label {
-          line-height: 25px;
-        }
-        .ant-form-item-children {
-          line-height: 22px;
-        }
-        .ant-col-sm-8 {
-          width: 10.5%;
-        }
-        .ant-col-sm-16 {
-          width: 89.5%;
-        }
-      }
-      .add {
-        padding-top: 4px;
-      }
-      .anticon-question-circle {
-        color: #c49f47;
-        margin-right: 3px;
-      }
-    }
-    .custom-table .ant-empty {
-      margin: 20px 8px!important;
-    }
-    .errorval {
-      display: inline-block;
-      width: 30px;
-    }
-    .operation-btn {
-      display: inline-block;
-      font-size: 16px;
-      padding: 0 5px;
-      cursor: pointer;
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/settingform/index.jsx b/src/mob/datasource/verifycard/settingform/index.jsx
deleted file mode 100644
index cfe861a..0000000
--- a/src/mob/datasource/verifycard/settingform/index.jsx
+++ /dev/null
@@ -1,334 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-// import { fromJS } from 'immutable'
-import { Form, Row, Col, Input, Radio, Select, Tooltip, Icon, notification } from 'antd'
-import moment from 'moment'
-
-import Api from '@/api'
-import Utils from '@/utils/utils.js'
-// import SettingUtils from './utils.jsx'
-import CodeMirror from '@/templates/zshare/codemirror'
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    menuId: PropTypes.string,    // 鑿滃崟Id
-    setting: PropTypes.object,   // 鏁版嵁婧愰厤缃�
-    columns: PropTypes.array,    // 鍒楄缃�
-    scripts: PropTypes.array,    // 鑷畾涔夎剼鏈�
-  }
-
-  state = {
-    interType: this.props.setting.interType || 'system',
-    structure: this.props.setting.structure || 'array'
-  }
-
-  handleConfirm = (otype) => {
-    const { setting } = this.props
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          // 鏁版嵁婧愬墠绔獙璇�
-          if (values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' && !values.dataresource) {
-            notification.warning({
-              top: 92,
-              message: '璇峰~鍐欏唴閮ㄥ嚱鏁版垨鏁版嵁婧愶紒',
-              duration: 5
-            })
-            reject()
-            return
-          } else if (values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' && values.dataresource) {
-            let _quot = values.dataresource.match(/'{1}/g)
-            let _lparen = values.dataresource.match(/\({1}/g)
-            let _rparen = values.dataresource.match(/\){1}/g)
-
-            _quot = _quot ? _quot.length : 0
-            _lparen = _lparen ? _lparen.length : 0
-            _rparen = _rparen ? _rparen.length : 0
-
-            if (_quot % 2 !== 0) {
-              notification.warning({
-                top: 92,
-                message: '鏁版嵁婧愪腑\'蹇呴』鎴愬鍑虹幇',
-                duration: 5
-              })
-              reject()
-              return
-            } else if (_lparen !== _rparen) {
-              notification.warning({
-                top: 92,
-                message: '鏁版嵁婧愪腑()蹇呴』鎴愬鍑虹幇',
-                duration: 5
-              })
-              reject()
-              return
-            } else if (/--/ig.test(values.dataresource)) {
-              notification.warning({
-                top: 92,
-                message: '鏁版嵁婧愪腑锛屼笉鍙嚭鐜板瓧绗� -- 锛屾敞閲婅鐢� /*鍐呭*/',
-                duration: 5
-              })
-              reject()
-              return
-            }
-
-            let error = Utils.verifySql(values.dataresource)
-
-            if (error) {
-              notification.warning({
-                top: 92,
-                message: '鏁版嵁婧愪腑涓嶅彲浣跨敤' + error,
-                duration: 5
-              })
-              reject()
-              return
-            }
-          }
-
-          // 鏁版嵁婧愪繚瀛�
-          if (
-            values.interType === 'inner' && !values.innerFunc && values.execute !== 'false' &&
-            /[^\s]+\s+[^\s]+/ig.test(values.dataresource) && setting.dataresource !== values.dataresource
-          ) {
-            let param = {
-              func: 's_DataSrc_Save',
-              LText: values.dataresource,
-              MenuID: this.props.menuId
-            }
-    
-            param.LText = Utils.formatOptions(param.LText)
-            param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-            param.secretkey = Utils.encrypt(param.LText, param.timestamp)
-    
-            Api.getLocalConfig(param)
-          }
-
-          resolve(values)
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  onRadioChange = (e, key) => {
-    let value = e.target.value
-
-    if (key === 'interType') {
-      this.setState({
-        interType: value
-      })
-    } else if (key === 'structure') {
-      this.setState({
-        structure: value
-      })
-    }
-  }
-
-  render() {
-    const { columns, setting } = this.props
-    const { getFieldDecorator } = this.props.form
-    const { interType, structure } = this.state
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-
-    return (
-      <div className="mob-datasource-setting-form-box">
-        <Form {...formItemLayout} className="mob-setting-form">
-          <Row gutter={24}>
-            <Col span={8}>
-              <Form.Item label="鍚嶇О">
-                {getFieldDecorator('name', {
-                  initialValue: setting.name,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['mob.required.input'] + '鍚嶇О!'
-                    },
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col>
-            <Col span={8}>
-              <Form.Item label="琛ㄥ悕">
-                {getFieldDecorator('tableName', {
-                  initialValue: setting.tableName,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['mob.required.input'] + '琛ㄥ悕!'
-                    },
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col>
-            <Col span={8}>
-              <Form.Item label="鏁版嵁缁撴瀯">
-                {getFieldDecorator('structure', {
-                  initialValue: structure,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['mob.required.select'] + '琛ㄥ悕!'
-                    },
-                  ]
-                })(
-                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'structure')}}>
-                  <Radio value="array">鏁扮粍</Radio>
-                  <Radio value="field">瀛楁</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col>
-            <Col span={8}>
-              <Form.Item label="鎺ュ彛绫诲瀷">
-                {getFieldDecorator('interType', {
-                  initialValue: interType,
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['mob.required.select'] + '鎺ュ彛绫诲瀷!'
-                    },
-                  ]
-                })(
-                <Radio.Group onChange={(e) => {this.onRadioChange(e, 'interType')}}>
-                  <Radio value="inner">鍐呴儴</Radio>
-                  <Radio value="outer">澶栭儴</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col>
-            {interType === 'inner' ? <Col span={8}>
-              <Form.Item label="鍐呴儴鍑芥暟">
-                {getFieldDecorator('innerFunc', {
-                  initialValue: setting.innerFunc || '',
-                  rules: [
-                    
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col> : null}
-            {interType === 'outer' ? <Col span={8}>
-              <Form.Item label="鎺ュ彛鍦板潃">
-                {getFieldDecorator('interface', {
-                  initialValue: setting.interface || '',
-                  rules: [
-                    {
-                      required: true,
-                      message: this.props.dict['mob.required.input'] + '鎺ュ彛鍦板潃!'
-                    },
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col> : null}
-            {interType === 'outer' ? <Col span={8}>
-              <Form.Item label="澶栭儴鍑芥暟">
-                {getFieldDecorator('outerFunc', {
-                  initialValue: setting.outerFunc || '',
-                  rules: [
-
-                  ]
-                })(<Input placeholder={''} autoComplete="off" />)}
-              </Form.Item>
-            </Col> : null}
-            {interType === 'inner' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
-              <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={
-                <Tooltip placement="topLeft" title={'浣跨敤绯荤粺鍑芥暟鏃讹紝闇�濉啓鏁版嵁婧愩�傛敞锛氭暟鎹潈闄愭浛鎹㈢ $@ -> /* 鎴� \'\'銆� @$ -> */ 鎴� \'\''}>
-                  <Icon type="question-circle" />
-                  鏁版嵁婧�
-                </Tooltip>
-              }>
-                {getFieldDecorator('dataresource', {
-                  initialValue: setting.dataresource || ''
-                })(<CodeMirror />)}
-              </Form.Item>
-            </Col> : null}
-            {interType === 'inner' ? <Col span={8}>
-              <Form.Item label={
-                <Tooltip placement="topLeft" title={'鏌ヨ鏃讹紝鎼滅储鏉′欢浠here鏉′欢鎷兼帴杩涘叆sql锛岀粺璁℃椂锛屽皢鏁版嵁婧愪腑浠モ�淍+鎼滅储瀛楁+@鈥濈殑鍐呭锛屼互鎼滅储鏉′欢涓殑鍊艰繘琛屾浛鎹㈠悗锛屾彁浜ゆ煡璇紝娉細鏌ヨ绫诲瀷浠呭湪浣跨敤绯荤粺鍑芥暟鏃舵湁鏁堛��'}>
-                  <Icon type="question-circle" />
-                  鏌ヨ绫诲瀷
-                </Tooltip>
-              }>
-                {getFieldDecorator('queryType', {
-                  initialValue: setting.queryType || 'query'
-                })(
-                <Radio.Group>
-                  <Radio value="query">鏌ヨ</Radio>
-                  <Radio value="statistics">缁熻</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col> : null}
-            {structure === 'array' ? <Col span={8}>
-              <Form.Item label="涓婚敭">
-                {getFieldDecorator('primaryKey', {
-                  initialValue: setting.primaryKey || ''
-                })(
-                  <Select onChange={(value) => {this.selectChange('primaryKey', value)}}>
-                    {columns.map((option, i) =>
-                      <Select.Option key={i} value={option.value}>
-                        {option.text}
-                      </Select.Option>
-                    )}
-                  </Select>
-                )}
-              </Form.Item>
-            </Col> : null}
-            {structure === 'array' ? <Col span={8}>
-              <Form.Item label="榛樿鎺掑簭">
-                {getFieldDecorator('order', {
-                  initialValue: setting.order || ''
-                })(<Input placeholder={'ID asc, UID desc'} autoComplete="off" />)}
-              </Form.Item>
-            </Col> : null}
-            {structure === 'array' ? <Col span={8}>
-              <Form.Item label="鍒嗛〉">
-                {getFieldDecorator('laypage', {
-                  initialValue: setting.laypage || 'true'
-                })(
-                <Radio.Group>
-                  <Radio value="true">鏄�</Radio>
-                  <Radio value="false">鍚�</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col> : null}
-            
-            {interType === 'inner' ? <Col span={8}>
-              <Form.Item label="榛樿sql">
-                {getFieldDecorator('execute', {
-                  initialValue: setting.execute || 'true'
-                })(
-                <Radio.Group>
-                  <Radio value="true">鎵ц</Radio>
-                  <Radio value="false">涓嶆墽琛�</Radio>
-                </Radio.Group>)}
-              </Form.Item>
-            </Col> : null}
-            <Col span={8}>
-              <Form.Item label="鎼滅储鏉′欢">
-                {getFieldDecorator('search', {
-                  initialValue: setting.search || 'true'
-                })(
-                <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/datasource/verifycard/settingform/index.scss b/src/mob/datasource/verifycard/settingform/index.scss
deleted file mode 100644
index f9bc3f3..0000000
--- a/src/mob/datasource/verifycard/settingform/index.scss
+++ /dev/null
@@ -1,22 +0,0 @@
-.mob-datasource-setting-form-box {
-  position: relative;
-
-  .mob-setting-form {
-    .data-source {
-      .ant-form-item-label {
-        width: 11%;
-      }
-      .ant-form-item-control-wrapper {
-        width: 89%;
-      }
-      .CodeMirror {
-        height: 150px;
-      }
-    }
-    .anticon-question-circle {
-      color: #c49f47;
-      margin-right: 3px;
-    }
-  }
-
-}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/settingform/utils.jsx b/src/mob/datasource/verifycard/settingform/utils.jsx
deleted file mode 100644
index a1e29d0..0000000
--- a/src/mob/datasource/verifycard/settingform/utils.jsx
+++ /dev/null
@@ -1,84 +0,0 @@
-
-export default class SettingUtils {
-  /**
-   * @description 鐢熸垚椤甸潰鏌ヨ璇彞
-   * @return {String}  arr_field     鏄剧ず鍒楀瓧娈�
-   * @return {String}  search        鎼滅储鏉′欢
-   * @return {Object}  setting       椤甸潰璁剧疆
-   * @return {Array}   regoptions    鎼滅储鏉′欢姝e垯鏇挎崲
-   */
-  static getDebugSql (setting, arr_field, regoptions, search) {
-    let sql = ''
-    let _dataresource = setting.dataresource
-    let _customScript = setting.customScript
-
-    if (setting.interType === 'system' && setting.default === 'false') {
-      _dataresource = ''
-    }
-    
-    if (_dataresource) {
-      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
-    }
-    if (_customScript) {
-      _customScript = _customScript.replace(/@\$|\$@/ig, '')
-    }
-    
-    // 姝e垯鏇挎崲
-    let _regoptions = regoptions.map(item => {
-      return {
-        reg: new RegExp('@' + item.key + '@', 'ig'),
-        value: `'${item.value}'`
-      }
-    })
-    let _search = search
-
-    if (setting.queryType === 'statistics' && _dataresource) {
-      _regoptions.forEach(item => {
-        _dataresource = _dataresource.replace(item.reg, item.value)
-      })
-
-      _search = ''
-    }
-
-    if (_customScript) {
-      _regoptions.push({
-        reg: new RegExp('@orderBy@', 'ig'),
-        value: setting.order
-      })
-      if (setting.laypage !== 'false') {
-        _regoptions.push({
-          reg: new RegExp('@pageSize@', 'ig'),
-          value: 10
-        }, {
-          reg: new RegExp('@pageIndex@', 'ig'),
-          value: 1
-        })
-      }
-      _regoptions.forEach(item => {
-        _customScript = _customScript.replace(item.reg, item.value)
-      })
-    }
-
-    // 鏁版嵁婧愬鐞�, 瀛樺湪鏄剧ず鍒楁椂 
-    if (arr_field && _dataresource) {
-      if (/\s/.test(_dataresource)) {
-        _dataresource = '(' + _dataresource + ') tb'
-      }
-
-      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
-    }
-
-    if (_customScript) {
-      sql = `${_customScript}
-        ${_dataresource}
-        aaa:
-        if @ErrorCode!=''
-          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
-      `
-    } else {
-      sql = _dataresource
-    }
-    
-    return sql
-  }
-}
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/utils.jsx b/src/mob/datasource/verifycard/utils.jsx
deleted file mode 100644
index 8a3cf2e..0000000
--- a/src/mob/datasource/verifycard/utils.jsx
+++ /dev/null
@@ -1,99 +0,0 @@
-
-export default class SettingUtils {
-  /**
-   * @description 鐢熸垚椤甸潰鏌ヨ璇彞
-   * @return {String}  scripts       鑷畾涔夎剼鏈�
-   * @return {String}  searches      鎼滅储鏉′欢
-   * @return {Object}  setting       椤甸潰璁剧疆
-   * @return {Array}   columns       鏄剧ず瀛楁
-   */
-  static getDebugSql (setting, scripts, columns, searches) {
-    let sql = ''
-    let _dataresource = ''
-    let _customScript = ''
-    let arr_field = columns.map(item => item.field).join(',')
-
-    if (scripts.length > 0) {
-      scripts.forEach(item => {
-        _customScript += `
-          ${item.sql}
-        `
-      })
-    }
-
-    if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
-        ${_customScript}
-      `
-    }
-
-    if (setting.interType === 'system' && setting.execute !== 'false') {
-      _dataresource = setting.dataresource
-    }
-    
-    if (_dataresource) {
-      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
-    }
-    if (_customScript) {
-      _customScript = _customScript.replace(/@\$|\$@/ig, '')
-    }
-    
-    // 姝e垯鏇挎崲
-    let _regoptions = searches.map(item => {
-      return {
-        reg: new RegExp('@' + item.key + '@', 'ig'),
-        value: `'${item.value}'`
-      }
-    })
-    let _search = ''
-
-    if (setting.queryType === 'statistics' && _dataresource) {
-      _regoptions.forEach(item => {
-        _dataresource = _dataresource.replace(item.reg, item.value)
-      })
-
-      _search = ''
-    }
-
-    if (_customScript) {
-      _regoptions.push({
-        reg: new RegExp('@orderBy@', 'ig'),
-        value: setting.order
-      })
-      if (setting.laypage !== 'false') {
-        _regoptions.push({
-          reg: new RegExp('@pageSize@', 'ig'),
-          value: 10
-        }, {
-          reg: new RegExp('@pageIndex@', 'ig'),
-          value: 1
-        })
-      }
-      _regoptions.forEach(item => {
-        _customScript = _customScript.replace(item.reg, item.value)
-      })
-    }
-
-    // 鏁版嵁婧愬鐞�, 瀛樺湪鏄剧ず鍒楁椂 
-    if (arr_field && _dataresource) {
-      if (/\s/.test(_dataresource)) {
-        _dataresource = '(' + _dataresource + ') tb'
-      }
-
-      _dataresource = `select ${setting.laypage !== 'false' ?  'top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
-    }
-
-    if (_customScript) {
-      sql = `${_customScript}
-        ${_dataresource}
-        aaa:
-        if @ErrorCode!=''
-          insert into tmp_err_retmsg (ID, ErrorCode, retmsg, CreateUserID) select @time_id@,@ErrorCode, @retmsg,@UserID@
-      `
-    } else {
-      sql = _dataresource
-    }
-    
-    return sql
-  }
-}
\ No newline at end of file
diff --git a/src/mob/header/index.jsx b/src/mob/header/index.jsx
index 34db685..38c5863 100644
--- a/src/mob/header/index.jsx
+++ b/src/mob/header/index.jsx
@@ -1,106 +1,28 @@
 import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { is, fromJS } from 'immutable'
-import { connect } from 'react-redux'
-import { withRouter } from 'react-router-dom'
-import {Dropdown, Menu, Icon, Modal, Tooltip, Button } from 'antd'
 
-import { logout } from '@/store/action'
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
 import avatar from '@/assets/img/avatar.jpg'
 import MainLogo from '@/assets/img/main-logo.png'
 import './index.scss'
 
-const { confirm } = Modal
-
 class MobHeader extends Component {
-  static propTpyes = {
-    view: PropTypes.string,
-    saveIng: PropTypes.any,
-    triggerSave: PropTypes.func,
-    jumpToManage: PropTypes.func
-  }
-
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     avatar: sessionStorage.getItem('CloudAvatar') || avatar,
     userName: sessionStorage.getItem('CloudUserName')
   }
 
-  logout = () => {
-    // 閫�鍑虹櫥褰�
-    let _this = this
-    confirm({
-      title: this.state.dict['mob.logout.hint'],
-      content: '',
-      onOk() {
-        sessionStorage.clear()
-        _this.props.logout()
-        _this.props.history.replace('/login')
-      },
-      onCancel() {}
-    })
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
   render () {
-    const { view } = this.props
-
     return (
       <header className="mob-header-container">
         <div className="header-logo"><img src={MainLogo} alt=""/></div>
-        {view === 'manage' ?
-          <div className="mob-manage-title">
-            搴旂敤绠$悊
-          </div> :null
-        }
-        {view === 'design' ?
-          <Menu
-            mode="inline"
-            theme="dark"
-            inlineCollapsed={this.state.collapsed}
-          >
-            <Menu.Item key="1">
-              <Tooltip placement="bottom" title="杩斿洖搴旂敤绠$悊">
-                <Icon type="arrow-left" onClick={this.props.jumpToManage} />
-              </Tooltip>
-            </Menu.Item>
-            <Menu.Item key="2">
-              <Tooltip placement="bottom" title="淇濆瓨">
-                <Button icon="save" loading={this.props.saveIng} onClick={this.props.triggerSave}></Button>
-              </Tooltip>
-            </Menu.Item>
-          </Menu> : null
-        }
-        <Dropdown className="header-setting" overlay={
-          <Menu>
-            <Menu.Item key="2" onClick={this.logout}>{this.state.dict['mob.logout']}</Menu.Item>
-          </Menu>
-        }>
-          <div>
-            <img src={this.state.avatar} alt=""/>
-            <span>
-              <span className="username">{this.state.userName}</span> <Icon type="down" />
-            </span>
-          </div>
-        </Dropdown>
+        <div className="header-user">
+          <img src={this.state.avatar} alt=""/>
+          <span>
+            <span className="username">{this.state.userName}</span>
+          </span>
+        </div>
       </header>
     )
   }
 }
 
-const mapStateToProps = () => {
-  return {}
-}
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    logout: () => dispatch(logout())
-  }
-}
-
-export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MobHeader))
\ No newline at end of file
+export default MobHeader
\ No newline at end of file
diff --git a/src/mob/header/index.scss b/src/mob/header/index.scss
index 1e07272..40ac737 100644
--- a/src/mob/header/index.scss
+++ b/src/mob/header/index.scss
@@ -1,7 +1,6 @@
 .mob-header-container {
   width: 100%;
   height: 48px;
-  padding-right: 320px;
   color: rgba(255, 255, 255, 0.65);
   position: fixed;
   top: 0px;
@@ -23,7 +22,7 @@
       max-height: 40px;
     }
   }
-  .header-setting {
+  .header-user {
     float: right;
     line-height: 48px;
     margin-right: 10px;
@@ -44,51 +43,6 @@
         text-overflow: ellipsis;
         white-space: nowrap;
       }
-    }
-  }
-  .mob-manage-title {
-    position: absolute;
-    left: calc(50vw - 45px);
-    color: #ffffff;
-    font-size: 16px;
-    line-height: 48px;
-    letter-spacing: 2px;
-  }
-  >.ant-menu {
-    float: left;
-    width: unset!important;
-    .ant-menu-item {
-      margin-bottom: 0!important;
-      float: left;
-      width: unset!important;
-      cursor: default;
-      .anticon-arrow-left {
-        height: 24px;
-        cursor: pointer;
-      }
-      .ant-btn {
-        color: #fff;
-        width: unset;
-        cursor: pointer;
-        height: 37px;
-        background: transparent;
-        border: 0;
-        .anticon-save {
-          margin-right: 0;
-        }
-      }
-      .ant-btn[ant-click-animating-without-extra-node="true"]::after {
-        display: none!important;
-      }
-      .ant-btn::before {
-        display: none!important;
-      }
-    }
-    .ant-menu-item.ant-menu-item-selected {
-      background-color: transparent;
-    }
-    .ant-menu-item:not(:last-child) {
-      border-right: 1px solid #353535;
     }
   }
 }
\ No newline at end of file
diff --git a/src/mob/home/index.jsx b/src/mob/home/index.jsx
index c3e627e..dde5d1f 100644
--- a/src/mob/home/index.jsx
+++ b/src/mob/home/index.jsx
@@ -22,7 +22,7 @@
   //   collapse: PropTypes.bool,
   // }
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
   }
 
   shouldComponentUpdate (nextProps, nextState) {
diff --git a/src/mob/mobcard/index.jsx b/src/mob/mobcard/index.jsx
deleted file mode 100644
index 9bcb24d..0000000
--- a/src/mob/mobcard/index.jsx
+++ /dev/null
@@ -1,222 +0,0 @@
-import React, {Component} from 'react'
-import { is, fromJS } from 'immutable'
-import PropTypes from 'prop-types'
-import { Card, Spin, Icon, Row, Col, Modal, notification } from 'antd'
-
-import Api from '@/api'
-import Utils from '@/utils/utils.js'
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import asyncComponent from '@/utils/asyncSpinComponent'
-import './index.scss'
-
-const { confirm } = Modal
-const MutilForm = asyncComponent(() => import('./mutilform'))
-
-class CardChart extends Component {
-  static propTpyes = {
-    jumpMenu: PropTypes.func  // 椤甸潰璺宠浆
-  }
-
-  state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    loading: true,
-    visible: false,
-    confirmloading: false,
-    data: [],
-    card: null
-  }
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  UNSAFE_componentWillMount() {
-    this.getMobCards()
-  }
-
-  getMobCards = () => {
-    let param = {
-      func: 's_get_kei'
-    }
-
-    Api.getCloudConfig(param).then(result => {
-      if (result.status) {
-        this.setState({
-          loading: false,
-          data: result.data.map(item => {
-            return {
-              uuid: item.ID,
-              keiNo: item.kei_no,
-              name: item.remark,
-              type: item.typename
-            }
-          })
-        })
-
-      } else {
-        this.setState({
-          loading: false
-        })
-        notification.warning({
-          top: 92,
-          message: result.message,
-          duration: 5
-        })
-      }
-    })
-  }
-
-  editCard = (card) => {
-    this.setState({
-      card: card || null,
-      visible: true
-    })
-  }
-
-  deleteCard = (card) => {
-    let _this = this
-
-    confirm({
-      title: '纭畾鍒犻櫎銆�' + card.name + '銆嬪悧锛�',
-      content: '',
-      onOk() {
-        return new Promise(resolve => {
-          let param = {
-            func: 's_kei_del',
-            ID: card.uuid,
-            kei_no: card.keiNo
-          }
-    
-          Api.getCloudConfig(param).then(result => {
-            if (result.status) {
-              notification.success({
-                top: 92,
-                message: '鍒犻櫎鎴愬姛锛�',
-                duration: 5
-              })
-
-              _this.getMobCards()
-            } else {
-              notification.warning({
-                top: 92,
-                message: result.message,
-                duration: 5
-              })
-            }
-            resolve()
-          })
-        })
-      },
-      onCancel() {}
-    })
-  }
-
-  submitCard = () => {
-    const { card } = this.state
-
-    this.mobcardRef.handleConfirm().then(res => {
-      this.setState({
-        confirmloading: true
-      })
-
-      let param = {
-        func: 's_kei_addupt',
-        ID: card ? card.uuid : Utils.getuuid(),
-        TypeName: res.type,
-        remark: res.name,
-        kei_no: res.keiNo
-      }
-
-      Api.getCloudConfig(param).then(result => {
-        if (result.status) {
-          notification.success({
-            top: 92,
-            message: card ? '淇敼鎴愬姛锛�' : '娣诲姞鎴愬姛锛�',
-            duration: 5
-          })
-
-          this.setState({
-            confirmloading: false,
-            visible: false,
-            loading: true
-          })
-          this.getMobCards()
-        } else {
-          this.setState({
-            confirmloading: false
-          })
-          notification.warning({
-            top: 92,
-            message: result.message,
-            duration: 5
-          })
-        }
-      }, () => {
-        this.setState({
-          confirmloading: false
-        })
-      })
-    })
-  }
-
-  render() {
-    const { data, loading, card, dict } = this.state
-
-    return (
-      <div className="mob-card-row-box">
-        {loading ?
-          <div className="loading-mask">
-            <div className="ant-spin-blur"></div>
-            <Spin />
-          </div> : null
-        }
-        <Row gutter={24}>
-          {data && data.length > 0 &&
-            data.map(item => (
-              <Col key={item.uuid} span={6}>
-                <Card
-                  size="small"
-                  className="chart-card"
-                  actions={[
-                    <Icon title="edit" onClick={() => this.editCard(item)} type="edit" />,
-                    <Icon title="delete" onClick={() => this.deleteCard(item)} type="close" />,
-                    <Icon title="detail" onClick={() => this.props.jumpMenu(item)} type="arrow-right" />
-                  ]}
-                >
-                  <div className="mk-mob-card-title">
-                    {item.name}
-                  </div>
-                  <div className="mk-mob-card-type">
-                    {item.type === 'mob' ? '绉诲姩绔�' : 'PC绔�'}
-                  </div>
-                </Card>
-              </Col>
-            ))
-          }
-          
-          <Col span={6} key="insert">
-            <div className="chart-card insert" onClick={() => this.editCard()}>
-              <Icon type="plus" />
-            </div>
-          </Col>
-        </Row>
-        <Modal
-          className="mob-card-modal"
-          title={card ? '缂栬緫搴旂敤' : '鏂板缓搴旂敤'}
-          width={'600px'}
-          maskClosable={false}
-          visible={this.state.visible}
-          onCancel={() => this.setState({visible: false})}
-          confirmLoading={this.state.confirmloading}
-          onOk={this.submitCard}
-          destroyOnClose
-        >
-          <MutilForm card={card} dict={dict} wrappedComponentRef={(inst) => this.mobcardRef = inst} inputSubmit={this.submitCard} />
-        </Modal>
-      </div>
-    )
-  }
-}
-
-export default CardChart
\ No newline at end of file
diff --git a/src/mob/mobcard/index.scss b/src/mob/mobcard/index.scss
deleted file mode 100644
index 54c55cc..0000000
--- a/src/mob/mobcard/index.scss
+++ /dev/null
@@ -1,64 +0,0 @@
-.mob-card-row-box {
-  padding: 40px 20px;
-  position: relative;
-
-  .chart-card {
-    height: 250px;
-    border: 1px solid #e8e8e8;
-    .ant-card-body {
-      height: 201px;
-      .mk-mob-card-title {
-        margin-top: 40px;
-        font-size: 24px;
-        font-weight: 600;
-        text-align: center;
-      }
-      .mk-mob-card-type {
-        font-size: 14px;
-        color: rgba(0, 0 , 0, 0.55);
-        text-align: center;
-      }
-    }
-
-    .ant-card-actions > li > span {
-      > .anticon-close:hover {
-        color: #ff4d4f;
-      }
-      > .anticon-arrow-right:hover {
-        color: rgb(38, 194, 129);
-      }
-    }
-  }
-
-  .chart-card.insert {
-    text-align: center;
-    cursor: pointer;
-
-    .anticon-plus {
-      color: rgb(38, 194, 129);
-      font-size: 100px;
-      line-height: 250px;
-    }
-  }
-
-  .loading-mask {
-    position: absolute;
-    left: 20px;
-    top: 0;
-    right: 20px;
-    bottom: 0;
-    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;
-    }
-  }
-}
diff --git a/src/mob/mobcard/mutilform/index.jsx b/src/mob/mobcard/mutilform/index.jsx
deleted file mode 100644
index 0ba42e2..0000000
--- a/src/mob/mobcard/mutilform/index.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, Select, Radio } from 'antd'
-import './index.scss'
-
-class MainSearch extends Component {
-  static propTpyes = {
-    dict: PropTypes.object,      // 瀛楀吀椤�
-    card: PropTypes.any,         // 缂栬緫搴旂敤
-    inputSubmit: PropTypes.func  // input鍥炶溅鎻愪氦
-  }
-
-  state = {}
-
-  /**
-   * @description 鑾峰彇琛ㄥ崟鍊�
-   */
-  handleConfirm = () => {
-    return new Promise(resolve => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          resolve(values)
-        }
-      })
-    })
-  }
-
-  /**
-   * @description 鍥炶溅鎻愪氦
-   */
-  handleSubmit = (e) => {
-    e.preventDefault()
-    this.props.inputSubmit()
-  }
-
-  render() {
-    const { card } = this.props
-    const { getFieldDecorator } = this.props.form
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-    return (
-      <Form {...formItemLayout} className="mob-card-edit-form">
-        <Row gutter={24}>
-          <Col span={24}>
-            <Form.Item label="搴旂敤鍚�">
-              {getFieldDecorator('name', {
-                initialValue: card ? card.name : '',
-                rules: [{
-                  required: true,
-                  message: this.props.dict['mob.required.input'] + '搴旂敤鍚�!'
-                }, {
-                  max: 20,
-                  message: '搴旂敤鍚嶄笉鍙秴杩�20涓瓧绗�!'
-                }]
-              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label="搴旂敤绫诲瀷">
-              {getFieldDecorator('type', {
-                initialValue: card ? card.type : 'mob',
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['mob.required.select'] + '搴旂敤绫诲瀷!'
-                  }
-                ]
-              })(
-                <Select>
-                  <Select.Option value="mob">绉诲姩绔�</Select.Option>
-                  <Select.Option value="pc">PC绔�</Select.Option>
-                </Select>
-              )}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label="搴旂敤缂栫爜">
-              {getFieldDecorator('keiNo', {
-                initialValue: card ? card.keiNo : '',
-                rules: [{
-                  required: true,
-                  message: this.props.dict['mob.required.input'] + '搴旂敤缂栫爜!'
-                }, {
-                  pattern: /^[a-zA-Z_]*$/ig,
-                  message: '搴旂敤缂栫爜鍙厑璁稿寘鍚ぇ灏忓啓瀛楁瘝鍙奯!'
-                }, {
-                  max: 20,
-                  message: '搴旂敤缂栫爜涓嶅彲瓒呰繃20涓瓧绗�!'
-                }]
-              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <Form.Item label="鏉冮檺绠$悊">
-              {getFieldDecorator('role_manage', {
-                initialValue: card ? card.role_manage || 'false' : 'false',
-                rules: [{
-                  required: true,
-                  message: this.props.dict['mob.required.select'] + '鏄惁鍚敤鏉冮檺绠$悊!'
-                }]
-              })(
-                <Radio.Group>
-                  <Radio value="true">鍚敤</Radio>
-                  <Radio value="false">涓嶅惎鐢�</Radio>
-                </Radio.Group>
-              )}
-            </Form.Item>
-          </Col>
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(MainSearch)
\ No newline at end of file
diff --git a/src/mob/mobshell/card.jsx b/src/mob/mobshell/card.jsx
index 5b7a510..30225a7 100644
--- a/src/mob/mobshell/card.jsx
+++ b/src/mob/mobshell/card.jsx
@@ -2,56 +2,86 @@
 import { useDrag, useDrop } from 'react-dnd'
 
 import asyncComponent from '@/utils/asyncComponent'
-
 import './index.scss'
 
-// const Home = asyncComponent(() => import('@/mob/home'))
-const MobLogin1 = asyncComponent(() => import('@/mob/components/login/mob-login-1'))
-const MobLogin2 = asyncComponent(() => import('@/mob/components/login/mob-login-2'))
+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 AntvTabs = asyncComponent(() => import('@/menu/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'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
+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 NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
+const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
 
-const Card = ({ id, card, moveCard, findCard, editId, editCard, delCard, doubleClickCard, updateConfig }) => {
+const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
   const originalIndex = findCard(id).index
   const [{ isDragging }, drag] = useDrag({
-    item: { type: 'mob', id, originalIndex },
+    item: { type: 'menu', id, originalIndex, floor: card.floor },
     collect: monitor => ({
       isDragging: monitor.isDragging(),
     }),
   })
   const [, drop] = useDrop({
-    accept: 'mob',
+    accept: 'menu',
     canDrop: () => true,
     drop: (item) => {
-      const { id: draggedId, originalIndex } = item
-
+      const { id: draggedId, originalIndex, floor } = item
       if (originalIndex === undefined) {
         item.dropTargetId = id
-      } else if (draggedId && draggedId !== 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 && card.type !== 'login') {
+  if (isDragging) {
     style = { opacity: 0.3}
-  }
-  if (card.type === 'login') {
-    style.height = '100%'
   }
 
   const getCardComponent = () => {
-    if (card.type === 'login') {
-      if (card.subtype === 'mob-login-1') {
-        return (<MobLogin1 card={card} triggerEdit={editCard} editId={editId} onDoubleClick={doubleClickCard} updateConfig={updateConfig} />)
-      } else if (card.subtype === 'mob-login-2') {
-        return (<MobLogin2 card={card} triggerEdit={editCard} editId={editId} onDoubleClick={doubleClickCard} updateConfig={updateConfig} />)
-      }
+    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 === 'form') {
+      return (<NormalForm 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 === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
     }
   }
-
   return (
-    <div className="mk-component-card" ref={node => drag(drop(node))} style={style}>
+    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
       {getCardComponent()}
     </div>
   )
diff --git a/src/mob/mobshell/index.jsx b/src/mob/mobshell/index.jsx
index 2dd4b00..ee23621 100644
--- a/src/mob/mobshell/index.jsx
+++ b/src/mob/mobshell/index.jsx
@@ -1,23 +1,23 @@
 import React, { useState } from 'react'
 import { useDrop } from 'react-dnd'
-import { is, fromJS } from 'immutable'
 import update from 'immutability-helper'
-import { message, Empty } from 'antd'
+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 Container = ({config, editId, handleList, editCard, deleteCard, doubleClickCard }) => {
-  const [cards, setCards] = useState(config.components)
+const { confirm } = Modal
+
+const Container = ({menu, handleList }) => {
+  const [cards, setCards] = useState(menu.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)
+    handleList({...menu, components: _cards})
+    setCards(_cards)
   }
   
   const findCard = id => {
@@ -29,27 +29,94 @@
   }
 
   const updateConfig = (element) => {
-    handleList({...config, components: cards.map(item => item.uuid === element.uuid ? element : item)})
+    const _cards = cards.map(item => item.uuid === element.uuid ? element : item)
+    handleList({...menu, components: _cards})
+    setCards(_cards)
+  }
+
+  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() {
+        MKEmitter.emit('delButtons', uuids)
+        const _cards = cards.filter(item => item.uuid !== card.uuid)
+        handleList({...menu, components: _cards})
+        setCards(_cards)
+      },
+      onCancel() {}
+    })
   }
 
   const [, drop] = useDrop({
-    accept: 'mob',
+    accept: 'menu',
     drop(item) {
-      if (item.hasOwnProperty('originalIndex')) {
+      if (item.hasOwnProperty('originalIndex') || item.added) {
+        delete item.added // 鍒犻櫎缁勪欢娣诲姞鏍囪
         return
       }
 
-      if (cards.length > 0 && cards[0].type === 'login') {
-        message.warning('鐧诲綍椤典笉鍙坊鍔犲叾浠栧厓绱狅紒')
-        return
+      if (item.component === 'search') { // 鎼滅储缁勪欢涓嶅彲閲嶅娣诲姞
+        if (cards.filter(card => card.type === 'search').length > 0) {
+          notification.warning({
+            top: 92,
+            message: '鎼滅储鏉′欢涓嶅彲閲嶅娣诲姞锛�',
+            duration: 5
+          })
+          return
+        }
+      }
+
+      let name = ''
+      let names = {
+        bar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+        tabs: '鏍囩缁�',
+        pie: '楗煎浘',
+        search: '鎼滅储',
+        table: '琛ㄦ牸',
+        group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
+        carousel: '杞挱',
+        form: '琛ㄥ崟',
+        card: '鍗$墖'
+      }
+      let i = 1
+      
+      while (!name && names[item.component]) {
+        let _name = names[item.component] + i
+        if (menu.components.filter(com => com.name === _name).length === 0) {
+          name = _name
+        }
+        i++
       }
 
       let newcard = {
         uuid: Utils.getuuid(),
-        type: item.componentType,
+        type: item.component,
         subtype: item.subtype,
+        config: item.config,
+        width: item.width || 24,
+        dataName: Utils.getdataName(),
+        name: name,
+        floor: 1,   // 缁勪欢鐨勫眰绾�
+        isNew: true // 鏂版坊鍔犳爣蹇楋紝鐢ㄤ簬鍒濆鍖�
       }
-
+      
       let targetId = ''
 
       if (item.dropTargetId) {
@@ -59,29 +126,29 @@
         targetId = cards.slice(-1)[0].uuid
       }
 
-      const { index: overIndex } = findCard(`${targetId}`) // cards涓虹┖鏃� overIndex 涓� -1
+      const { index: overIndex } = findCard(`${targetId}`)
       const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
 
-      handleList({...config, components: _cards})
+      handleList({...menu, components: _cards})
+      setCards(_cards)
     }
   })
 
   return (
-    <div ref={drop} className="mob-shell-inner">
-      {cards.map(card => (
-        <Card
-          id={card.uuid}
-          key={card.uuid}
-          card={card}
-          editId={editId}
-          moveCard={moveCard}
-          editCard={editCard}
-          delCard={deleteCard}
-          findCard={findCard}
-          updateConfig={updateConfig}
-          doubleClickCard={doubleClickCard}
-        />
-      ))}
+    <div ref={drop} className="mob-shell-inner" id="menu-shell-inner" style={menu.style}>
+      <div className="ant-row">
+        {cards.map(card => (
+          <Card
+            id={card.uuid}
+            key={card.uuid}
+            card={card}
+            moveCard={moveCard}
+            delCard={deleteCard}
+            findCard={findCard}
+            updateConfig={updateConfig}
+          />
+        ))}
+      </div>
       {cards.length === 0 ?
         <Empty description="璇锋坊鍔犵粍浠�" /> : null
       }
@@ -89,3 +156,4 @@
   )
 }
 export default Container
+
diff --git a/src/mob/modelsource/dragsource/index.jsx b/src/mob/modelsource/dragsource/index.jsx
deleted file mode 100644
index 6ae90a6..0000000
--- a/src/mob/modelsource/dragsource/index.jsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import React from 'react'
-import { useDrag } from 'react-dnd'
-import { Tooltip } from 'antd'
-import './index.scss'
-
-const MobSourceElement = ({content}) => {
-  const [, drag] = useDrag({ item: content })
-  return (
-    <div ref={drag} className="mob-source-item" style={{backgroundImage: 'url(' + content.url + ')', ...content.style}}>
-      <Tooltip placement="right" overlayClassName="mob-source-tooltip-box" mouseEnterDelay={0.3} mouseLeaveDelay={0} title={
-        <div><img style={{width: '100%'}} src={content.url} alt=""/></div>
-      }>
-        <div className="tooltip-block"></div>
-      </Tooltip>
-    </div>
-  )
-}
-export default MobSourceElement
\ No newline at end of file
diff --git a/src/mob/modelsource/dragsource/index.scss b/src/mob/modelsource/dragsource/index.scss
deleted file mode 100644
index 9884d46..0000000
--- a/src/mob/modelsource/dragsource/index.scss
+++ /dev/null
@@ -1,25 +0,0 @@
-.mob-source-item {
-  display: inline-block;
-  width: 130px;
-  margin-right: 15px;
-  cursor: move;
-  height: 130px;
-  box-shadow: 0px 0px 3px #cdcdcd;
-
-  background-size: cover;
-  background-position: top center;
-  background-repeat: no-repeat;
-
-  .tooltip-block {
-    width: 100%;
-    height: 100%;
-    background: transparent;
-  }
-}
-
-.mob-source-tooltip-box {
-  margin-left: 20px;
-  .ant-tooltip-content {
-    width: 250px;
-  }
-}
\ No newline at end of file
diff --git a/src/mob/modelsource/index.jsx b/src/mob/modelsource/index.jsx
deleted file mode 100644
index 9adfe9c..0000000
--- a/src/mob/modelsource/index.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React, {Component} from 'react'
-import { is, fromJS } from 'immutable'
-import PropTypes from 'prop-types'
-
-import { mobOptions } from './option'
-import SourceWrap from './dragsource'
-import './index.scss'
-
-class CardChart extends Component {
-  static propTpyes = {
-    appType: PropTypes.string  // 搴旂敤绫诲瀷
-  }
-
-  state = {}
-
-  shouldComponentUpdate (nextProps, nextState) {
-    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
-  }
-
-  render() {
-    const { appType } = this.props
-
-    return (
-      <div className="mob-card-source-box">
-        {appType === 'mob' && mobOptions.map((item, index) => (
-          <div key={index}>
-            <p>{item.title}</p>
-            {item.options.map((cell, i) => (<SourceWrap key={i} content={cell} />))}
-          </div>
-        ))}
-      </div>
-    )
-  }
-}
-
-export default CardChart
\ No newline at end of file
diff --git a/src/mob/modelsource/option.jsx b/src/mob/modelsource/option.jsx
deleted file mode 100644
index 0123971..0000000
--- a/src/mob/modelsource/option.jsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import zhCN from '@/locales/zh-CN/mob.js'
-import enUS from '@/locales/en-US/mob.js'
-import mobLogin1 from '@/assets/mobimg/mob-login1.png'
-import mobLogin2 from '@/assets/mobimg/mob-login2.png'
-
-const _dict =  sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
-
-// 缁勪欢閰嶇疆淇℃伅
-export const mobOptions = [{
-  title: _dict['mob.login'],
-  sourceType: 'login',
-  options: [
-    { subtype: 'mob-login-1', componentType: 'login', type: 'mob', url: mobLogin1,  style: {} },
-    { subtype: 'mob-login-2', componentType: 'login', type: 'mob', url: mobLogin2,  style: {} },
-  ]
-}]
diff --git a/src/mob/modulesource/dragsource/index.jsx b/src/mob/modulesource/dragsource/index.jsx
new file mode 100644
index 0000000..0f13372
--- /dev/null
+++ b/src/mob/modulesource/dragsource/index.jsx
@@ -0,0 +1,15 @@
+import React from 'react'
+import { useDrag } from 'react-dnd'
+import { Icon } from 'antd'
+import './index.scss'
+
+const MobSourceElement = ({item, triggerDel}) => {
+  const [, drag] = useDrag({ item })
+  return (
+    <div className="menu-source-item">
+      <div className="property"><span>{item.title}</span>{item.config ? <Icon onClick={() => triggerDel(item)} type="close-circle" /> : null}</div>
+      <img ref={drag} src={item.url} alt=""/>
+    </div>
+  )
+}
+export default MobSourceElement
\ No newline at end of file
diff --git a/src/mob/modulesource/dragsource/index.scss b/src/mob/modulesource/dragsource/index.scss
new file mode 100644
index 0000000..ac891ba
--- /dev/null
+++ b/src/mob/modulesource/dragsource/index.scss
@@ -0,0 +1,46 @@
+.menu-source-item {
+  display: inline-block;
+  width: 100%;
+  margin-bottom: 15px;
+  height: auto;
+  min-height: 70px;
+
+  .property {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.65);
+    margin-bottom: 2px;
+
+    .anticon-close-circle {
+      opacity: 0;
+      cursor: pointer;
+      padding: 0 3px;
+      color: #ff4d4f;
+      transition: all 0.3s;
+    }
+  }
+
+  img {
+    width: 100%;
+    cursor: move;
+    box-shadow: 0px 0px 1px #1890ff;
+  }
+
+  .tooltip-block {
+    width: 100%;
+    height: 100%;
+    background: transparent;
+  }
+}
+
+.menu-source-item:hover .property {
+  .anticon-close-circle {
+    opacity: 1;
+  }
+}
+
+.menu-source-tooltip-box {
+  margin-left: 20px;
+  .ant-tooltip-content {
+    width: 250px;
+  }
+}
\ No newline at end of file
diff --git a/src/mob/modulesource/index.jsx b/src/mob/modulesource/index.jsx
new file mode 100644
index 0000000..f4705bf
--- /dev/null
+++ b/src/mob/modulesource/index.jsx
@@ -0,0 +1,93 @@
+import React, {Component} from 'react'
+import { is, fromJS } from 'immutable'
+import { Modal, notification } from 'antd'
+
+import Api from '@/api'
+import { menuOptions } from './option'
+import SourceWrap from './dragsource'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const { confirm } = Modal
+
+class ModelSource extends Component {
+  state = {
+    menuOptions: null,
+  }
+
+  UNSAFE_componentWillMount () {
+    const { components } = this.props
+    let options = []
+    
+    if (components) {
+      options = fromJS(components).toJS()
+    } else {
+      options = fromJS(menuOptions).toJS()
+    }
+
+    this.setState({
+      menuOptions: options
+    })
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    if (nextProps.components && !is(fromJS(this.props.components), fromJS(nextProps.components))) {
+      this.setState({
+        menuOptions: fromJS(nextProps.components).toJS()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  triggerDel = (item) => {
+    confirm({
+      title: `纭畾鍒犻櫎<${item.title}>鍚楋紵`,
+      content: '',
+      onOk() {
+        return new Promise(resolve => {
+          Api.getSystemConfig({
+            func: 's_custom_components_adduptdel',
+            c_id: item.uuid,
+            images: '',
+            c_name: item.title,
+            long_param: '',
+            del_type: 'Y'
+          }).then(result => {
+            if (result.status) {
+              notification.success({
+                top: 92,
+                message: '鍒犻櫎鎴愬姛锛�',
+                duration: 5
+              })
+
+              MKEmitter.emit('updateCustomComponent')
+            } else {
+              notification.warning({
+                top: 92,
+                message: result.message,
+                duration: 5
+              })
+            }
+            resolve()
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  render() {
+    const { menuOptions } = this.state
+
+    return (
+      <div className="mob-card-source-box">
+        {menuOptions.map((item, index) => (<SourceWrap key={index} item={item} triggerDel={this.triggerDel} />))}
+      </div>
+    )
+  }
+}
+
+export default ModelSource
\ No newline at end of file
diff --git a/src/mob/modelsource/index.scss b/src/mob/modulesource/index.scss
similarity index 78%
rename from src/mob/modelsource/index.scss
rename to src/mob/modulesource/index.scss
index db58054..cd91c8f 100644
--- a/src/mob/modelsource/index.scss
+++ b/src/mob/modulesource/index.scss
@@ -1,5 +1,5 @@
 .mob-card-source-box {
-  padding: 20px 0px 20px 15px;
+  padding: 20px 0px;
   position: relative;
 
   p {
diff --git a/src/mob/modulesource/option.jsx b/src/mob/modulesource/option.jsx
new file mode 100644
index 0000000..6260773
--- /dev/null
+++ b/src/mob/modulesource/option.jsx
@@ -0,0 +1,42 @@
+import bar from '@/assets/mobimg/bar.png'
+import bar1 from '@/assets/mobimg/bar1.png'
+import line from '@/assets/mobimg/line.png'
+import line1 from '@/assets/mobimg/line1.png'
+import tabs from '@/assets/mobimg/tabs.png'
+import group from '@/assets/mobimg/group.png'
+import card1 from '@/assets/mobimg/card1.png'
+import card2 from '@/assets/mobimg/card2.png'
+import TableCard from '@/assets/mobimg/table-card.png'
+import NormalTable from '@/assets/mobimg/normal-table.png'
+import Pie from '@/assets/mobimg/pie.png'
+import SandBox from '@/assets/mobimg/sandbox.png'
+import Pie1 from '@/assets/mobimg/ring.png'
+import Pie2 from '@/assets/mobimg/nightingale.png'
+import Mainsearch from '@/assets/mobimg/mainsearch.png'
+import Navbar from '@/assets/mobimg/navbar-mob.png'
+import Carousel from '@/assets/mobimg/carousel.png'
+import Carousel1 from '@/assets/mobimg/carousel1.png'
+import form from '@/assets/mobimg/form.png'
+
+// 缁勪欢閰嶇疆淇℃伅
+export const menuOptions = [
+  { type: 'menu', url: Navbar, component: 'navbar', subtype: 'mobnavbar', title: '瀵艰埅鏍�', width: 1200 },
+  { type: 'menu', url: tabs, component: 'tabs', subtype: 'tabs', title: '鏍囩椤�', width: 24 },
+  { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '鎼滅储鏉′欢', width: 24 },
+  { 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: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', 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 },
+  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '琛ㄦ牸', width: 24 },
+  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
+  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
+  { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '鏌辩姸鍥�', width: 24 },
+  { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
+  { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 24 },
+  { type: 'menu', url: Pie1, component: 'pie', subtype: 'ring', title: '鐜浘', width: 24 },
+  { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 },
+  { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '鍗椾竵鏍煎皵鍥�', width: 24 },
+  { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24 },
+]
diff --git a/src/pc/bgcontroller/index.jsx b/src/pc/bgcontroller/index.jsx
new file mode 100644
index 0000000..67d98f5
--- /dev/null
+++ b/src/pc/bgcontroller/index.jsx
@@ -0,0 +1,172 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Form, Icon } from 'antd'
+
+import zhCN from '@/locales/zh-CN/mob.js'
+import enUS from '@/locales/en-US/mob.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
+const StyleInput = asyncComponent(() => import('@/menu/stylecontroller/styleInput'))
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+
+class MobController extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    backgroundColor: '',
+    backgroundImage: '',
+  }
+
+  UNSAFE_componentWillMount () {
+    const { config } = this.props
+
+    let bgImg = config.style.backgroundImage || ''
+
+    if (bgImg && /^url/.test(bgImg)) {
+      bgImg = bgImg.replace('url(', '')
+      bgImg = bgImg.replace(')', '')
+    }
+
+    this.setState({
+      backgroundColor: config.style.backgroundColor,
+      backgroundImage: bgImg
+    })
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 淇敼鑳屾櫙棰滆壊 锛岄鑹叉帶浠�
+   */
+  changeBackgroundColor = (val) => {
+    let config = fromJS(this.props.config).toJS()
+
+    this.setState({
+      backgroundColor: val
+    })
+
+    config.style.backgroundColor = val
+    this.props.updateConfig(config)
+  }
+
+  /**
+   * @description 淇敼鑳屾櫙棰滆壊 锛岄鑹叉帶浠�
+   */
+  changePadding = (val, type) => {
+    let config = fromJS(this.props.config).toJS()
+
+    config.style[type] = val
+    this.props.updateConfig(config)
+  }
+
+
+  imgChange = (val) => {
+    this.setState({
+      backgroundImage: val
+    })
+
+    let config = fromJS(this.props.config).toJS()
+
+    if (val) {
+      config.style.backgroundImage = `url(${val})`
+    } else {
+      delete config.style.backgroundImage
+    }
+    this.props.updateConfig(config)
+  }
+
+  render () {
+    const { config } = this.props
+    const { backgroundColor, backgroundImage } = this.state
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 5 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 19 }
+      }
+    }
+
+    return (
+      <div className="pc-style-controller">
+        <Form {...formItemLayout}>
+          <Form.Item
+            colon={false}
+            label="瀹藉害"
+          >
+            <StyleInput defaultValue={config.style.width || '100%'} options={['px', '%', 'vw']} onChange={(val) => this.changePadding(val, 'width')}/>
+          </Form.Item>
+          <Form.Item className="color-control" colon={false} label="鑳屾櫙鑹�">
+            <ColorSketch value={backgroundColor} onChange={this.changeBackgroundColor} />
+          </Form.Item>
+          <Form.Item colon={false} label="鑳屾櫙鍥�">
+            <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
+          </Form.Item>
+          <p style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>鍐呰竟璺�</p>
+          <Form.Item
+            colon={false}
+            label={<Icon title="涓婅竟璺�" type="arrow-up"/>}
+          >
+            <StyleInput defaultValue={config.style.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingTop')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="涓嬭竟璺�" type="arrow-down"/>}
+          >
+            <StyleInput defaultValue={config.style.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingBottom')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="宸﹁竟璺�" type="arrow-left"/>}
+          >
+            <StyleInput defaultValue={config.style.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingLeft')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="鍙宠竟璺�" type="arrow-right"/>}
+          >
+            <StyleInput defaultValue={config.style.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'paddingRight')}/>
+          </Form.Item>
+          <p style={{borderBottom: '1px solid #eaeaea', color: '#40a9ff'}}>澶栬竟璺�</p>
+          <Form.Item
+            colon={false}
+            label={<Icon title="涓婅竟璺�" type="arrow-up"/>}
+          >
+            <StyleInput defaultValue={config.style.marginTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginTop')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="涓嬭竟璺�" type="arrow-down"/>}
+          >
+            <StyleInput defaultValue={config.style.marginBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginBottom')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="宸﹁竟璺�" type="arrow-left"/>}
+          >
+            <StyleInput defaultValue={config.style.marginLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginLeft')}/>
+          </Form.Item>
+          <Form.Item
+            colon={false}
+            label={<Icon title="鍙宠竟璺�" type="arrow-right"/>}
+          >
+            <StyleInput defaultValue={config.style.marginRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changePadding(val, 'marginRight')}/>
+          </Form.Item>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default MobController
\ No newline at end of file
diff --git a/src/pc/bgcontroller/index.scss b/src/pc/bgcontroller/index.scss
new file mode 100644
index 0000000..848da99
--- /dev/null
+++ b/src/pc/bgcontroller/index.scss
@@ -0,0 +1,19 @@
+.pc-style-controller {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  .color-control .ant-form-item-control {
+    padding-top: 10px;
+    line-height: 35px;
+  }
+  .mk-source-wrap {
+    height: 32px;
+    .mk-source-item-info {
+      top: 5px;
+    }
+  }
+  .ant-form-item label > .anticon {
+    font-size: 16px;
+    vertical-align: middle;
+  }
+}
diff --git a/src/pc/components/navbar/normal-navbar/index.jsx b/src/pc/components/navbar/normal-navbar/index.jsx
new file mode 100644
index 0000000..efa2b56
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/index.jsx
@@ -0,0 +1,237 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Icon, Popover, Menu, Button } from 'antd'
+
+import asyncIconComponent from '@/utils/asyncIconComponent'
+
+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 MenuComponent = asyncIconComponent(() => import('./menusetting'))
+const LinkComponent = asyncIconComponent(() => import('./linksetting'))
+
+const { SubMenu } = Menu
+
+class NormalNavbar extends Component {
+  static propTpyes = {
+    card: PropTypes.object,
+    deletecomponent: PropTypes.func,
+    updateConfig: PropTypes.func,
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    card: null,
+    back: false
+  }
+
+  UNSAFE_componentWillMount () {
+    const { card } = this.props
+
+    if (card.isNew) {
+      let _card = {
+        uuid: card.uuid,
+        type: card.type,
+        floor: card.floor,
+        dataName: card.dataName || '',
+        width: card.width || 24,
+        name: card.name,
+        subtype: card.subtype,
+        wrap: { name: card.name, width: card.width || 1200 },
+        logoStyle: { width: '100px' },
+        style: { },
+        links: [],
+        menus: [],
+      }
+
+      if (card.config) {
+        let config = fromJS(card.config).toJS()
+
+        _card.wrap = config.wrap
+        _card.wrap.name = card.name
+        _card.style = config.style
+      }
+
+      this.setState({
+        card: _card
+      })
+      this.props.updateConfig(_card)
+    } else {
+      this.setState({
+        card: fromJS(card).toJS()
+      })
+    }
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('submitStyle', this.getStyle)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('submitStyle', this.getStyle)
+  }
+  
+  /**
+   * @description 鍗$墖琛屽灞備俊鎭洿鏂帮紙鏁版嵁婧愶紝鏍峰紡绛夛級
+   */
+  updateComponent = (component) => {
+    this.setState({
+      card: component
+    })
+
+    component.width = component.wrap.width
+    component.name = component.wrap.name
+
+    this.props.updateConfig(component)
+  }
+
+  getStyle = (comIds, style) => {
+    const { card } = this.state
+
+    if (comIds[0] !== card.uuid) return
+
+    let _card = {...card}
+    if (comIds.length === 1) {
+      _card = {...card, style}
+    } else if (comIds[1] === 'logo') {
+      _card = {...card, logoStyle: style}
+    }
+
+    this.setState({
+      card: _card
+    })
+    
+    this.props.updateConfig(_card)
+  }
+
+  changeStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'shadow'], card.style)
+  }
+
+  changeLogoStyle = () => {
+    const { card } = this.state
+
+    MKEmitter.emit('changeStyle', [card.uuid, 'logo'], ['width', 'margin'], card.logoStyle)
+  }
+
+  clickComponent = (e) => {
+    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
+      e.stopPropagation()
+      MKEmitter.emit('clickComponent', this.state.card)
+    }
+  }
+
+  changeMenu = (menu) => {
+    MKEmitter.emit('changeEditMenu', {
+      fixed: menu.property === 'menu',
+      MenuID: menu.property === 'linkmenu' ? menu.linkMenuId : menu.MenuID,
+      copyMenuId: menu.property === 'menu' ? menu.copyMenuId : '',
+      MenuNo: menu.MenuNo,
+      MenuName: menu.name,
+    })
+  }
+
+  changeLogoMenu = () => {
+    const { card } = this.state
+
+    if (!card.wrap.logolink) return
+
+    MKEmitter.emit('changeEditMenu', {MenuID: card.wrap.logolink})
+  }
+
+  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}>
+        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+          <div className="mk-popover-control">
+            <MenuComponent config={card} updateConfig={this.updateComponent} />
+            <LinkComponent config={card} updateConfig={this.updateComponent} />
+            <WrapComponent config={card} updateConfig={this.updateComponent} />
+            <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeStyle} type="font-colors" />
+            <Icon className="close" title="鍒犻櫎缁勪欢" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
+          </div>
+        } trigger="hover">
+          <Icon type="tool" />
+        </Popover>
+        <div className="navbar-wrap" style={{width: card.wrap.width + 'px', height: card.wrap.height + 'px', lineHeight: card.wrap.height + 'px'}}>
+          {card.wrap.logo ? <Popover overlayClassName="mk-popover-control-wrap top-menu-popover" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
+            <div className="mk-popover-control">
+              <Icon className="style" title="璋冩暣鏍峰紡" onClick={this.changeLogoStyle} type="font-colors" />
+            </div>
+          } trigger="hover">
+            <div className={'logo' + (card.wrap.logolink ? ' pointer' : '')} style={card.logoStyle} onDoubleClick={this.changeLogoMenu}><img src={card.wrap.logo} alt=""/></div>
+          </Popover> : null}
+          <div className="menu">
+            <Menu mode="horizontal">
+              {card.menus.map(fst => {
+                if (fst.property === 'classify' && fst.sublist.length > 0) {
+                  return (
+                    <SubMenu title={fst.name} key={fst.MenuID} popupClassName="normal-navbar-submenu">
+                      {fst.sublist.map(scd => {
+                        if (scd.property === 'classify' && scd.sublist.length > 0) {
+                          return (
+                            <Menu.ItemGroup key={scd.MenuID} title={scd.name}>
+                              {scd.sublist.map(thd => {
+                                return (
+                                  <Menu.Item key={thd.MenuID} >
+                                    <span onClick={(e) => e.stopPropagation()} onDoubleClick={() => this.changeMenu(thd)}>{thd.name}</span>
+                                  </Menu.Item>
+                                )
+                              })}
+                            </Menu.ItemGroup>
+                          )
+                        } else {
+                          return (
+                            <Menu.Item key={scd.MenuID} onClick={(e) => e.stopPropagation()}>
+                              <span onClick={(e) => e.stopPropagation()} onDoubleClick={() => this.changeMenu(scd)}>{scd.name}</span>
+                            </Menu.Item>
+                          )
+                        }
+                      })}
+                    </SubMenu>
+                  )
+                } else {
+                  return (
+                    <Menu.Item key={fst.MenuID}>
+                      <span onClick={(e) => e.stopPropagation()} onDoubleClick={() => this.changeMenu(fst)}>{fst.name}</span>
+                    </Menu.Item>
+                  )
+                }
+              })}
+            </Menu>
+          </div>
+          <div className="link">
+            {card.links.map(link => {
+              return <Button type="link" key={link.MenuID} onDoubleClick={() => this.changeMenu(link)}>{link.name}</Button>
+            })}
+          </div>
+        </div>
+      </div>
+    )
+  }
+}
+
+export default NormalNavbar
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/index.scss b/src/pc/components/navbar/normal-navbar/index.scss
new file mode 100644
index 0000000..5882cf7
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/index.scss
@@ -0,0 +1,159 @@
+.normal-navbar-edit-box {
+  position: fixed;
+  top: 0px;
+  left: 0px;
+  width: 100%;
+  box-sizing: border-box;
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 50px;
+  z-index: 3;
+  
+  .navbar-wrap {
+    margin: 0 auto;
+    display: flex;
+    max-width: 100%;
+
+    .logo {
+      display: inline-block;
+      img {
+        max-width: 100%;
+        max-height: 100%;
+      }
+    }
+    .logo.pointer {
+      cursor: pointer;
+    }
+    .menu {
+      flex: 1;
+      display: inline-block;
+      font-size: inherit;
+      color: inherit;
+      .ant-menu {
+        background: transparent;
+        line-height: inherit;
+        font-size: inherit;
+        color: inherit;
+        border: 0;
+        .ant-menu-item:hover, .ant-menu-item-active, .ant-menu:not(.ant-menu-inline) .ant-menu-submenu-open, .ant-menu-submenu-active, .ant-menu-submenu-title:hover {
+          color: unset;
+        }
+        .ant-menu-item span {
+          display: inline-block;
+        }
+      }
+      .ant-menu-horizontal > .ant-menu-item:hover, .ant-menu-horizontal > .ant-menu-submenu:hover, .ant-menu-horizontal > .ant-menu-item-active, .ant-menu-horizontal > .ant-menu-submenu-active, .ant-menu-horizontal > .ant-menu-item-open, .ant-menu-horizontal > .ant-menu-submenu-open, .ant-menu-horizontal > .ant-menu-item-selected, .ant-menu-horizontal > .ant-menu-submenu-selected {
+        color: unset;
+      }
+    }
+    .link {
+      flex: 1;
+      display: inline-block;
+      text-align: right;
+      color: inherit;
+      button {
+        color: inherit;
+      }
+    }
+  }
+  .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: 25px;
+    top: 1px;
+    cursor: pointer;
+    padding: 5px;
+    background: rgba(255, 255, 255, 0.55);
+  }
+
+  .card-item {
+    overflow: hidden;
+    position: relative;
+    background-color: #ffffff;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+    min-height: 20px;
+  }
+  
+  .card-item:hover {
+    box-shadow: 0px 0px 2px #1890ff;
+  }
+
+  .model-menu-card-cell-list .card-detail-row > .anticon-plus {
+    position: absolute;
+    right: -30px;
+    font-size: 16px;
+  }
+  .model-menu-action-list {
+    line-height: 40px;
+    .ant-row > .anticon-plus {
+      position: absolute;
+      right: -30px;
+      font-size: 16px;
+    }
+  }
+  .card-add-button {
+    text-align: right;
+    clear: left;
+    .anticon-plus {
+      font-size: 20px;
+      color: #26C281;
+      padding: 5px;
+      margin-right: 10px;
+    }
+  }
+  .ant-pagination {
+    float: right;
+    margin: 10px;
+  }
+
+  .model-menu-action-list {
+    .page-card {
+      line-height: 55px;
+    }
+  }
+}
+.normal-navbar-edit-box::after {
+  display: block;
+  content: ' ';
+  clear: both;
+}
+.normal-navbar-edit-box:hover {
+  box-shadow: 0px 0px 4px #1890ff;
+}
+.top-menu-popover {
+  padding-top: 0!important;
+}
+.normal-navbar-submenu {
+  .ant-menu-item-group {
+    float: left;
+  }
+  .ant-menu-item {
+    height: 32px;
+    line-height: 32px;
+    span {
+      display: inline-block;
+      width: 100%;
+      height: 100%;
+      padding: 0 16px 0 28px;
+    }
+    padding: 0;
+  }
+  .ant-menu .ant-menu-item-selected {
+    background-color: #ffffff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/linksetting/index.jsx b/src/pc/components/navbar/normal-navbar/linksetting/index.jsx
new file mode 100644
index 0000000..28bc15c
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/linksetting/index.jsx
@@ -0,0 +1,61 @@
+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 LinkTable from './linktable'
+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
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+
+    this.props.updateConfig({...config, links: this.mTable.state.data || []})
+    this.setState({visible: false})
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict } = this.state
+
+    return (
+      <div className="model-link-setting-wrap">
+        <Icon type="link" title="閾炬帴" onClick={() => this.setState({ visible: true })}/>
+        <Modal
+          wrapClassName="popview-modal"
+          title="閾炬帴缂栬緫"
+          visible={visible}
+          width={950}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <LinkTable
+            links={config.links || []}
+            ref={(ref) => { this.mTable = ref }}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/linksetting/index.scss b/src/pc/components/navbar/normal-navbar/linksetting/index.scss
new file mode 100644
index 0000000..e12d9e9
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/linksetting/index.scss
@@ -0,0 +1,11 @@
+.model-link-setting-wrap {
+  display: inline-block;
+
+  >.anticon-link {
+    color: rgb(38, 194, 129);
+  }
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx b/src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx
new file mode 100644
index 0000000..7fc5a87
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx
@@ -0,0 +1,164 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon, Select } from 'antd'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class SettingForm extends Component {
+  static propTpyes = {
+    menu: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    property: this.props.menu.property || 'link',
+    appMenus: [],
+  }
+
+  UNSAFE_componentWillMount () {
+    let appMenus = sessionStorage.getItem('appMenus')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
+
+    this.setState({appMenus})
+  }
+
+  componentDidMount() {
+    const { menu } = this.props
+
+    if (!menu.MenuID) {
+      let _form = document.getElementById('name')
+      _form && _form.select()
+    }
+  }
+
+  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()
+    }
+  }
+
+  changeProperty = (e) => {
+    let val = e.target.value
+
+    this.setState({property: val})
+  }
+
+  render() {
+    const { menu } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { property, appMenus } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout}>
+        <Row gutter={24}>
+          <Col span={22}>
+            <Form.Item label="閾炬帴鍚嶇О">
+              {getFieldDecorator('name', {
+                initialValue: menu.name,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ラ摼鎺ュ悕绉�!'
+                  }
+                ]
+              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={22}>
+            <Form.Item label="閾炬帴灞炴��">
+              {getFieldDecorator('property', {
+                initialValue: menu.property || 'link'
+              })(
+                <Radio.Group onChange={this.changeProperty}>
+                  <Radio value="link">閾炬帴</Radio>
+                  <Radio value="linkmenu">鍏宠仈鑿滃崟</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {property === 'link' ? <Col span={22}>
+            <Form.Item label="閾炬帴鍦板潃">
+              {getFieldDecorator('link', {
+                initialValue: menu.link || '',
+                rules: [{
+                  required: true,
+                  message: '璇疯緭鍏ラ摼鎺ュ湴鍧�!'
+                }]
+              })(<TextArea rows={2} />)}
+            </Form.Item>
+          </Col> : null}
+          <Col span={22}>
+            <Form.Item label="鎵撳紑鏂瑰紡">
+              {getFieldDecorator('open', {
+                initialValue: menu.open || 'blank'
+              })(
+                <Radio.Group>
+                  <Radio value="blank">鏂扮獥鍙�</Radio>
+                  <Radio value="self">褰撳墠绐楀彛</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {property === 'linkmenu' ? <Col span={22}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍏宠仈褰撳墠app涓凡鏈夌殑鑿滃崟銆�">
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
+                鍏宠仈鑿滃崟
+              </Tooltip>
+            }>
+              {getFieldDecorator('linkMenuId', {
+                initialValue: menu.linkMenuId || '',
+                rules: [{
+                  required: true,
+                  message: '璇烽�夋嫨鍏宠仈鑿滃崟!'
+                }]
+              })(
+                <Select>
+                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
+                </Select>
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/columnform/index.scss b/src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss
similarity index 100%
copy from src/mob/datasource/verifycard/columnform/index.scss
copy to src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss
diff --git a/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx b/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx
new file mode 100644
index 0000000..f4a693c
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx
@@ -0,0 +1,160 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Table, Button, Modal, Icon } from 'antd'
+
+import LinkForm from '../linkform'
+import Utils from '@/utils/utils.js'
+import './index.scss'
+
+const { confirm } = Modal
+
+class LinkTable extends Component {
+  static propTpyes = {
+    links: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    editMenu: null,
+    columns: [
+      { title: '閾炬帴鍚嶇О', dataIndex: 'name', key: 'name' },
+      { title: '閾炬帴灞炴��', dataIndex: 'property', key: 'property',  render: text => {
+        const trans = {link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟'}
+
+        return trans[text]
+      }},
+      { title: '閾炬帴鍦板潃', dataIndex: 'link', key: 'link'},
+      { title: '鎵撳紑鏂瑰紡', dataIndex: 'open', key: 'open',  render: (text, record) => {
+        const trans = {blank: '鏂扮獥鍙�', self: '褰撳墠绐楀彛'}
+
+        return trans[text]
+      }},
+      { title: '鎿嶄綔', key: 'operation', align: 'center', width: '190px', render: (text, record) =>
+        (<div>
+          <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>缂栬緫</Button>
+          <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>鍒犻櫎</Button>
+          <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/>
+          <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/>
+        </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    const { links } = this.props
+
+    this.setState({data: fromJS(links).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  moveUp = (record) => {
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex - 1
+
+    if (hoverIndex === -1) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+  }
+
+  moveDown = (record) => {
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex + 1
+
+    if (hoverIndex === data.length) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+  }
+
+  delMenu = (record) => {
+    const { data } = this.state
+    const _this = this
+
+    confirm({
+      title: '纭畾鍒犻櫎鍚楋紵',
+      content: '',
+      onOk() {
+        _this.setState({data: data.filter(item => item.MenuID !== record.MenuID)})
+      },
+      onCancel() {}
+    })
+  }
+
+  editMenu = (record) => {
+    this.setState({editMenu: record, visible: true})
+  }
+
+  plusMenu = () => {
+    let _menu = {
+      name: '閾炬帴'
+    }
+
+    this.setState({editMenu: _menu, visible: true})
+  }
+
+  menuSubmit = () => {
+    const { editMenu, data } = this.state
+
+    this.menuRef.handleConfirm().then(res => {
+      let _menu = {...editMenu, ...res}
+      if (!_menu.MenuID) {
+        _menu.MenuID = Utils.getuuid()
+        this.setState({data: [...data, _menu], editMenu: null, visible: false})
+      } else {
+        this.setState({
+          editMenu: null,
+          visible: false,
+          data: data.map(item => {
+            if (item.MenuID === _menu.MenuID) {
+              return _menu
+            } else {
+              return item
+            }
+          })
+        })
+      }
+    })
+  }
+
+  render() {
+    const { columns, data, visible, editMenu } = this.state
+
+    return (
+      <div className="link-control-wrap">
+        <Button className="link-plus mk-green" onClick={this.plusMenu}>娣诲姞</Button>
+        <Table
+          rowKey="MenuID"
+          columns={columns}
+          dataSource={data}
+          pagination={false}
+        />
+        <Modal
+          title="缂栬緫"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.menuSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <LinkForm
+            menu={editMenu}
+            inputSubmit={this.menuSubmit}
+            wrappedComponentRef={(inst) => this.menuRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default LinkTable
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss b/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss
new file mode 100644
index 0000000..0fa8d83
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss
@@ -0,0 +1,20 @@
+.link-control-wrap {
+  position: relative;
+
+  .link-plus {
+    float: right;
+    position: relative;
+    z-index: 1;
+    margin-bottom: 5px;
+  }
+  .ant-empty {
+    margin: 5px 0;
+  }
+  thead tr {
+    background: #fbfbfb;
+  }
+
+  .ant-table-body {
+    margin: 0!important;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/index.jsx
new file mode 100644
index 0000000..18f00b1
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/index.jsx
@@ -0,0 +1,62 @@
+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 MenuTable from './menutable'
+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
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  verifySubmit = () => {
+    const { config } = this.props
+    let menus = this.mTable.state.data || []
+
+    this.props.updateConfig({...config, menus})
+    this.setState({visible: false})
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict } = this.state
+
+    return (
+      <div className="model-menu-setting-wrap">
+        <Icon type="menu" title="鑿滃崟" onClick={() => this.setState({ visible: true })}/>
+        <Modal
+          wrapClassName="popview-modal"
+          title="鑿滃崟缂栬緫"
+          visible={visible}
+          width={950}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuTable
+            menus={config.menus}
+            ref={(ref) => { this.mTable = ref }}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default DataSource
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/index.scss
new file mode 100644
index 0000000..3a906d8
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/index.scss
@@ -0,0 +1,11 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-menu {
+    color: purple;
+  }
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
new file mode 100644
index 0000000..fad6b76
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
@@ -0,0 +1,207 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Tooltip, Icon, Select } from 'antd'
+
+import './index.scss'
+
+const { TextArea } = Input
+
+class SettingForm extends Component {
+  static propTpyes = {
+    menu: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    property: this.props.menu.property || 'menu',
+    appMenus: [],
+  }
+
+  UNSAFE_componentWillMount () {
+    let appMenus = sessionStorage.getItem('appMenus')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
+
+    this.setState({appMenus})
+  }
+
+  componentDidMount() {
+    const { menu } = this.props
+
+    if (!menu.MenuID) {
+      let _form = document.getElementById('name')
+      _form && _form.select()
+    }
+  }
+
+  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()
+    }
+  }
+
+  changeProperty = (e) => {
+    let val = e.target.value
+
+    this.setState({property: val})
+  }
+
+  render() {
+    const { menu } = this.props
+    const { getFieldDecorator } = this.props.form
+    const { property, appMenus } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout}>
+        <Row gutter={24}>
+          <Col span={22}>
+            <Form.Item label="鑿滃崟鍚嶇О">
+              {getFieldDecorator('name', {
+                initialValue: menu.name,
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ヨ彍鍗曞悕绉�!'
+                  }
+                ]
+              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={22}>
+            <Form.Item label="鑿滃崟鍙傛暟">
+              {getFieldDecorator('MenuNo', {
+                initialValue: menu.MenuNo || '',
+                rules: [
+                  {
+                    required: true,
+                    message: '璇疯緭鍏ヨ彍鍗曞弬鏁�!'
+                  }
+                ]
+              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={22}>
+            <Form.Item label="鑿滃崟灞炴��">
+              {getFieldDecorator('property', {
+                initialValue: menu.property || 'menu'
+              })(
+                <Radio.Group onChange={this.changeProperty}>
+                  <Radio value="menu">鑿滃崟</Radio>
+                  <Radio value="link">閾炬帴</Radio>
+                  <Radio value="linkmenu">鍏宠仈鑿滃崟</Radio>
+                  {menu.level === 1 || menu.level === 2 ? <Radio value="classify">鍒嗙被</Radio> : null}
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={22}>
+            <Form.Item label="闅愯棌">
+              {getFieldDecorator('hidden', {
+                initialValue: menu.hidden || 'false'
+              })(
+                <Radio.Group>
+                  <Radio value="false">鍚�</Radio>
+                  <Radio value="true">鏄�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {property === 'link' ? <Col span={22}>
+            <Form.Item label="閾炬帴鍦板潃">
+              {getFieldDecorator('link', {
+                initialValue: menu.link || '',
+                rules: [{
+                  required: true,
+                  message: '璇疯緭鍏ラ摼鎺ュ湴鍧�!'
+                }]
+              })(<TextArea rows={2} />)}
+            </Form.Item>
+          </Col> : null}
+          {property !== 'classify' ? <Col span={22}>
+            <Form.Item label="鎵撳紑鏂瑰紡">
+              {getFieldDecorator('open', {
+                initialValue: menu.open || 'blank'
+              })(
+                <Radio.Group>
+                  <Radio value="blank">鏂扮獥鍙�</Radio>
+                  <Radio value="self">褰撳墠绐楀彛</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col> : null}
+          {property === 'linkmenu' ? <Col span={22}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="鍏宠仈褰撳墠app涓凡鏈夌殑鑿滃崟銆�">
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
+                鍏宠仈鑿滃崟
+              </Tooltip>
+            }>
+              {getFieldDecorator('linkMenuId', {
+                initialValue: menu.linkMenuId || '',
+                rules: [{
+                  required: true,
+                  message: '璇烽�夋嫨鍏宠仈鑿滃崟!'
+                }]
+              })(
+                <Select>
+                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
+                </Select>
+              )}
+            </Form.Item>
+          </Col> : null}
+          {property === 'menu' ? <Col span={22}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="澶嶅埗鑿滃崟浠呭湪褰撳墠鑿滃崟涓嶅瓨鍦ㄦ椂鏈夋晥銆�">
+                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
+                澶嶅埗鑿滃崟
+              </Tooltip>
+            }>
+              {getFieldDecorator('copyMenuId', {
+                initialValue: menu.copyMenuId || ''
+              })(
+                <Select>
+                  {appMenus.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
+                </Select>
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/mob/datasource/verifycard/columnform/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
similarity index 100%
copy from src/mob/datasource/verifycard/columnform/index.scss
copy to src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
new file mode 100644
index 0000000..92d751b
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
@@ -0,0 +1,549 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Table, Button, Modal, Icon } from 'antd'
+
+import MenuForm from '../menuform'
+import Utils from '@/utils/utils.js'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const { confirm } = Modal
+
+class ThdTable extends Component {
+  static propTpyes = {
+    menus: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    menuUpdate: PropTypes.func    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    editMenu: null,
+    columns: [
+      { title: '鑿滃崟鍚嶇О', dataIndex: 'name', key: 'name' },
+      { title: '鑿滃崟鍙傛暟', dataIndex: 'MenuNo', key: 'MenuNo' },
+      { title: '鑿滃崟灞炴��', dataIndex: 'property', key: 'property',  render: text => {
+        const trans = {menu: '鑿滃崟', link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟', classify: '鍒嗙被'}
+
+        return trans[text]
+      }},
+      { title: '鎵撳紑鏂瑰紡', dataIndex: 'open', key: 'open',  render: (text, record) => {
+        if (record.property === 'classify') return ''
+
+        const trans = {blank: '鏂扮獥鍙�', self: '褰撳墠绐楀彛'}
+
+        return trans[text]
+      }},
+      { title: '鏄惁闅愯棌', dataIndex: 'hidden', key: 'hidden',  render: (text, record) => {
+        const trans = {'true': '鏄�', 'false': '鍚�'}
+        return trans[text] || '鍚�'
+      }},
+      { title: '鎿嶄綔', key: 'operation', align: 'center', width: '190px', render: (text, record) =>
+        (<div>
+          <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>缂栬緫</Button>
+          <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>鍒犻櫎</Button>
+          <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/>
+          <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/>
+        </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    const { menu } = this.props
+    this.setState({data: menu.sublist ? fromJS(menu.sublist).toJS() : []})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  moveUp = (record) => {
+    const { menu } = this.props
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex - 1
+
+    if (hoverIndex === -1) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+    this.props.menuUpdate({...menu, sublist: data})
+  }
+
+  moveDown = (record) => {
+    const { menu } = this.props
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex + 1
+
+    if (hoverIndex === data.length) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+    this.props.menuUpdate({...menu, sublist: data})
+  }
+
+  delMenu = (record) => {
+    const { menu } = this.props
+    const _this = this
+    
+    confirm({
+      title: '纭畾鍒犻櫎鍚楋紵',
+      content: '',
+      onOk() {
+        let _data = _this.state.data.filter(item => item.MenuID !== record.MenuID)
+        _this.setState({data: _data})
+        _this.props.menuUpdate({...menu, sublist: _data})
+        MKEmitter.emit('delButtons', [record.MenuID])
+      },
+      onCancel() {}
+    })
+  }
+
+  editMenu = (record) => {
+    this.setState({editMenu: record, visible: true})
+  }
+
+  plusMenu = () => {
+    let _menu = {
+      name: '鑿滃崟',
+      property: 'menu',
+      level: 3,
+      sublist: []
+    }
+
+    this.setState({editMenu: _menu, visible: true})
+  }
+
+  menuSubmit = () => {
+    const { menu } = this.props
+    const { editMenu } = this.state
+
+    this.menuRef.handleConfirm().then(res => {
+      let _menu = {...editMenu, ...res}
+      let _data = this.state.data
+      if (!_menu.MenuID) {
+        _menu.MenuID = Utils.getuuid()
+        _data.push(_menu)
+      } else {
+        _data = _data.map(item => {
+          if (item.MenuID === _menu.MenuID) {
+            return _menu
+          } else {
+            return item
+          }
+        })
+      }
+      this.setState({data: _data, editMenu: null, visible: false})
+      this.props.menuUpdate({...menu, sublist: _data})
+    })
+  }
+
+  render() {
+    const { columns, data, visible, editMenu } = this.state
+
+    return (
+      <div className="thdmenu-control-wrap">
+        <Icon type="plus" style={{color: '#26C281', padding: '5px', fontSize: '16px'}} onClick={this.plusMenu}/>
+        <Table
+          rowKey="MenuID"
+          size="small"
+          columns={columns}
+          dataSource={data}
+          pagination={false}
+        />
+        <Modal
+          title="缂栬緫"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.menuSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuForm
+            menu={editMenu}
+            inputSubmit={this.menuSubmit}
+            wrappedComponentRef={(inst) => this.menuRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+class SubTable extends Component {
+  static propTpyes = {
+    menu: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    menuUpdate: PropTypes.func    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    editMenu: null,
+    columns: [
+      { title: '鑿滃崟鍚嶇О', dataIndex: 'name', key: 'name' },
+      { title: '鑿滃崟鍙傛暟', dataIndex: 'MenuNo', key: 'MenuNo' },
+      { title: '鑿滃崟灞炴��', dataIndex: 'property', key: 'property',  render: text => {
+        const trans = {menu: '鑿滃崟', link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟', classify: '鍒嗙被'}
+
+        return trans[text]
+      }},
+      { title: '鎵撳紑鏂瑰紡', dataIndex: 'open', key: 'open',  render: (text, record) => {
+        if (record.property === 'classify') return ''
+
+        const trans = {blank: '鏂扮獥鍙�', self: '褰撳墠绐楀彛'}
+
+        return trans[text]
+      }},
+      { title: '鏄惁闅愯棌', dataIndex: 'hidden', key: 'hidden',  render: (text, record) => {
+        const trans = {'true': '鏄�', 'false': '鍚�'}
+        return trans[text] || '鍚�'
+      }},
+      { title: '鎿嶄綔', key: 'operation', align: 'center', width: '190px', render: (text, record) =>
+        (<div>
+          <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>缂栬緫</Button>
+          <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>鍒犻櫎</Button>
+          <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/>
+          <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/>
+        </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    const { menu } = this.props
+
+    this.setState({data: menu.sublist ? fromJS(menu.sublist).toJS() : []})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  moveUp = (record) => {
+    const { menu } = this.props
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex - 1
+
+    if (hoverIndex === -1) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+    this.props.menuUpdate({...menu, sublist: data})
+  }
+
+  moveDown = (record) => {
+    const { menu } = this.props
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex + 1
+
+    if (hoverIndex === data.length) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+    this.props.menuUpdate({...menu, sublist: data})
+  }
+
+  delMenu = (record) => {
+    const { menu } = this.props
+    const _this = this
+    
+    confirm({
+      title: (record.property === 'classify' && record.sublist.length > 0 ? '鑿滃崟涓嬪惈鏈夊瓙鑿滃崟锛�' : '') + '纭畾鍒犻櫎鍚楋紵',
+      content: '',
+      onOk() {
+        let _data = _this.state.data.filter(item => item.MenuID !== record.MenuID)
+        _this.setState({data: _data})
+        _this.props.menuUpdate({...menu, sublist: _data})
+
+        let uuids = [record.MenuID]
+        record.sublist && record.sublist.forEach(item => {
+          uuids.push(item.MenuID)
+        })
+        MKEmitter.emit('delButtons', uuids)
+      },
+      onCancel() {}
+    })
+  }
+
+  editMenu = (record) => {
+    this.setState({editMenu: record, visible: true})
+  }
+
+  plusMenu = () => {
+    let _menu = {
+      name: '鑿滃崟',
+      property: 'classify',
+      level: 2,
+      sublist: []
+    }
+
+    this.setState({editMenu: _menu, visible: true})
+  }
+
+  menuSubmit = () => {
+    const { menu } = this.props
+    const { editMenu } = this.state
+
+    this.menuRef.handleConfirm().then(res => {
+      let _menu = {...editMenu, ...res}
+      let _data = this.state.data
+      if (!_menu.MenuID) {
+        _menu.MenuID = Utils.getuuid()
+        _data.push(_menu)
+      } else {
+        _data = _data.map(item => {
+          if (item.MenuID === _menu.MenuID) {
+            return _menu
+          } else {
+            return item
+          }
+        })
+      }
+      this.setState({data: _data, editMenu: null, visible: false})
+      this.props.menuUpdate({...menu, sublist: _data})
+    })
+  }
+
+  menuUpdate = (res) => {
+    const { menu } = this.props
+
+    let _data = this.state.data.map(item => {
+      if (item.MenuID === res.MenuID) {
+        return res
+      } else {
+        return item
+      }
+    })
+
+    this.setState({data: _data})
+    this.props.menuUpdate({...menu, sublist: _data})
+  }
+
+  render() {
+    const { columns, data, visible, editMenu } = this.state
+
+    return (
+      <div className="submenu-control-wrap">
+        <Icon type="plus" style={{color: '#26C281', padding: '5px', fontSize: '16px'}} onClick={this.plusMenu}/>
+        <Table
+          size="middle"
+          rowKey="MenuID"
+          columns={columns}
+          rowClassName={record => record.property}
+          expandedRowRender={record => <ThdTable menu={record} menuUpdate={this.menuUpdate} />}
+          dataSource={data}
+          pagination={false}
+        />
+        <Modal
+          title="缂栬緫"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.menuSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuForm
+            menu={editMenu}
+            inputSubmit={this.menuSubmit}
+            wrappedComponentRef={(inst) => this.menuRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+class MenuTable extends Component {
+  static propTpyes = {
+    menus: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+  }
+
+  state = {
+    data: [],
+    editMenu: null,
+    columns: [
+      { title: '鑿滃崟鍚嶇О', dataIndex: 'name', key: 'name' },
+      { title: '鑿滃崟鍙傛暟', dataIndex: 'MenuNo', key: 'MenuNo' },
+      { title: '鑿滃崟灞炴��', dataIndex: 'property', key: 'property',  render: text => {
+        const trans = {menu: '鑿滃崟', link: '閾炬帴', linkmenu: '鍏宠仈鑿滃崟', classify: '鍒嗙被'}
+
+        return trans[text]
+      }},
+      { title: '鎵撳紑鏂瑰紡', dataIndex: 'open', key: 'open',  render: (text, record) => {
+        if (record.property === 'classify') return ''
+
+        const trans = {blank: '鏂扮獥鍙�', self: '褰撳墠绐楀彛'}
+
+        return trans[text]
+      }},
+      { title: '鏄惁闅愯棌', dataIndex: 'hidden', key: 'hidden',  render: (text, record) => {
+        const trans = {'true': '鏄�', 'false': '鍚�'}
+        return trans[text] || '鍚�'
+      }},
+      { title: '鎿嶄綔', key: 'operation', align: 'center', width: '190px', render: (text, record) =>
+        (<div>
+          <Button type="link" style={{padding: '0 5px', marginRight: '5px'}} onClick={() => this.editMenu(record)}>缂栬緫</Button>
+          <Button type="link" style={{color: '#ff4d4f', padding: '0 5px', marginRight: '5px'}} onClick={() => this.delMenu(record)}>鍒犻櫎</Button>
+          <Icon type="arrow-up" style={{color: '#26C281', cursor: 'pointer', padding: '0 5px', marginRight: '5px'}} onClick={() => this.moveUp(record)}/>
+          <Icon type="arrow-down" style={{color: '#ff4d4f', cursor: 'pointer', padding: '0 5px'}} onClick={() => this.moveDown(record)}/>
+        </div>)
+      }
+    ]
+  }
+
+  UNSAFE_componentWillMount () {
+    const { menus } = this.props
+
+    this.setState({data: fromJS(menus).toJS()})
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  moveUp = (record) => {
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex - 1
+
+    if (hoverIndex === -1) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+  }
+
+  moveDown = (record) => {
+    let data = fromJS(this.state.data).toJS()
+
+    let dragIndex = data.findIndex(c => c.MenuID === record.MenuID)
+    let hoverIndex = dragIndex + 1
+
+    if (hoverIndex === data.length) return
+
+    data.splice(hoverIndex, 0, ...data.splice(dragIndex, 1))
+    this.setState({data})
+  }
+
+  delMenu = (record) => {
+    const { data } = this.state
+    const _this = this
+
+    confirm({
+      title: (record.property === 'classify' && record.sublist.length > 0 ? '鑿滃崟涓嬪惈鏈夊瓙鑿滃崟锛�' : '') + '纭畾鍒犻櫎鍚楋紵',
+      content: '',
+      onOk() {
+        _this.setState({data: data.filter(item => item.MenuID !== record.MenuID)})
+
+        let uuids = [record.MenuID]
+        record.sublist && record.sublist.forEach(item => {
+          uuids.push(item.MenuID)
+
+          item.sublist && item.sublist.forEach(cell => {
+            uuids.push(cell.MenuID)
+          })
+        })
+        MKEmitter.emit('delButtons', uuids)
+      },
+      onCancel() {}
+    })
+  }
+
+  editMenu = (record) => {
+    this.setState({editMenu: record, visible: true})
+  }
+
+  plusMenu = () => {
+    let _menu = {
+      name: '鑿滃崟',
+      property: 'classify',
+      level: 1,
+      sublist: []
+    }
+
+    this.setState({editMenu: _menu, visible: true})
+  }
+
+  menuSubmit = () => {
+    const { editMenu, data } = this.state
+
+    this.menuRef.handleConfirm().then(res => {
+      let _menu = {...editMenu, ...res}
+      if (!_menu.MenuID) {
+        _menu.MenuID = Utils.getuuid()
+        this.setState({data: [...data, _menu], editMenu: null, visible: false})
+      } else {
+        this.setState({
+          editMenu: null,
+          visible: false,
+          data: data.map(item => {
+            if (item.MenuID === _menu.MenuID) {
+              return _menu
+            } else {
+              return item
+            }
+          })
+        })
+      }
+    })
+  }
+
+  menuUpdate = (res) => {
+    const { data } = this.state
+
+    this.setState({
+      data: data.map(item => {
+        if (item.MenuID === res.MenuID) {
+          return res
+        } else {
+          return item
+        }
+      })
+    })
+  }
+
+  render() {
+    const { columns, data, visible, editMenu } = this.state
+
+    return (
+      <div className="menu-control-wrap">
+        <Button className="menu-plus mk-green" onClick={this.plusMenu}>娣诲姞</Button>
+        <Table
+          rowKey="MenuID"
+          columns={columns}
+          rowClassName={record => record.property}
+          expandedRowRender={record => <SubTable menu={record} menuUpdate={this.menuUpdate} />}
+          dataSource={data}
+          pagination={false}
+        />
+        <Modal
+          title="缂栬緫"
+          visible={visible}
+          width={600}
+          maskClosable={false}
+          onOk={this.menuSubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <MenuForm
+            menu={editMenu}
+            inputSubmit={this.menuSubmit}
+            wrappedComponentRef={(inst) => this.menuRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default MenuTable
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss
new file mode 100644
index 0000000..120837c
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss
@@ -0,0 +1,64 @@
+.menu-control-wrap {
+  position: relative;
+
+  .menu-plus {
+    float: right;
+    position: relative;
+    z-index: 1;
+    margin-bottom: 5px;
+  }
+  .ant-empty {
+    margin: 5px 0;
+  }
+  thead tr {
+    background: #fbfbfb;
+  }
+  tbody > tr:not(.ant-table-expanded-row) {
+    background: #ffffff;
+  }
+  tr:not(.classify) {
+    > td {
+      >.ant-table-row-expand-icon-cell {
+        div {
+          display: none;
+        }
+      }
+      >.ant-table-row-expand-icon {
+        display: none;
+      }
+    }
+  }
+  tr:not(.classify) + .ant-table-expanded-row {
+    display: none;
+  }
+  td[colspan="6"] {
+    padding: 5px 0px 5px 5px!important;
+  }
+  .ant-table-body {
+    margin: 0!important;
+  }
+
+  .submenu-control-wrap {
+    position: relative;
+
+    .anticon-plus {
+      position: absolute;
+      top: 8px;
+      right: 10px;
+      z-index: 1;
+    }
+
+    .thdmenu-control-wrap {
+      position: relative;
+      .ant-table-row-indent {
+        display: none;
+      }
+      .ant-table-row-expand-icon {
+        display: none;
+      }
+      .ant-table-small {
+        border: 0;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx b/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx
new file mode 100644
index 0000000..81346a6
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx
@@ -0,0 +1,83 @@
+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/pc/components/navbar/normal-navbar/wrapsetting/index.scss b/src/pc/components/navbar/normal-navbar/wrapsetting/index.scss
new file mode 100644
index 0000000..04372e6
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/index.scss
@@ -0,0 +1,7 @@
+.model-menu-setting-wrap {
+  display: inline-block;
+
+  >.anticon-edit {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
new file mode 100644
index 0000000..b5d097d
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
@@ -0,0 +1,184 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Tooltip, Icon, InputNumber, Select, Radio } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+    config: PropTypes.object,    // 鍗$墖琛屼俊鎭�
+    wrap: PropTypes.object,      // 鏁版嵁婧愰厤缃�
+    inputSubmit: PropTypes.func  // 鍥炶溅浜嬩欢
+  }
+
+  state = {
+    appMenus: []
+  }
+
+  UNSAFE_componentWillMount () {
+    let appMenus = sessionStorage.getItem('appMenus')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
+
+    this.setState({appMenus})
+  }
+
+  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 { appMenus } = 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('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="鑿滃崟鍙傛暟">
+                {getFieldDecorator('MenuNo', {
+                  initialValue: wrap.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="瀵艰埅鏍忎富浣撳唴瀹瑰搴︼紙鍖呮嫭logo銆佽彍鍗曘�侀摼鎺ョ瓑锛夈��">
+                  <Icon type="question-circle" />
+                  瀹藉害
+                </Tooltip>
+              }>
+                {getFieldDecorator('width', {
+                  initialValue: wrap.width || 1200,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '瀹藉害!'
+                    }
+                  ]
+                })(<InputNumber min={400} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="楂樺害">
+                {getFieldDecorator('height', {
+                  initialValue: wrap.height || 50,
+                  rules: [
+                    {
+                      required: true,
+                      message: this.props.dict['form.required.input'] + '楂樺害!'
+                    }
+                  ]
+                })(<InputNumber min={50} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="logo">
+                {getFieldDecorator('logo', {
+                  initialValue: wrap.logo
+                })(
+                  <SourceComponent type="image" />
+                )}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label="logo閾炬帴">
+                {getFieldDecorator('logolink', {
+                  initialValue: wrap.logolink || ''
+                })(
+                  <Select
+                    showSearch
+                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
+                  >
+                    <Select.Option key="empty" intid={''} value={''}>鏃�</Select.Option>
+                    {appMenus.map(option =>
+                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
+                    )}
+                  </Select>
+                )}
+              </Form.Item>
+            </Col>
+            <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title="瀛樺湪鐧诲綍涓斿彇鍒扮櫥褰曚俊鎭椂锛屾樉绀虹敤鎴峰ご鍍忋�佺敤鎴峰悕鍙婇��鍑恒��">
+                  <Icon type="question-circle" />
+                  鐢ㄦ埛淇℃伅
+                </Tooltip>
+              }>
+                {getFieldDecorator('user', {
+                  initialValue: wrap.user || 'hidden'
+                })(
+                  <Radio.Group>
+                    <Radio value="hidden">闅愯棌</Radio>
+                    <Radio value="show">鏄剧ず</Radio>
+                  </Radio.Group>
+                )}
+              </Form.Item>
+            </Col>
+          </Row>
+        </Form>
+      </div>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss
new file mode 100644
index 0000000..159130b
--- /dev/null
+++ b/src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss
@@ -0,0 +1,11 @@
+.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/pc/menushell/card.jsx b/src/pc/menushell/card.jsx
new file mode 100644
index 0000000..843c8c5
--- /dev/null
+++ b/src/pc/menushell/card.jsx
@@ -0,0 +1,99 @@
+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 AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
+const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
+const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
+const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
+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 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'))
+const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
+const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
+
+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}
+  }
+  let col = ' ant-col ant-col-' + (card.width || 24)
+  if (card.type === 'navbar') {
+    col = ''
+  }
+
+  const getCardComponent = () => {
+    if (card.type === 'bar' || card.type === 'line') {
+      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') {
+      return (<NormalForm 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 === '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 === 'code') {
+      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
+    }
+  }
+  return (
+    <div className={`mk-component-card ${col}`} ref={node => drag(drop(node))} style={style}>
+      {getCardComponent()}
+    </div>
+  )
+}
+export default Card
diff --git a/src/pc/menushell/index.jsx b/src/pc/menushell/index.jsx
new file mode 100644
index 0000000..29be748
--- /dev/null
+++ b/src/pc/menushell/index.jsx
@@ -0,0 +1,167 @@
+import React, { useState } from 'react'
+import { useDrop } from 'react-dnd'
+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 = ({menu, handleList }) => {
+  const [cards, setCards] = useState(menu.components)
+  const moveCard = (id, atIndex) => {
+    const { card, index } = findCard(id)
+    const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
+    handleList({...menu, components: _cards})
+    setCards(_cards)
+  }
+  
+  const findCard = id => {
+    const card = cards.filter(c => `${c.uuid}` === id)[0]
+    return {
+      card,
+      index: cards.indexOf(card),
+    }
+  }
+
+  const updateConfig = (element) => {
+    const _cards = cards.map(item => item.uuid === element.uuid ? element : item)
+    handleList({...menu, components: _cards})
+    setCards(_cards)
+  }
+
+  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() {
+        MKEmitter.emit('delButtons', uuids)
+        const _cards = cards.filter(item => item.uuid !== card.uuid)
+        handleList({...menu, components: _cards})
+        setCards(_cards)
+      },
+      onCancel() {}
+    })
+  }
+
+  const [, drop] = useDrop({
+    accept: 'menu',
+    drop(item) {
+      if (item.hasOwnProperty('originalIndex') || item.added) {
+        delete item.added // 鍒犻櫎缁勪欢娣诲姞鏍囪
+        return
+      }
+
+      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 === 'navbar') {
+        if (cards.filter(card => card.type === 'navbar').length > 0) {
+          notification.warning({
+            top: 92,
+            message: '瀵艰埅鏍忎笉鍙噸澶嶆坊鍔狅紒',
+            duration: 5
+          })
+          return
+        }
+      }
+
+      let name = ''
+      let names = {
+        bar: '鏌辩姸鍥�',
+        line: '鎶樼嚎鍥�',
+        tabs: '鏍囩缁�',
+        pie: '楗煎浘',
+        search: '鎼滅储',
+        table: '琛ㄦ牸',
+        group: '鍒嗙粍',
+        editor: '瀵屾枃鏈�',
+        code: '鑷畾涔�',
+        navbar: '瀵艰埅鏍�',
+        carousel: '杞挱',
+        card: '鍗$墖'
+      }
+      let i = 1
+      
+      while (!name && names[item.component]) {
+        let _name = names[item.component] + i
+        if (menu.components.filter(com => com.name === _name).length === 0) {
+          name = _name
+        }
+        i++
+      }
+
+      let newcard = {
+        uuid: Utils.getuuid(),
+        type: item.component,
+        subtype: item.subtype,
+        config: item.config,
+        width: item.width || 24,
+        dataName: Utils.getdataName(),
+        name: name,
+        floor: 1,   // 缁勪欢鐨勫眰绾�
+        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({...menu, components: _cards})
+      setCards(_cards)
+    }
+  })
+
+  return (
+    <div ref={drop} className="menu-shell-inner" id="menu-shell-inner" style={menu.style}>
+      <div className="ant-row">
+        {cards.map(card => (
+          <Card
+            id={card.uuid}
+            key={card.uuid}
+            card={card}
+            moveCard={moveCard}
+            delCard={deleteCard}
+            findCard={findCard}
+            updateConfig={updateConfig}
+          />
+        ))}
+      </div>
+      {cards.length === 0 ?
+        <Empty description="璇锋坊鍔犵粍浠�" /> : null
+      }
+    </div>
+  )
+}
+export default Container
diff --git a/src/pc/menushell/index.scss b/src/pc/menushell/index.scss
new file mode 100644
index 0000000..21f8a7d
--- /dev/null
+++ b/src/pc/menushell/index.scss
@@ -0,0 +1,23 @@
+.menu-shell-inner {
+  min-height: calc(100vh - 100px);
+  width: 100%;
+  background-size: 100%;
+
+  .anticon {
+    cursor: unset;
+  }
+
+  .mk-component-card {
+    position: relative;
+  }
+  
+  >.ant-empty {
+    padding-top: 150px;
+  }
+  .anticon-tool {
+    color: rgba(0, 0, 0, 0.55);
+  }
+  .anticon-tool:hover {
+    color: #1890ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/modulesource/dragsource/index.jsx b/src/pc/modulesource/dragsource/index.jsx
new file mode 100644
index 0000000..0f13372
--- /dev/null
+++ b/src/pc/modulesource/dragsource/index.jsx
@@ -0,0 +1,15 @@
+import React from 'react'
+import { useDrag } from 'react-dnd'
+import { Icon } from 'antd'
+import './index.scss'
+
+const MobSourceElement = ({item, triggerDel}) => {
+  const [, drag] = useDrag({ item })
+  return (
+    <div className="menu-source-item">
+      <div className="property"><span>{item.title}</span>{item.config ? <Icon onClick={() => triggerDel(item)} type="close-circle" /> : null}</div>
+      <img ref={drag} src={item.url} alt=""/>
+    </div>
+  )
+}
+export default MobSourceElement
\ No newline at end of file
diff --git a/src/pc/modulesource/dragsource/index.scss b/src/pc/modulesource/dragsource/index.scss
new file mode 100644
index 0000000..ac891ba
--- /dev/null
+++ b/src/pc/modulesource/dragsource/index.scss
@@ -0,0 +1,46 @@
+.menu-source-item {
+  display: inline-block;
+  width: 100%;
+  margin-bottom: 15px;
+  height: auto;
+  min-height: 70px;
+
+  .property {
+    font-size: 14px;
+    color: rgba(0, 0, 0, 0.65);
+    margin-bottom: 2px;
+
+    .anticon-close-circle {
+      opacity: 0;
+      cursor: pointer;
+      padding: 0 3px;
+      color: #ff4d4f;
+      transition: all 0.3s;
+    }
+  }
+
+  img {
+    width: 100%;
+    cursor: move;
+    box-shadow: 0px 0px 1px #1890ff;
+  }
+
+  .tooltip-block {
+    width: 100%;
+    height: 100%;
+    background: transparent;
+  }
+}
+
+.menu-source-item:hover .property {
+  .anticon-close-circle {
+    opacity: 1;
+  }
+}
+
+.menu-source-tooltip-box {
+  margin-left: 20px;
+  .ant-tooltip-content {
+    width: 250px;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/modulesource/index.jsx b/src/pc/modulesource/index.jsx
new file mode 100644
index 0000000..f4705bf
--- /dev/null
+++ b/src/pc/modulesource/index.jsx
@@ -0,0 +1,93 @@
+import React, {Component} from 'react'
+import { is, fromJS } from 'immutable'
+import { Modal, notification } from 'antd'
+
+import Api from '@/api'
+import { menuOptions } from './option'
+import SourceWrap from './dragsource'
+import MKEmitter from '@/utils/events.js'
+import './index.scss'
+
+const { confirm } = Modal
+
+class ModelSource extends Component {
+  state = {
+    menuOptions: null,
+  }
+
+  UNSAFE_componentWillMount () {
+    const { components } = this.props
+    let options = []
+    
+    if (components) {
+      options = fromJS(components).toJS()
+    } else {
+      options = fromJS(menuOptions).toJS()
+    }
+
+    this.setState({
+      menuOptions: options
+    })
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    if (nextProps.components && !is(fromJS(this.props.components), fromJS(nextProps.components))) {
+      this.setState({
+        menuOptions: fromJS(nextProps.components).toJS()
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  triggerDel = (item) => {
+    confirm({
+      title: `纭畾鍒犻櫎<${item.title}>鍚楋紵`,
+      content: '',
+      onOk() {
+        return new Promise(resolve => {
+          Api.getSystemConfig({
+            func: 's_custom_components_adduptdel',
+            c_id: item.uuid,
+            images: '',
+            c_name: item.title,
+            long_param: '',
+            del_type: 'Y'
+          }).then(result => {
+            if (result.status) {
+              notification.success({
+                top: 92,
+                message: '鍒犻櫎鎴愬姛锛�',
+                duration: 5
+              })
+
+              MKEmitter.emit('updateCustomComponent')
+            } else {
+              notification.warning({
+                top: 92,
+                message: result.message,
+                duration: 5
+              })
+            }
+            resolve()
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  render() {
+    const { menuOptions } = this.state
+
+    return (
+      <div className="mob-card-source-box">
+        {menuOptions.map((item, index) => (<SourceWrap key={index} item={item} triggerDel={this.triggerDel} />))}
+      </div>
+    )
+  }
+}
+
+export default ModelSource
\ No newline at end of file
diff --git a/src/mob/modelsource/index.scss b/src/pc/modulesource/index.scss
similarity index 78%
copy from src/mob/modelsource/index.scss
copy to src/pc/modulesource/index.scss
index db58054..cd91c8f 100644
--- a/src/mob/modelsource/index.scss
+++ b/src/pc/modulesource/index.scss
@@ -1,5 +1,5 @@
 .mob-card-source-box {
-  padding: 20px 0px 20px 15px;
+  padding: 20px 0px;
   position: relative;
 
   p {
diff --git a/src/pc/modulesource/option.jsx b/src/pc/modulesource/option.jsx
new file mode 100644
index 0000000..8e94aea
--- /dev/null
+++ b/src/pc/modulesource/option.jsx
@@ -0,0 +1,44 @@
+import bar from '@/assets/mobimg/bar.png'
+import bar1 from '@/assets/mobimg/bar1.png'
+import line from '@/assets/mobimg/line.png'
+import line1 from '@/assets/mobimg/line1.png'
+import tabs from '@/assets/mobimg/tabs.png'
+import group from '@/assets/mobimg/group.png'
+import card1 from '@/assets/mobimg/card1.png'
+import card2 from '@/assets/mobimg/card2.png'
+import TableCard from '@/assets/mobimg/table-card.png'
+import NormalTable from '@/assets/mobimg/normal-table.png'
+import Pie from '@/assets/mobimg/pie.png'
+import Editor from '@/assets/mobimg/editor.png'
+import SandBox from '@/assets/mobimg/sandbox.png'
+import Pie1 from '@/assets/mobimg/ring.png'
+import Pie2 from '@/assets/mobimg/nightingale.png'
+import Mainsearch from '@/assets/mobimg/mainsearch.png'
+import Navbar from '@/assets/mobimg/navbar.png'
+import Carousel from '@/assets/mobimg/carousel.png'
+import Carousel1 from '@/assets/mobimg/carousel1.png'
+import form from '@/assets/mobimg/form.png'
+
+// 缁勪欢閰嶇疆淇℃伅
+export const menuOptions = [
+  { type: 'menu', url: Navbar, component: 'navbar', subtype: 'navbar', title: '瀵艰埅鏍�', width: 1200 },
+  { type: 'menu', url: tabs, component: 'tabs', subtype: 'tabs', title: '鏍囩椤�', width: 24 },
+  { type: 'menu', url: Mainsearch, component: 'search', subtype: 'mainsearch', title: '鎼滅储鏉′欢', width: 24 },
+  { 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: form, component: 'form', subtype: 'stepform', title: '琛ㄥ崟', 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 },
+  { type: 'menu', url: TableCard, component: 'table', subtype: 'tablecard', title: '琛ㄦ牸', width: 12 },
+  { type: 'menu', url: line, component: 'line', subtype: 'line', title: '鎶樼嚎鍥�', width: 24 },
+  { type: 'menu', url: line1, component: 'line', subtype: 'line1', title: '闃舵鎶樼嚎鍥�', width: 24 },
+  { type: 'menu', url: bar, component: 'bar', subtype: 'bar', title: '鏌辩姸鍥�', width: 24 },
+  { type: 'menu', url: bar1, component: 'bar', subtype: 'bar1', title: '鏉″舰鍥�', width: 24 },
+  { type: 'menu', url: Pie, component: 'pie', subtype: 'pie', title: '楗煎浘', width: 12 },
+  { type: 'menu', url: Pie1, component: 'pie', subtype: 'ring', title: '鐜浘', width: 12 },
+  { type: 'menu', url: Editor, component: 'editor', subtype: 'brafteditor', title: '瀵屾枃鏈�', width: 24 },
+  { type: 'menu', url: SandBox, component: 'code', subtype: 'sandbox', title: '鑷畾涔�', width: 24 },
+  { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '鍗椾竵鏍煎皵鍥�', width: 12 },
+  { type: 'menu', url: group, component: 'group', subtype: 'normalgroup', title: '鍒嗙粍', width: 24 },
+]
diff --git a/src/pc/quotecomponent/index.jsx b/src/pc/quotecomponent/index.jsx
new file mode 100644
index 0000000..11426ac
--- /dev/null
+++ b/src/pc/quotecomponent/index.jsx
@@ -0,0 +1,123 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { Button, Modal, notification } from 'antd'
+
+import zhCN from '@/locales/zh-CN/model.js'
+import enUS from '@/locales/en-US/model.js'
+import SettingForm from './settingform'
+import Api from '@/api'
+import './index.scss'
+
+class Quotecomponent extends Component {
+  static propTpyes = {
+    config: PropTypes.any,
+    updateConfig: PropTypes.func
+  }
+
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    visible: false,
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  verifySubmit = () => {
+    let config = fromJS(this.props.config).toJS()
+
+    this.verifyRef.handleConfirm().then(res => {
+      let exit = false
+      config.components.forEach(item => {
+        if (item.type === res.keys_type) {
+          exit = true
+        }
+      })
+
+      if (exit) {
+        let msg = ''
+        if (res.keys_type === 'navbar') {
+          msg = '瀵艰埅鏍忓凡瀛樺湪锛�'
+        }
+        notification.warning({
+          top: 92,
+          message: msg,
+          duration: 5
+        })
+        return
+      }
+
+      Api.getSystemConfig({
+        func: 'sPC_Get_LongParam',
+        TypeCharOne: sessionStorage.getItem('kei_no'),
+        typename: 'pc',
+        MenuID: res.keys_id
+      }).then(result => {
+        if (!result.status) {
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+          return
+        }
+        let _config = null
+        try {
+          _config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
+        } catch (e) {
+          console.warn('Parse Failure')
+          _config = null
+        }
+
+        if (!_config) {
+          notification.warning({
+            top: 92,
+            message: '鏈幏鍙栧埌閰嶇疆淇℃伅锛�',
+            duration: 5
+          })
+          return
+        }
+
+        _config.open_edition = result.open_edition || ''
+        window.GLOB.CacheIndependent.set(_config.uuid, fromJS(_config).toJS())
+
+        config.components.unshift(_config)
+
+        this.setState({
+          visible: false
+        })
+        this.props.updateConfig(config)
+      })
+    })
+  }
+
+  render () {
+    const { config } = this.props
+    const { visible, dict } = this.state
+
+    return (
+      <div className="quote-wrap">
+        <Button icon="appstore" onClick={() => {this.setState({visible: true})}}>缁勪欢寮曠敤</Button>
+        <Modal
+          title="缁勪欢寮曠敤"
+          visible={visible}
+          width={500}
+          maskClosable={false}
+          okText={dict['model.submit']}
+          onOk={this.verifySubmit}
+          onCancel={() => { this.setState({ visible: false }) }}
+          destroyOnClose
+        >
+          <SettingForm
+            dict={dict}
+            config={config}
+            wrappedComponentRef={(inst) => this.verifyRef = inst}
+          />
+        </Modal>
+      </div>
+    )
+  }
+}
+
+export default Quotecomponent
\ No newline at end of file
diff --git a/src/pc/quotecomponent/index.scss b/src/pc/quotecomponent/index.scss
new file mode 100644
index 0000000..161ee6e
--- /dev/null
+++ b/src/pc/quotecomponent/index.scss
@@ -0,0 +1,6 @@
+.quote-wrap {
+  button {
+    border-color: #40a9ff;
+    color: #40a9ff;
+  }
+}
\ No newline at end of file
diff --git a/src/pc/quotecomponent/settingform/index.jsx b/src/pc/quotecomponent/settingform/index.jsx
new file mode 100644
index 0000000..afadc00
--- /dev/null
+++ b/src/pc/quotecomponent/settingform/index.jsx
@@ -0,0 +1,88 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Select } from 'antd'
+
+import './index.scss'
+
+class SettingForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object,      // 瀛楀吀椤�
+  }
+
+  state = {
+    appMenus: []
+  }
+
+  UNSAFE_componentWillMount () {
+    let appMenus = sessionStorage.getItem('appViewList')
+    if (appMenus) {
+      try {
+        appMenus = JSON.parse(appMenus)
+        appMenus = appMenus.filter(item => item.keys_type !== 'index')
+      } catch {
+        appMenus = []
+      }
+    } else {
+      appMenus = []
+    }
+
+    this.setState({appMenus})
+  }
+
+  handleConfirm = () => {
+    const { appMenus } = this.state
+    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
+    return new Promise((resolve, reject) => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          let item = appMenus.filter(_menu => _menu.keys_id === values.menu)[0]
+          resolve(item)
+        } else {
+          reject(err)
+        }
+      })
+    })
+  }
+
+  render() {
+    const { getFieldDecorator } = this.props.form
+    const { appMenus } = this.state
+
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout}>
+        <Row gutter={24}>
+          <Col span={20}>
+            <Form.Item label="鑿滃崟">
+              {getFieldDecorator('menu', {
+                initialValue: '',
+                rules: [{
+                  required: true,
+                  message: '璇烽�夋嫨鑿滃崟!'
+                }]
+              })(
+                <Select>
+                  {appMenus.map(option =>
+                    <Select.Option key={option.keys_id} value={option.keys_id}>{option.remark}</Select.Option>
+                  )}
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/pc/quotecomponent/settingform/index.scss b/src/pc/quotecomponent/settingform/index.scss
new file mode 100644
index 0000000..159130b
--- /dev/null
+++ b/src/pc/quotecomponent/settingform/index.scss
@@ -0,0 +1,11 @@
+.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/router/index.js b/src/router/index.js
index e3d881c..8f61de0 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -2,16 +2,18 @@
 import {HashRouter, Switch, Route, Redirect} from 'react-router-dom'
 import md5 from 'md5'
 import moment from 'moment'
-import options from '@/store/options.js'
+import { styles } from '@/store/options.js'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncLoadComponent from '@/utils/asyncLoadComponent'
 
 const Pay = asyncLoadComponent(() => import('@/views/pay'))
 const Sso = asyncLoadComponent(() => import('@/views/sso'))
 const Main = asyncLoadComponent(() => import('@/views/main'))
+const Design = asyncLoadComponent(() => import('@/views/design'))
 const Login = asyncLoadComponent(() => import('@/views/login'))
 const NotFound = asyncComponent(() => import('@/views/404'))
-const MobManage = asyncLoadComponent(() => import('@/views/mobmanage'))
+const AppManage = asyncLoadComponent(() => import('@/views/appmanage'))
+const PCDesign = asyncLoadComponent(() => import('@/views/pcdesign'))
 const MobDesign = asyncLoadComponent(() => import('@/views/mobdesign'))
 const MenuDesign = asyncLoadComponent(() => import('@/views/menudesign'))
 const BillPrint = asyncLoadComponent(() => import('@/views/billprint'))
@@ -23,8 +25,10 @@
   {path: '/print/:param', name: 'print', component: PrintT, auth: false},
   {path: '/ssologin/:param', name: 'ssologin', component: Sso, auth: false},
   {path: '/main', name: 'main', component: Main, auth: true},
-  {path: '/mobmanage', name: 'mobmanage', component: MobManage, auth: true},
-  {path: '/mobdesign/:appId/:appType/:appCode/:appName', name: 'mobdesign', component: MobDesign, auth: true},
+  {path: '/design', name: 'design', component: Design, auth: true},
+  {path: '/appmanage', name: 'appmanage', component: AppManage, auth: true},
+  {path: '/pcdesign/:param', name: 'pcdesign', component: PCDesign, auth: true},
+  {path: '/mobdesign/:param', name: 'mobdesign', component: MobDesign, auth: true},
   {path: '/menudesign/:param', name: 'menudesign', component: MenuDesign, auth: true},
   {path: '/billprint/:param', name: 'billprint', component: BillPrint, auth: true},
   {path: '/paramsmain/:param', name: 'pmain', component: Main, auth: true}
@@ -56,8 +60,11 @@
           if (_param.mainlogo) {
             window.GLOB.mainlogo = _param.mainlogo
           }
-          if (_param.mstyle && options.styles[_param.mstyle]) {
-            document.getElementById('root').className = options.styles[_param.mstyle]
+          if (_param.navBar) {
+            window.GLOB.navBar = _param.navBar
+          }
+          if (_param.mstyle && styles[_param.mstyle]) {
+            document.body.className = styles[_param.mstyle]
           }
           if (_param.MainMenu) {
             sessionStorage.setItem('MainMenu', _param.MainMenu)
diff --git a/src/setupProxy.js b/src/setupProxy.js
index 3f0dc0e..518e445 100644
--- a/src/setupProxy.js
+++ b/src/setupProxy.js
@@ -1,61 +1,52 @@
-const proxy = require('http-proxy-middleware')
-const host = 'http://bms-test.kresstools.cn'
-const service = 'oc/'
+// const proxy = require('http-proxy-middleware')
+// const host = 'http://qingqiumarket.cn'
+// const service = 'mkwms/'
 
-module.exports = function(app) {
-  app.use(proxy('/webapi', { 
-    target: `${host}/${service}webapi`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/webapi': '/'
-    }
-    // cookieDomainRewrite: "http://localhost:3000"
-  }))
+module.exports = function() {}
+// module.exports = function(app) {
+//   app.use(proxy('/webapi', { 
+//     target: `${host}/${service}webapi`,
+//     secure: false,
+//     changeOrigin: true,
+//     pathRewrite: {
+//     '^/webapi': '/'
+//     }
+//     // cookieDomainRewrite: "http://localhost:3000"
+//   }))
   
-  app.use(proxy('/zh-CN', { // 鐧诲綍鎺ュ彛
-    target: `${host}/${service}zh-CN`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/zh-CN': '/'
-    }
-  }))
+//   app.use(proxy('/zh-CN', { // 鐧诲綍鎺ュ彛
+//     target: `${host}/${service}zh-CN`,
+//     secure: false,
+//     changeOrigin: true,
+//     pathRewrite: {
+//     '^/zh-CN': '/'
+//     }
+//   }))
 
-  app.use(proxy('/Upload', {
-    target: `${host}/${service}zh-CN/Home/Upload`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/Upload': '/'
-    }
-  }))
+//   app.use(proxy('/Upload', {
+//     target: `${host}/${service}zh-CN/Home/Upload`,
+//     secure: false,
+//     changeOrigin: true,
+//     pathRewrite: {
+//     '^/Upload': '/'
+//     }
+//   }))
 
-  app.use(proxy('/wxpay', {
-    target: `${host}/${service}wxpay`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/wxpay': '/'
-    }
-  }))
+//   app.use(proxy('/wxpay', {
+//     target: `${host}/${service}wxpay`,
+//     secure: false,
+//     changeOrigin: true,
+//     pathRewrite: {
+//     '^/wxpay': '/'
+//     }
+//   }))
 
-  app.use(proxy('/Home', { 
-    target: `${host}/Home`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/Home': '/'
-    }
-    // cookieDomainRewrite: "http://localhost:3000"
-  }))
-
-  app.use(proxy('/trans', {
-    target: `${host}/${service}trans`,
-    secure: false,
-    changeOrigin: true,
-    pathRewrite: {
-    '^/trans': '/'
-    }
-  }))
-}
\ No newline at end of file
+//   app.use(proxy('/trans', {
+//     target: `${host}/${service}trans`,
+//     secure: false,
+//     changeOrigin: true,
+//     pathRewrite: {
+//     '^/trans': '/'
+//     }
+//   }))
+// }
diff --git a/src/store/action-type.js b/src/store/action-type.js
index 5668a97..a63129f 100644
--- a/src/store/action-type.js
+++ b/src/store/action-type.js
@@ -13,14 +13,8 @@
 // 淇敼绐楀彛鏍峰紡锛屽尯鍒唅frame涓庢甯搁〉闈�
 export const TOGGLE_ISIFRAME = 'TOGGLE_ISIFRAME'
 
-// 閫�鍑虹郴缁熸椂鍙傛暟閲嶇疆
-export const RESET_STATE = 'RESET_STATE'
-
 // 鍒锋柊tab椤甸潰
 export const REFRESH_TABVIEW = 'REFRESH_TABVIEW'
-
-// 閲嶇疆缂栬緫鐘舵��
-export const RESET_EDITSTATE = 'RESET_EDITSTATE'
 
 // 閲嶇疆缂栬緫绾у埆
 export const RESET_EDITLEVEL = 'RESET_EDITLEVEL'
@@ -33,9 +27,6 @@
 
 // 淇敼浼氬憳绛夌骇
 export const MODIFY_MEMBERLEVEL = 'MODIFY_MEMBERLEVEL'
-
-// 淇敼鑷畾涔夎彍鍗�
-export const MODIFY_CUSTOMMENU = 'MODIFY_CUSTOMMENU'
 
 // 閫�鍑�
 export const LOGOUT = 'LOGOUT'
\ No newline at end of file
diff --git a/src/store/action.js b/src/store/action.js
index 7b0bcf7..37d29d0 100644
--- a/src/store/action.js
+++ b/src/store/action.js
@@ -40,21 +40,6 @@
   }
 }
 
-// 閫�鍑虹郴缁熸椂鍙傛暟閲嶇疆
-export const resetState = () => {
-  return {
-    type: user.RESET_STATE
-  }
-}
-
-// 閲嶇疆缂栬緫鐘舵��
-export const resetEditState = (editState) => {
-  return {
-    type: user.RESET_EDITSTATE,
-    editState
-  }
-}
-
 // 閲嶇疆缂栬緫绾у埆
 export const resetEditLevel = (editLevel) => {
   return {
@@ -84,14 +69,6 @@
   return {
     type: user.MODIFY_MEMBERLEVEL,
     memberLevel: memberLevel
-  }
-}
-
-// 淇敼鑷畾涔夎彍鍗�
-export const modifyCustomMenu = (customMenu) => {
-  return {
-    type: user.MODIFY_CUSTOMMENU,
-    customMenu: customMenu
   }
 }
 
diff --git a/src/store/options.js b/src/store/options.js
index 347bcec..ec0cb09 100644
--- a/src/store/options.js
+++ b/src/store/options.js
@@ -1,37 +1,47 @@
-// 绯荤粺閰嶇疆
+/**
+ * @description 绯荤粺淇℃伅
+ * 1銆亂un ( Y2xv$mkdWQ= ) 銆� dandian ( U1$mkNP ) 銆� yewu ( bG9j$mkYWw= )
+ * 2銆亀indow.btoa('') 鍩熷悕 涓嶅甫 /
+ * 3銆乥aoshide old ( aHR0cDovL2Nsb3VkLnBv$mkc2l0ZWNncm91cC5jb206ODA4MA== )
+ * 4銆乥aoshide ( aHR0cHM6Ly9jbG91ZC5$mkwb3NpdGVjZ3JvdXAuY29t )
+ */
 export default {
-  sysType: 'bG9j$mkYWw=', // yun ( Y2xv$mkdWQ= ) 銆� dandian ( U1$mkNP ) 銆� yewu ( bG9j$mkYWw= )
+  sysType: 'bG9j$mkYWw=',
   caId: 'MjAyMDAxMTYxMjMzMzU1MDd$mkGQzkyMzI1Rjk4MDY0QUNGQjQ2Mg==',
   cakey: 'MjAyMDAxMTYxMjQwMDQ2NDM$mk2N0QzODE2MjExNUI0MTc4OTVDMQ==',
-  cdomain: 'aHR0cDovL2Nsb3VkLnBv$mkc2l0ZWNncm91cC5jb206ODA4MA==', // window.btoa('') 鍩熷悕 涓嶅甫 /; baoshide ( aHR0cDovL2Nsb3VkLnBv$mkc2l0ZWNncm91cC5jb206ODA4MA== )
-  styles: {
-    bg_black_style_blue: 'mk-blue-black',
-    bg_white_style_blue: 'mk-blue-white',
-    bg_black_style_red: 'mk-red-black',
-    bg_white_style_red: 'mk-red-white',
-    bg_black_style_orange_red: 'mk-orange-red-black',
-    bg_white_style_orange_red: 'mk-orange-red-white',
-    bg_black_style_orange: 'mk-orange-black',
-    bg_white_style_orange: 'mk-orange-white',
-    bg_black_style_orange_yellow: 'mk-orange-yellow-black',
-    bg_white_style_orange_yellow: 'mk-orange-yellow-white',
-    bg_black_style_yellow: 'mk-yellow-black',
-    bg_white_style_yellow: 'mk-yellow-white',
-    bg_black_style_yellow_green: 'mk-yellow-green-black',
-    bg_white_style_yellow_green: 'mk-yellow-green-white',
-    bg_black_style_green: 'mk-green-black',
-    bg_white_style_green: 'mk-green-white',
-    bg_black_style_cyan: 'mk-cyan-black',
-    bg_white_style_cyan: 'mk-cyan-white',
-    bg_black_style_blue_purple: 'mk-blue-purple-black',
-    bg_white_style_blue_purple: 'mk-blue-purple-white',
-    bg_black_style_purple: 'mk-purple-black',
-    bg_white_style_purple: 'mk-purple-white',
-    bg_black_style_magenta: 'mk-magenta-black',
-    bg_white_style_magenta: 'mk-magenta-white',
-    bg_black_style_grass_green: 'mk-grass-green-black',
-    bg_white_style_grass_green: 'mk-grass-green-white',
-    bg_black_style_deep_red: 'mk-deep-red-black',
-    bg_white_style_deep_red: 'mk-deep-red-white',
-  }
+  cdomain: 'aHR0cHM6Ly9jbG91ZC5$mkwb3NpdGVjZ3JvdXAuY29t'
+}
+
+/**
+ * @description 绯荤粺鏍峰紡搴�
+ */
+export const styles = {
+  bg_black_style_blue: 'mk-blue-black',
+  bg_white_style_blue: 'mk-blue-white',
+  bg_black_style_red: 'mk-red-black',
+  bg_white_style_red: 'mk-red-white',
+  bg_black_style_orange_red: 'mk-orange-red-black',
+  bg_white_style_orange_red: 'mk-orange-red-white',
+  bg_black_style_orange: 'mk-orange-black',
+  bg_white_style_orange: 'mk-orange-white',
+  bg_black_style_orange_yellow: 'mk-orange-yellow-black',
+  bg_white_style_orange_yellow: 'mk-orange-yellow-white',
+  bg_black_style_yellow: 'mk-yellow-black',
+  bg_white_style_yellow: 'mk-yellow-white',
+  bg_black_style_yellow_green: 'mk-yellow-green-black',
+  bg_white_style_yellow_green: 'mk-yellow-green-white',
+  bg_black_style_green: 'mk-green-black',
+  bg_white_style_green: 'mk-green-white',
+  bg_black_style_cyan: 'mk-cyan-black',
+  bg_white_style_cyan: 'mk-cyan-white',
+  bg_black_style_blue_purple: 'mk-blue-purple-black',
+  bg_white_style_blue_purple: 'mk-blue-purple-white',
+  bg_black_style_purple: 'mk-purple-black',
+  bg_white_style_purple: 'mk-purple-white',
+  bg_black_style_magenta: 'mk-magenta-black',
+  bg_white_style_magenta: 'mk-magenta-white',
+  bg_black_style_grass_green: 'mk-grass-green-black',
+  bg_white_style_grass_green: 'mk-grass-green-white',
+  bg_black_style_deep_red: 'mk-deep-red-black',
+  bg_white_style_deep_red: 'mk-deep-red-white',
 }
\ No newline at end of file
diff --git a/src/store/reducer.js b/src/store/reducer.js
index ba3511a..40d35ee 100644
--- a/src/store/reducer.js
+++ b/src/store/reducer.js
@@ -27,12 +27,10 @@
   tabviews: [],         // 瀵艰埅鏍�
   collapse: _collapse,  // 鏄惁鏀惰捣渚ц竟鏍忓鑸�
   isiframe: false,      // 鏄惁涓篿frame绐楀彛
-  editState: false,     // 鏄惁涓虹紪杈戠姸鎬侊紝鍊间负false銆乼rue
   editLevel: null,      // 缂栬緫鑿滃崟绾у埆锛屽�间负level1銆乴evel2銆乴evel3銆丠S
   permAction: {},       // 鐢ㄦ埛鎸夐挳鏉冮檺
   permMenus: [],        // 鐢ㄦ埛涓夌骇鑿滃崟鍒楄〃
   memberLevel: _level,  // 浼氬憳绛夌骇
-  customMenu: null      // 缂栬緫涓殑鑿滃崟锛堣嚜瀹氫箟椤甸潰锛�
 }
 
 // 鐢ㄦ埛娑堟伅
@@ -68,31 +66,10 @@
         ...state,
         isiframe: action.isiframe
       }
-    case Type.RESET_STATE:
-    // 閲嶇疆榛樿鍙傛暟锛堥��鍑烘椂锛�
-      return {
-        ...state,
-        ...{
-          mainMenu: null,
-          tabviews: [],
-          collapse: false,
-          isiframe: false
-        }
-      }
-    case Type.RESET_EDITSTATE:
-    // 閲嶇疆缂栬緫鐘舵��
-      document.body.className = ''
-      return {
-        ...state,
-        tabviews: [],
-        editState: action.editState,
-        collapse: false
-      }
     case Type.RESET_EDITLEVEL:
     // 閲嶇疆缂栬緫绾у埆
       return {
         ...state,
-        editState: true,
         editLevel: action.editLevel
       }
     case Type.INIT_ACTIONPERMISSION:
@@ -113,12 +90,6 @@
         ...state,
         memberLevel: action.memberLevel
       }
-    case Type.MODIFY_CUSTOMMENU:
-      // 淇敼鑷畾涔夎彍鍗曚俊鎭�
-      return {
-        ...state,
-        customMenu: action.customMenu
-      }
     case Type.LOGOUT:
       return {
         menuTree: null,
@@ -126,11 +97,9 @@
         tabviews: [],
         collapse: localStorage.getItem('collapse') === 'true',
         isiframe: false,
-        editState: false,
         editLevel: null,
         permAction: {},
         permMenus: [],
-        customMenu: null
       }
     default:
       return state
diff --git a/src/tabviews/calendar/index.jsx b/src/tabviews/calendar/index.jsx
index 550326c..6141c25 100644
--- a/src/tabviews/calendar/index.jsx
+++ b/src/tabviews/calendar/index.jsx
@@ -124,14 +124,8 @@
       let roleId = sessionStorage.getItem('role_id') || ''
       config.search = config.search.map(item => {
         item.oriInitval = item.initval
-        if (['text', 'select', 'link'].includes(item.type) && param) {
-          if (param.searchkey === item.field) {
-            item.initval = param.searchval
-          } else if (param.BID && item.field.toLowerCase() === 'bid') {
-            item.initval = param.BID
-          } else if (param.data && param.data[item.field]) {
-            item.initval = param.data[item.field]
-          }
+        if (['text', 'select', 'link'].includes(item.type) && param && param.$searchkey === item.field) {
+          item.initval = param.$searchval
         }
 
         if (item.required === 'true' && !item.initval) {
@@ -175,12 +169,21 @@
           config.setting.dataresource = config.setting.dataresource.replace(/@\$|\$@/ig, '')
           _customScript = _customScript.replace(/@\$|\$@/ig, '')
         }
+        if (config.urlFields) {
+          let _param = param || {}
+          config.urlFields.forEach(field => {
+            let reg = new RegExp('@' + field + '@', 'ig')
+            let val = `'${_param[field] || ''}'`
+            config.setting.dataresource = config.setting.dataresource.replace(reg, val)
+            _customScript = _customScript.replace(reg, val)
+          })
+        }
 
         config.setting.customScript = _customScript
       }
 
       this.setState({
-        BID: param && param.BID ? param.BID : '',
+        BID: param && param.$BID ? param.$BID : '',
         loadingview: false,
         config: config,
         userConfig: userConfig,
@@ -328,6 +331,7 @@
     let regoptions = []
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -343,6 +347,9 @@
         }
       })
       regoptions.push({
+        reg: new RegExp('@login_city@', 'ig'),
+        value: city
+      }, {
         reg: new RegExp('@userName@', 'ig'),
         value: userName
       }, {
@@ -380,8 +387,8 @@
         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)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+      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}
       `
 
@@ -464,18 +471,6 @@
   UNSAFE_componentWillMount () {
     // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
     this.loadconfig()
-  }
-
-  UNSAFE_componentWillReceiveProps(nextProps) {
-    if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) {
-      let search = this.state.search.map(item => {
-        if (item.type === 'text' && item.key === nextProps.param.searchkey) {
-          item.value = nextProps.param.searchval
-        }
-        return item
-      })
-      this.refreshbysearch(search)
-    }
   }
 
   shouldComponentUpdate (nextProps, nextState) {
diff --git a/src/tabviews/commontable/index.jsx b/src/tabviews/commontable/index.jsx
index 73a85b8..ce0938e 100644
--- a/src/tabviews/commontable/index.jsx
+++ b/src/tabviews/commontable/index.jsx
@@ -179,14 +179,8 @@
       // 瀛楁鏉冮檺榛戝悕鍗�
       config.search = config.search.map(item => {
         item.oriInitval = item.initval
-        if (['text', 'select', 'link'].includes(item.type) && param) {
-          if (param.searchkey === item.field) {
-            item.initval = param.searchval
-          } else if (param.BID && item.field.toLowerCase() === 'bid') {
-            item.initval = param.BID
-          } else if (param.data && param.data[item.field]) {
-            item.initval = param.data[item.field]
-          }
+        if (['text', 'select', 'link'].includes(item.type) && param && param.$searchkey === item.field) {
+          item.initval = param.$searchval
         }
 
         if (!item.blacklist || item.blacklist.length === 0) return item
@@ -213,6 +207,7 @@
       config.setting.laypage = config.setting.laypage !== 'false'     // 鏄惁鍒嗛〉锛岃浆涓篵oolean 缁熶竴鏍煎紡
       config.setting.execute = config.setting.default !== 'false'     // 榛樿sql鏄惁鎵ц锛岃浆涓篵oolean 缁熶竴鏍煎紡
       config.setting.customScript = ''                                // 鑷畾涔夎剼鏈�
+      config.setting.dataresource = config.setting.dataresource || ''
 
       if (config.setting.interType === 'system' || (config.setting.interType === 'custom' && config.setting.requestMode === 'system')) {
         if (config.setting.scripts && config.setting.scripts.length > 0) {
@@ -228,8 +223,6 @@
   
         if (!config.setting.execute) { // 榛樿sql 涓嶆墽琛屾椂 缃┖
           config.setting.dataresource = ''
-        } else {
-          config.setting.dataresource = config.setting.dataresource || ''
         }
         if (/\s/.test(config.setting.dataresource)) {
           config.setting.dataresource = '(' + config.setting.dataresource + ') tb'
@@ -243,6 +236,15 @@
         } else {
           config.setting.dataresource = config.setting.dataresource.replace(/@\$|\$@/ig, '')
           config.setting.customScript = config.setting.customScript.replace(/@\$|\$@/ig, '')
+        }
+        if (config.urlFields) {
+          let _param = param || {}
+          config.urlFields.forEach(field => {
+            let reg = new RegExp('@' + field + '@', 'ig')
+            let val = `'${_param[field] || ''}'`
+            config.setting.dataresource = config.setting.dataresource.replace(reg, val)
+            config.setting.customScript = config.setting.customScript.replace(reg, val)
+          })
         }
       }
 
@@ -288,6 +290,13 @@
         if (col.field) {
           _arrField.push(col.field)
 
+          if (col.linkmenu && col.linkmenu.length > 0) {
+            let menu_id = col.linkmenu.slice(-1)[0]
+            col.linkThdMenu = permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+          } else {
+            col.linkThdMenu = ''
+          }
+
           col.nameField && _arrField.push(col.nameField) // 閾炬帴鍚嶅瓧娈�
           if (col.Hide !== 'true' && col.type === 'number' && col.sum === 'true') {
             statFields.push(col)
@@ -305,13 +314,6 @@
       // 鐢熸垚鏄剧ず鍒楋紝澶勭悊鍚堝苟鍒椾腑鐨勫瓧娈�
       config.columns.forEach((col, index) => {
         if (_hideCol.includes(col.uuid)) return
-        
-        if (col.linkmenu && col.linkmenu.length > 0) {
-          let menu_id = col.linkmenu.slice(-1)[0]
-          col.linkThdMenu = permMenus.filter(m => m.MenuID === menu_id)[0] || ''
-        } else {
-          col.linkThdMenu = ''
-        }
 
         col.sort = index
 
@@ -363,7 +365,7 @@
         actions: _actions,
         columns: _columns,
         arr_field: _arrField.join(','),
-        BID: param && param.BID ? param.BID : '',
+        BID: param && param.$BID ? param.$BID : '',
         search: Utils.initMainSearch(config.search), // 鎼滅储鏉′欢鍒濆鍖栵紙鍚湁鏃堕棿鏍煎紡锛岄渶瑕佽浆鍖栵級
         hasReqFields
       }, () => {
@@ -489,7 +491,7 @@
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
-        if (res.mk_ex_invoke === 'false') {
+        if (res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) {
           this.loadmaindata()
         } else {
           this.customOuterRequest(res)
@@ -537,7 +539,7 @@
       param[key] = result[key]
     })
 
-    Api.directRequest(url, setting.method, param).then(res => {
+    Api.directRequest(url, setting.method, param, setting.cross).then(res => {
       if (typeof(res) !== 'object' || Array.isArray(res)) {
         let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
 
@@ -715,8 +717,9 @@
     let result = await Api.genericInterface(param)
     if (result.status) {
       let data = fromJS(this.state.data).toJS()
+      let selectedData = fromJS(this.state.selectedData).toJS()
       if (result.data && result.data[0]) {
-        let _data = result.data[0]
+        let _data = result.data[0] || {}
 
         if (absFields.length) {
           absFields.forEach(field => {
@@ -726,18 +729,23 @@
             _data[field] = Math.abs(_data[field])
           })
         }
+        _data.$$uuid = _data[setting.primaryKey] || ''
+        _data.$$BID = BID || ''
 
         try {
           data = data.map(item => {
-            if (item[setting.primaryKey] === _data[setting.primaryKey]) {
+            if (item.$$uuid === _data.$$uuid) {
               _data.key = item.key
-              _data.$$uuid = _data[setting.primaryKey] || ''
-              _data.$$BID = BID || ''
-
               return _data
             } else {
               return item
             }
+          })
+          selectedData = selectedData.map(item => {
+            if (_data.$$uuid === item.$$uuid) {
+              return _data
+            }
+            return item
           })
         } catch {
           console.warn('鏁版嵁鏌ヨ閿欒')
@@ -745,7 +753,8 @@
       }
 
       this.setState({
-        data: data,
+        data,
+        selectedData,
         loading: false
       })
     } else {
@@ -994,19 +1003,11 @@
   }
 
   UNSAFE_componentWillReceiveProps(nextProps) {
-     if (!is(fromJS(this.props.tabviews), fromJS(nextProps.tabviews))) {
+    if (!is(fromJS(this.props.tabviews), fromJS(nextProps.tabviews))) {
       let selectTab = nextProps.tabviews.filter(tab => tab.selected)[0]
       if (selectTab && selectTab.MenuID === this.props.MenuID) {
         this.setShortcut()
       }
-    } else if (nextProps.param && !is(fromJS(this.props.param), fromJS(nextProps.param))) {
-      let search = this.state.search.map(item => {
-        if (item.type === 'text' && item.key === nextProps.param.searchkey) {
-          item.value = nextProps.param.searchval
-        }
-        return item
-      })
-      this.refreshbysearch(search)
     }
   }
 
diff --git a/src/tabviews/commontable/index.scss b/src/tabviews/commontable/index.scss
index 167d520..086a875 100644
--- a/src/tabviews/commontable/index.scss
+++ b/src/tabviews/commontable/index.scss
@@ -13,7 +13,7 @@
   .commontable-main-action {
     min-height: 25px;
     .button-list {
-      padding-right: 110px;
+      padding-right: 60px;
     }
   }
   .ant-modal-mask {
diff --git a/src/tabviews/custom/components/card/cardItem/index.jsx b/src/tabviews/custom/components/card/cardItem/index.jsx
index 10aa604..044bc4e 100644
--- a/src/tabviews/custom/components/card/cardItem/index.jsx
+++ b/src/tabviews/custom/components/card/cardItem/index.jsx
@@ -3,9 +3,6 @@
 import { is, fromJS } from 'immutable'
 
 import asyncComponent from '@/utils/asyncComponent'
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
-
 import './index.scss'
 
 const CardCellComponent = asyncComponent(() => import('../cardcellList'))
@@ -18,7 +15,6 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
   }
 
diff --git a/src/tabviews/custom/components/card/cardcellList/index.jsx b/src/tabviews/custom/components/card/cardcellList/index.jsx
index 961b02b..525c471 100644
--- a/src/tabviews/custom/components/card/cardcellList/index.jsx
+++ b/src/tabviews/custom/components/card/cardcellList/index.jsx
@@ -4,8 +4,6 @@
 import { Icon, Col, Tooltip, notification } from 'antd'
 import moment from 'moment'
 
-import zhCN from '@/locales/zh-CN/model.js'
-import enUS from '@/locales/en-US/model.js'
 import asyncComponent from './asyncButtonComponent'
 import asyncElementComponent from '@/utils/asyncComponent'
 
@@ -34,7 +32,6 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,          // 缂栬緫涓厓绱�
     elements: null,      // 鎸夐挳缁�
   }
@@ -278,7 +275,13 @@
       let _style = card.style ? {...card.style} : {}
 
       if (card.datatype === 'static') {
-        val = card.value
+        val = card.value || ''
+        if (/@username@|@fullName@|@login_city@/ig.test(val)) {
+          let userName = sessionStorage.getItem('User_Name') || ''
+          let fullName = sessionStorage.getItem('Full_Name') || ''
+          let city = sessionStorage.getItem('city') || ''
+          val = val.replace(/@username@/ig, userName).replace(/@fullName@/ig, fullName).replace(/@login_city@/ig, city)
+        }
       } else if (data.hasOwnProperty(card.field)) {
         val = data[card.field]
       }
@@ -304,7 +307,7 @@
       return (
         <Col key={card.uuid} span={card.width}>
           <div style={_style} onClick={(e) => {this.openNewView(e, card)}}>
-            <div className={'ant-mk-text line' + card.height} style={{height: card.innerHeight || 21}}>{val}</div>
+            <div className={'ant-mk-text line' + card.height} style={{height: card.innerHeight || 'auto'}}>{val}</div>
           </div>
         </Col>
       )
@@ -356,7 +359,7 @@
       return (
         <Col key={card.uuid} span={card.width}>
           <div style={_style}>
-            <div className={'ant-mk-text line' + card.height} style={{height: card.innerHeight || 21}}>{val}</div>
+            <div className={'ant-mk-text line' + card.height} style={{height: card.innerHeight || 'auto'}}>{val}</div>
           </div>
         </Col>
       )
diff --git a/src/tabviews/custom/components/card/data-card/index.jsx b/src/tabviews/custom/components/card/data-card/index.jsx
index 30ba645..59935cd 100644
--- a/src/tabviews/custom/components/card/data-card/index.jsx
+++ b/src/tabviews/custom/components/card/data-card/index.jsx
@@ -1,6 +1,7 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
 import { Spin, Empty, notification, Col, Pagination } from 'antd'
 
 import Api from '@/api'
@@ -10,6 +11,7 @@
 import nextImg from '@/assets/img/next.png'
 import MKEmitter from '@/utils/events.js'
 import asyncComponent from '@/utils/asyncComponent'
+import { modifyTabview } from '@/store/action'
 import './index.scss'
 
 const CardItem = asyncComponent(() => import('../cardItem'))
@@ -394,8 +396,10 @@
   changeCard = (index, item) => {
     const { config, selectKeys, selectedData, activeKey } = this.state
 
-    if (!config.wrap.cardType) return
+    this.openView(item)
 
+    if (!config.wrap.cardType) return
+    
     let _selectKeys = []
     let _selectedData = []
     let _activeKey = ''
@@ -430,6 +434,83 @@
     })
 
     MKEmitter.emit('resetSelectLine', config.uuid, (_item ? _item.$$uuid : ''), _item)
+  }
+
+  openView = (item) => {
+    const { card } = this.state
+
+    if (card.setting.click === 'menu') {
+      let menu = null
+
+      if (card.setting.menu && card.setting.menu.length > 0) {
+        let menu_id = card.setting.menu.slice(-1)[0]
+        menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+      }
+
+      if (!menu) {
+        notification.warning({
+          top: 92,
+          message: '鑿滃崟宸插垹闄ゆ垨娌℃湁璁块棶鏉冮檺锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let newtab = {
+        ...menu,
+        selected: true,
+        param: {}
+      }
+
+      if (card.setting.joint === 'true') {
+        newtab.param.$BID = item.$$uuid
+      }
+
+      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+        this.props.modifyTabview([newtab])
+      } else {
+        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)
+        })
+      }
+    } else if (card.setting.click === 'link') {
+      let src = card.setting.linkurl
+
+      if (src.indexOf('paramsmain/') > -1) {
+        try {
+          let _url = src.split('paramsmain/')[0] + 'paramsmain/'
+          let _param = JSON.parse(window.decodeURIComponent(window.atob(src.split('paramsmain/')[1])))
+
+          _param.UserID = sessionStorage.getItem('UserID')
+          _param.LoginUID = sessionStorage.getItem('LoginUID')
+          _param.User_Name = sessionStorage.getItem('User_Name')
+          _param.param = { BID: item.$$uuid }
+          src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
+        } catch {
+          console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
+        }
+      } else if (card.setting.joint === 'true') {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        src = src + `${con}id=${item.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
+      }
+
+      window.open(src)
+    }
   }
 
   render() {
@@ -475,7 +556,7 @@
           {switchable ? <div className={'prev-page ' + (pageIndex === 1 ? 'disabled' : '')} onClick={this.prevPage}><div><div><img src={preImg} alt=""/></div></div></div> : null}
           {data && data.length > 0 ? <div className="card-row-list">
             {data.map((item, index) => (
-              <Col className={activeKey === index ? 'active' : (selectKeys.indexOf(index) > -1 ? 'selected' : '')} key={index} span={card.setting.width} offset={!index ? offset : 0} onClick={() => {this.changeCard(index, item)}}>
+              <Col className={(activeKey === index ? 'active' : (selectKeys.indexOf(index) > -1 ? 'selected' : '')) + (card.setting.click ? ' pointer' : '')} key={index} span={card.setting.width} offset={!index ? offset : 0} onClick={() => {this.changeCard(index, item)}}>
                 <CardItem card={card} cards={config} data={item}/>
               </Col>
             ))}
@@ -489,4 +570,17 @@
   }
 }
 
-export default DataCard
\ No newline at end of file
+const mapStateToProps = (state) => {
+  return {
+    permMenus: state.permMenus,
+    tabviews: state.tabviews,
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(DataCard)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/data-card/index.scss b/src/tabviews/custom/components/card/data-card/index.scss
index c9e972b..49e69ef 100644
--- a/src/tabviews/custom/components/card/data-card/index.scss
+++ b/src/tabviews/custom/components/card/data-card/index.scss
@@ -74,6 +74,9 @@
       background-color: #ffffff;
       transition: all 0.3s;
     }
+    >.pointer {
+      cursor: pointer;
+    }
     >.active >.card-item-box {
       border-color: #1890ff!important;
       box-shadow: 0 0 4px #1890ff;
diff --git a/src/tabviews/custom/components/card/prop-card/index.jsx b/src/tabviews/custom/components/card/prop-card/index.jsx
index f621976..0c85678 100644
--- a/src/tabviews/custom/components/card/prop-card/index.jsx
+++ b/src/tabviews/custom/components/card/prop-card/index.jsx
@@ -1,18 +1,22 @@
 import React, {Component} from 'react'
 import PropTypes from 'prop-types'
 import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
 import { Spin, notification, Col } from 'antd'
+import moment from 'moment'
 
-import asyncComponent from '@/utils/asyncComponent'
 import Api from '@/api'
+import Utils from '@/utils/utils.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
+import asyncComponent from '@/utils/asyncComponent'
 import MKEmitter from '@/utils/events.js'
+import { modifyTabview } from '@/store/action'
 import './index.scss'
 
 const CardItem = asyncComponent(() => import('../cardItem'))
 const NormalHeader = asyncComponent(() => import('@/tabviews/custom/components/share/normalheader'))
 
-class DataCard extends Component {
+class PropCard extends Component {
   static propTpyes = {
     BID: PropTypes.any,              // 鐖剁骇Id
     data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
@@ -27,7 +31,8 @@
     loading: false,            // 鏁版嵁鍔犺浇鐘舵��
     activeKey: '',             // 閫変腑鏁版嵁
     sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
-    data: {}                   // 鏁版嵁
+    data: {},                  // 鏁版嵁
+    timer: null                // 瀹氭椂鍣ㄦ椂闂撮棿闅�
   }
 
   UNSAFE_componentWillMount () {
@@ -109,6 +114,7 @@
     MKEmitter.addListener('reloadData', this.reloadData)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+    this.handleTimer()
   }
 
   shouldComponentUpdate (nextProps, nextState) {
@@ -116,6 +122,7 @@
   }
 
   componentWillUnmount () {
+    clearTimeout(this.timer)
     this.setState = () => {
       return
     }
@@ -151,6 +158,65 @@
         })
       }
     }
+  }
+
+  handleTimer = () => {
+    const { config } = this.state
+
+    if (!config.timer) return
+
+    const _change = {
+      '15s': 15000,
+      '30s': 30000,
+      '1min': 60000,
+      '5min': 300000,
+      '10min': 600000,
+      '15min': 900000,
+      '30min': 1800000,
+      '1hour': 3600000
+    }
+
+    let timer = _change[config.timer]
+
+    if (!timer) return
+
+    let _param = {
+      func: 's_get_timers_role',
+      LText: `select '${window.GLOB.appkey || ''}','${config.uuid}'`,
+      timer_type: config.timer,
+      component_id: config.uuid
+    }
+    
+    _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 鏃堕棿鎴�
+    _param.LText = Utils.formatOptions(_param.LText)                   // 鍏抽敭瀛楃鏇挎崲锛宐ase64鍔犲瘑
+    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5瀵嗛挜
+
+    Api.getSystemConfig(_param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      } else if (result.run_type) {
+        this.setState({timer})
+        this.timer = setTimeout(() => {
+          this.timerTask()
+        }, timer)
+      }
+    })
+  }
+
+  timerTask = () => {
+    const { timer } = this.state
+    if (!timer) return
+    
+    this.loadData(true)
+
+    this.timer = setTimeout(() => {
+      this.timerTask()
+    }, timer)
   }
 
   /**
@@ -197,7 +263,7 @@
     this.loadData()
   }
 
-  async loadData () {
+  async loadData (hastimer) {
     const { mainSearch, menuType } = this.props
     const { config, arr_field, BID } = this.state
 
@@ -225,9 +291,11 @@
       })
     }
 
-    this.setState({
-      loading: true
-    })
+    if (!hastimer) {
+      this.setState({
+        loading: true
+      })
+    }
 
     let _orderBy = config.setting.order || ''
     let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, 1, 1, BID, menuType)
@@ -244,7 +312,8 @@
       })
     } else {
       this.setState({
-        loading: false
+        loading: false,
+        timer: null
       })
       notification.error({
         top: 92,
@@ -257,6 +326,8 @@
   changeCard = (index, item) => {
     const { config, data, activeKey } = this.state
 
+    this.openView(item)
+
     if (!config.wrap.cardType || activeKey === index) return
 
     this.setState({
@@ -265,6 +336,82 @@
 
     MKEmitter.emit('resetSelectLine', config.uuid, (item.setting.primaryId || ''), data)
   }
+
+  openView = (item) => {
+    if (item.setting.click === 'menu') {
+      let menu = null
+
+      if (item.setting.menu && item.setting.menu.length > 0) {
+        let menu_id = item.setting.menu.slice(-1)[0]
+        menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+      }
+
+      if (!menu) {
+        notification.warning({
+          top: 92,
+          message: '鑿滃崟宸插垹闄ゆ垨娌℃湁璁块棶鏉冮檺锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let newtab = {
+        ...menu,
+        selected: true,
+        param: {}
+      }
+
+      if (item.setting.joint === 'true') {
+        newtab.param.$BID = item.setting.primaryId || ''
+      }
+
+      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+        this.props.modifyTabview([newtab])
+      } else {
+        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)
+        })
+      }
+    } else if (item.setting.click === 'link') {
+      let src = item.setting.linkurl
+
+      if (src.indexOf('paramsmain/') > -1) {
+        try {
+          let _url = src.split('paramsmain/')[0] + 'paramsmain/'
+          let _param = JSON.parse(window.decodeURIComponent(window.atob(src.split('paramsmain/')[1])))
+
+          _param.UserID = sessionStorage.getItem('UserID')
+          _param.LoginUID = sessionStorage.getItem('LoginUID')
+          _param.User_Name = sessionStorage.getItem('User_Name')
+          _param.param = { BID: item.setting.primaryId }
+          src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
+        } catch {
+          console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
+        }
+      } else if (item.setting.joint === 'true') {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        src = src + `${con}id=${item.setting.primaryId}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
+      }
+
+      window.open(src)
+    }
+  }
+
 
   render() {
     const { config, loading, data, activeKey } = this.state
@@ -280,7 +427,7 @@
         <NormalHeader config={config}/>
         <div className={`card-row-list ${config.wrap.cardType || ''} ${config.wrap.scale || ''}`}>
           {config.subcards.map((item, index) => (
-            <Col className={activeKey === index ? 'active' : ''} key={index} span={item.setting.width || 6} offset={item.offset || 0} onClick={() => {this.changeCard(index, item)}}>
+            <Col className={(activeKey === index ? 'active' : '') + (item.setting.click ? ' pointer' : '')} key={index} span={item.setting.width || 6} offset={item.offset || 0} onClick={() => {this.changeCard(index, item)}}>
               <CardItem card={item} cards={config} data={data}/>
             </Col>
           ))}
@@ -290,4 +437,17 @@
   }
 }
 
-export default DataCard
\ No newline at end of file
+const mapStateToProps = (state) => {
+  return {
+    permMenus: state.permMenus,
+    tabviews: state.tabviews,
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(PropCard)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/card/prop-card/index.scss b/src/tabviews/custom/components/card/prop-card/index.scss
index 7ec304d..12c5e88 100644
--- a/src/tabviews/custom/components/card/prop-card/index.scss
+++ b/src/tabviews/custom/components/card/prop-card/index.scss
@@ -17,6 +17,9 @@
       background-color: #ffffff;
       transition: all 0.3s;
     }
+    >.pointer {
+      cursor: pointer;
+    }
     >.active >.card-item-box {
       border-color: #1890ff!important;
       box-shadow: 0 0 4px #1890ff;
diff --git a/src/tabviews/custom/components/carousel/cardItem/index.jsx b/src/tabviews/custom/components/carousel/cardItem/index.jsx
new file mode 100644
index 0000000..709043f
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/cardItem/index.jsx
@@ -0,0 +1,53 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+
+import asyncComponent from '@/utils/asyncComponent'
+
+import './index.scss'
+
+const CardCellComponent = asyncComponent(() => import('@/tabviews/custom/components/card/cardcellList'))
+
+class CardBoxComponent extends Component {
+  static propTpyes = {
+    cards: PropTypes.object,    // 鍗$墖琛岄厤缃俊鎭�
+    card: PropTypes.object,     // 鍗$墖閰嶇疆淇℃伅
+    data: PropTypes.object,
+  }
+
+  state = {
+    card: null,            // 鍗$墖淇℃伅锛屽寘鎷鍙嶉潰
+  }
+
+  /**
+   * @description 鎼滅储鏉′欢鍒濆鍖�
+   */
+  UNSAFE_componentWillMount () {
+
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState)) || !is(fromJS(this.props), fromJS(nextProps))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  render() {
+    const { card, data, cards } = this.props
+
+    return (
+      <div className="card-item-box" style={card.style}>
+        <CardCellComponent data={data} cards={cards} cardCell={card} elements={card.elements}/>
+      </div>
+    )
+  }
+}
+
+export default CardBoxComponent
\ No newline at end of file
diff --git a/src/tabviews/custom/components/carousel/cardItem/index.scss b/src/tabviews/custom/components/carousel/cardItem/index.scss
new file mode 100644
index 0000000..605a96b
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/cardItem/index.scss
@@ -0,0 +1,58 @@
+.card-item-box {
+  position: relative;
+  overflow: hidden;
+
+  .back-side {
+    position: absolute;
+    top: 0px;
+    left: 0px;
+    right: 0px;
+    bottom: 0px;
+    background-color: #ffffff;
+    transition: all 0.3s;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+  .back-side.up {
+    transform: translate(0, 100%);
+  }
+  .back-side.down {
+    transform: translate(0, -100%);
+  }
+  .back-side.left {
+    transform: translate(100%, 0);
+  }
+  .back-side.right {
+    transform: translate(-100%, 0);
+  }
+  .back-side.opacity {
+    opacity: 0;
+  }
+  .back-side.rotateX {
+    transform: rotateX(90deg);
+  }
+  .back-side.rotateY {
+    transform: rotateY(90deg);
+  }
+  .back-side.scale {
+    transform: scale(0, 0);
+  }
+}
+.card-item-box:hover {
+  .back-side.up, .back-side.down, .back-side.left, .back-side.right {
+    transform: translate(0, 0);
+  }
+  .back-side.opacity {
+    opacity: 1;
+  }
+  .back-side.rotateX {
+    transform: rotateX(0deg);
+  }
+  .back-side.rotateY {
+    transform: rotateY(0deg);
+  }
+  .back-side.scale {
+    transform: scale(1, 1);
+  }
+}
\ No newline at end of file
diff --git a/src/tabviews/custom/components/carousel/data-card/index.jsx b/src/tabviews/custom/components/carousel/data-card/index.jsx
new file mode 100644
index 0000000..1e6e1aa
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/data-card/index.jsx
@@ -0,0 +1,357 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
+import { Spin, Empty, notification, Carousel } from 'antd'
+
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import MKEmitter from '@/utils/events.js'
+import asyncComponent from '@/utils/asyncComponent'
+import { modifyTabview } from '@/store/action'
+import './index.scss'
+
+const CardItem = asyncComponent(() => import('../cardItem'))
+
+class DataCard extends Component {
+  static propTpyes = {
+    BID: PropTypes.any,              // 鐖剁骇Id
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+    menuType: PropTypes.any,         // 鑿滃崟绫诲瀷
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    card: null,                // 鍗$墖璁剧疆
+    data: null,                // 鏁版嵁
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, initdata, BID } = this.props
+    let _config = fromJS(this.props.config).toJS()
+    let _card = _config.subcards[0]
+    let _cols = new Map()
+
+    let _data = null
+    let _sync = _config.setting.sync === 'true'
+
+    if (_config.setting.sync === 'true' && data) {
+      _data = data[_config.dataName] || []
+      _sync = false
+    } else if (_config.setting.sync === 'true' && initdata) {
+      _data = initdata || []
+      _sync = false
+    }
+
+    if (_data) {
+      _data = _data.map((item, index) => {
+        item.key = index
+        item.$$uuid = item[_config.setting.primaryKey] || ''
+        item.$$BID = BID || ''
+        return item
+      })
+    }
+
+    _config.columns.forEach(item => {
+      _cols.set(item.field, item)
+    })
+
+    _card.style.height = _config.style.height
+
+    if (_card.setting.click) {
+      _card.style.cursor = 'pointer'
+    }
+
+    _card.elements = _card.elements.map(item => {
+      if (item.field && _cols.has(item.field)) {
+        item.col = _cols.get(item.field)
+      }
+      return item
+    })
+
+    _config.wrap.speed = (_config.wrap.speed || 3) * 1000
+
+    this.setState({
+      sync: _sync,
+      data: _data,
+      BID: BID || '',
+      config: _config,
+      card: _card,
+      arr_field: _config.columns.map(col => col.field).join(','),
+    }, () => {
+      if (_config.setting.sync !== 'true' && _config.setting.onload === 'true') {
+        this.loadData()
+      }
+    })
+  }
+
+  componentDidMount () {
+    MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.addListener('getexceloutparam', this.getexceloutparam)
+    MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config, BID } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = []
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName] || []
+      }
+
+      _data = _data.map((item, index) => {
+        item.key = index
+        item.$$uuid = item[config.setting.primaryKey] || ''
+        item.$$BID = BID || ''
+        return item
+      })
+
+      this.setState({sync: false, data: _data})
+    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      if (config.setting.syncRefresh === 'true') {
+        this.setState({}, () => {
+          this.loadData()
+        })
+      }
+    }
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('getexceloutparam', this.getexceloutparam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn) => {
+    const { config, BID } = this.state
+
+    if (config.uuid !== menuId) return
+
+    this.loadData(btn)                                                         // 鏁版嵁鍒锋柊
+
+    if (btn.syncComponentId && btn.syncComponentId !== config.uuid && btn.syncComponentId !== config.setting.supModule) {
+      MKEmitter.emit('reloadData', btn.syncComponentId)                        // 鍚岀骇鏍囩鍒锋柊
+    }
+
+    if (position === 'mainline' && config.setting.supModule) {                 // 涓昏〃琛屽埛鏂�
+      MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty'))
+    } else if (position === 'popclose') {                                      // 鏍囩鍏抽棴鍒锋柊
+      config.setting.supModule && MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty'))
+      btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
+    }
+  }
+
+  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 }, () => {
+        this.loadData()
+      })
+    }
+  }
+
+  /**
+   * @description 瀵煎嚭Excel鏃讹紝鑾峰彇椤甸潰鎼滅储鎺掑簭绛夊弬鏁�
+   */
+  getexceloutparam = (menuId, btnId) => {
+    const { mainSearch } = this.props
+    const { arr_field, config } = this.state
+
+    if (config.uuid !== menuId) return
+
+    let searches = mainSearch ? fromJS(mainSearch).toJS() : []
+
+    MKEmitter.emit('execExcelout', config.uuid, btnId, {
+      arr_field: arr_field,
+      orderBy: config.setting.order || '',
+      search: searches,
+      menuName: config.name
+    })
+  }
+
+  async loadData () {
+    const { mainSearch, menuType } = this.props
+    const { config, arr_field, BID } = this.state
+
+    if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: [],
+        loading: false
+      })
+      return
+    }
+
+    let searches = mainSearch ? fromJS(mainSearch).toJS() : []
+
+    this.setState({
+      loading: true
+    })
+
+    let _orderBy = config.setting.order || ''
+    let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID, menuType)
+
+    let result = await Api.genericInterface(param)
+    if (result.status) {
+      this.setState({
+        data: result.data.map((item, index) => {
+          item.key = index
+          item.$$uuid = item[config.setting.primaryKey] || ''
+          item.$$BID = BID || ''
+          return item
+        }),
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  openView = (item) => {
+    const { card } = this.state
+
+    if (card.setting.click === 'menu') {
+      let menu = null
+
+      if (card.setting.menu && card.setting.menu.length > 0) {
+        let menu_id = card.setting.menu.slice(-1)[0]
+        menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+      }
+
+      if (!menu) {
+        notification.warning({
+          top: 92,
+          message: '鑿滃崟宸插垹闄ゆ垨娌℃湁璁块棶鏉冮檺锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let newtab = {
+        ...menu,
+        selected: true,
+        param: {}
+      }
+
+      if (card.setting.joint === 'true') {
+        newtab.param.$BID = item.$$uuid
+      }
+
+      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+        this.props.modifyTabview([newtab])
+      } else {
+        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)
+        })
+      }
+    } else if (card.setting.click === 'link') {
+      let src = card.setting.linkurl
+
+      if (src.indexOf('paramsmain/') > -1) {
+        try {
+          let _url = src.split('paramsmain/')[0] + 'paramsmain/'
+          let _param = JSON.parse(window.decodeURIComponent(window.atob(src.split('paramsmain/')[1])))
+
+          _param.UserID = sessionStorage.getItem('UserID')
+          _param.LoginUID = sessionStorage.getItem('LoginUID')
+          _param.User_Name = sessionStorage.getItem('User_Name')
+          _param.param = { BID: item.$$uuid }
+          src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
+        } catch {
+          console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
+        }
+      } else if (card.setting.joint === 'true') {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        src = src + `${con}id=${item.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
+      }
+
+      window.open(src)
+    }
+  }
+
+  render() {
+    const { config, loading, data, card } = this.state
+
+    return (
+      <div className="custom-data-carousel-box" style={{...config.style, minHeight: config.wrap.minHeight}}>
+        {loading ?
+          <div className="loading-mask">
+            {data ? <div className="ant-spin-blur"></div> : null}
+            <Spin />
+          </div> : null
+        }
+        {data && data.length > 0 ? <Carousel
+          autoplay={config.wrap.autoplay !== 'false'}
+          dots={config.wrap.dots !== 'false'}
+          dotPosition={config.wrap.dotPosition || 'bottom'}
+          effect={config.wrap.effect || 'scrollx'}
+          autoplaySpeed={config.wrap.speed}
+        >
+          {data.map((item, index) => (
+            <div key={index} onClick={() => {this.openView(item)}}>
+              <CardItem card={card} cards={config} data={item}/>
+            </div>
+          ))}
+        </Carousel> : null}
+        {!data || data.length === 0 ? <Empty description={false}/> : 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)(DataCard)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/carousel/data-card/index.scss b/src/tabviews/custom/components/carousel/data-card/index.scss
new file mode 100644
index 0000000..15319b2
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/data-card/index.scss
@@ -0,0 +1,49 @@
+.custom-data-carousel-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 30px;
+
+  .card-item-box {
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+
+  .ant-empty {
+    width: 100%;
+    min-height: 100px;
+    padding-top: 15px;
+
+    .ant-empty-image {
+      height: 60px;
+    }
+  }
+  .loading-mask {
+    position: absolute;
+    left: 0px;
+    top: 0;
+    right: 0px;
+    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-data-carousel-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.jsx b/src/tabviews/custom/components/carousel/prop-card/index.jsx
new file mode 100644
index 0000000..d519d08
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/prop-card/index.jsx
@@ -0,0 +1,363 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
+import { Spin, notification, Carousel } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import Api from '@/api'
+import UtilsDM from '@/utils/utils-datamanage.js'
+import MKEmitter from '@/utils/events.js'
+import { modifyTabview } from '@/store/action'
+import './index.scss'
+
+const CardItem = asyncComponent(() => import('../cardItem'))
+
+class PropCard extends Component {
+  static propTpyes = {
+    BID: PropTypes.any,              // 鐖剁骇Id
+    data: PropTypes.array,           // 缁熶竴鏌ヨ鏁版嵁
+    config: PropTypes.object,        // 缁勪欢閰嶇疆淇℃伅
+    mainSearch: PropTypes.any,       // 澶栧眰鎼滅储鏉′欢
+    menuType: PropTypes.any,         // 鑿滃崟绫诲瀷
+  }
+
+  state = {
+    BID: '',                   // 涓婄骇ID
+    config: null,              // 鍥捐〃閰嶇疆淇℃伅
+    loading: false,            // 鏁版嵁鍔犺浇鐘舵��
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: {}                   // 鏁版嵁
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, initdata, BID } = this.props
+    let _config = fromJS(this.props.config).toJS()
+    let _cols = new Map()
+
+    let _data = {}
+    let _sync = false
+    
+    if (_config.setting && _config.wrap.datatype !== 'static') {
+      _sync = _config.setting.sync === 'true'
+
+      if (_sync && data) {
+        _data = data[_config.dataName] || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      } else if (_sync && initdata) {
+        _data = initdata || {}
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0] || {}
+        }
+        _sync = false
+      }
+    } else {
+      _data = {}
+    }
+
+    if (_data) {
+      _data.$$BID = BID || ''
+    }
+
+    _config.columns.forEach(item => {
+      _cols.set(item.field, item)
+    })
+
+    _config.subcards.forEach(card => {
+      card.style.height = _config.style.height
+      if (card.setting.click) {
+        card.style.cursor = 'pointer'
+      }
+      card.elements = card.elements.map(item => {
+        if (item.field && _cols.has(item.field)) {
+          item.col = _cols.get(item.field)
+        }
+        return item
+      })
+    })
+
+    _config.wrap.speed = (_config.wrap.speed || 3) * 1000
+
+    this.setState({
+      sync: _sync,
+      data: _data,
+      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('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('reloadData', this.reloadData)
+    MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+    MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  /**
+   * @description 鍥捐〃鏁版嵁鏇存柊锛屽埛鏂板唴瀹�
+   */
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    const { sync, config, BID } = this.state
+
+    if (sync && !is(fromJS(this.props.data), fromJS(nextProps.data))) {
+      let _data = {}
+      if (nextProps.data && nextProps.data[config.dataName]) {
+        _data = nextProps.data[config.dataName]
+        if (_data && Array.isArray(_data)) {
+          _data = _data[0]
+        }
+      }
+
+      if (_data) {
+        _data.$$BID = BID || ''
+      }
+
+      this.setState({sync: false, data: _data})
+    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      if (config.wrap.datatype !== 'static' && config.setting.syncRefresh === 'true') {
+        this.setState({}, () => {
+          this.loadData()
+        })
+      }
+    }
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn) => {
+    const { config, BID } = this.state
+
+    if (config.uuid !== menuId) return
+
+    this.loadData()                                                            // 鏁版嵁鍒锋柊
+
+    if (btn.syncComponentId && btn.syncComponentId !== config.uuid && btn.syncComponentId !== config.setting.supModule) {
+      MKEmitter.emit('reloadData', btn.syncComponentId)                        // 鍚岀骇鏍囩鍒锋柊
+    }
+
+    if (position === 'mainline' && config.setting.supModule) {                 // 涓昏〃琛屽埛鏂�
+      MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty'))
+    } else if (position === 'popclose') {                                      // 鏍囩鍏抽棴鍒锋柊
+      config.setting.supModule && MKEmitter.emit('reloadData', config.setting.supModule, (BID || 'empty'))
+      btn.$tabId && MKEmitter.emit('refreshPopButton', btn.$tabId)
+    }
+  }
+
+  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()
+      })
+    }
+  }
+
+  reloadData = (menuId) => {
+    const { config } = this.state
+
+    if (menuId !== config.uuid) return
+
+    this.loadData()
+  }
+
+  async loadData () {
+    const { mainSearch, menuType } = this.props
+    const { config, arr_field, BID } = this.state
+
+    if (config.wrap.datatype === 'static') {
+      this.setState({
+        data: {$$BID: BID || ''},
+        loading: false
+      })
+      return
+    } else if (config.setting.supModule && !BID) { // BID 涓嶅瓨鍦ㄦ椂锛屼笉鍋氭煡璇�
+      this.setState({
+        data: {$$BID: BID || ''},
+        loading: false
+      })
+      return
+    }
+
+    let searches = []
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key)
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key)) {
+          searches.push(item)
+        }
+      })
+    }
+
+    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] : {}
+      _data.$$BID = BID || ''
+
+      this.setState({
+        data: _data,
+        loading: false
+      })
+    } else {
+      this.setState({
+        loading: false
+      })
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  openView = (item) => {
+    if (item.setting.click === 'menu') {
+      let menu = null
+
+      if (item.setting.menu && item.setting.menu.length > 0) {
+        let menu_id = item.setting.menu.slice(-1)[0]
+        menu = this.props.permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+      }
+
+      if (!menu) {
+        notification.warning({
+          top: 92,
+          message: '鑿滃崟宸插垹闄ゆ垨娌℃湁璁块棶鏉冮檺锛�',
+          duration: 5
+        })
+        return
+      }
+
+      let newtab = {
+        ...menu,
+        selected: true,
+        param: {}
+      }
+
+      if (item.setting.joint === 'true') {
+        newtab.param.$BID = item.setting.primaryId
+      }
+
+      if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+        this.props.modifyTabview([newtab])
+      } else {
+        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)
+        })
+      }
+    } else if (item.setting.click === 'link') {
+      let src = item.setting.linkurl
+
+      if (src.indexOf('paramsmain/') > -1) {
+        try {
+          let _url = src.split('paramsmain/')[0] + 'paramsmain/'
+          let _param = JSON.parse(window.decodeURIComponent(window.atob(src.split('paramsmain/')[1])))
+
+          _param.UserID = sessionStorage.getItem('UserID')
+          _param.LoginUID = sessionStorage.getItem('LoginUID')
+          _param.User_Name = sessionStorage.getItem('User_Name')
+          _param.param = { BID: item.setting.primaryId }
+          src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
+        } catch {
+          console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
+        }
+      } else if (item.setting.joint === 'true') {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        src = src + `${con}id=${item.setting.primaryId}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
+      }
+
+      window.open(src)
+    }
+  }
+
+
+  render() {
+    const { config, loading, data } = this.state
+
+    return (
+      <div className="custom-prop-carousel-box" style={config.style}>
+        {loading ?
+          <div className="loading-mask">
+            <div className="ant-spin-blur"></div>
+            <Spin />
+          </div> : null
+        }
+        <Carousel
+          autoplay={config.wrap.autoplay !== 'false'}
+          dots={config.wrap.dots !== 'false'}
+          dotPosition={config.wrap.dotPosition || 'bottom'}
+          effect={config.wrap.effect || 'scrollx'}
+          autoplaySpeed={config.wrap.speed}
+        >
+          {config.subcards.map((item, index) => (
+            <div key={index} onClick={() => {this.openView(item)}}>
+              <CardItem card={item} cards={config} data={data}/>
+            </div>
+          ))}
+        </Carousel>
+      </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)(PropCard)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/carousel/prop-card/index.scss b/src/tabviews/custom/components/carousel/prop-card/index.scss
new file mode 100644
index 0000000..b5e46e6
--- /dev/null
+++ b/src/tabviews/custom/components/carousel/prop-card/index.scss
@@ -0,0 +1,42 @@
+.custom-prop-carousel-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  min-height: 30px;
+  position: relative;
+
+  .card-item-box {
+    position: relative;
+    background-position: center center;
+    background-repeat: no-repeat;
+    background-size: cover;
+  }
+
+  .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-prop-carousel-box::after {
+  content: ' ';
+  display: block;
+  clear: both;
+}
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 c9a083d..01878f0 100644
--- a/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-bar-line/index.jsx
@@ -5,6 +5,7 @@
 import { connect } from 'react-redux'
 import DataSet from '@antv/data-set'
 import { Spin, Empty, Select, notification } from 'antd'
+import moment from 'moment'
 
 import asyncComponent from './asyncButtonComponent'
 import { chartColors } from '@/utils/option.js'
@@ -236,12 +237,14 @@
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
     MKEmitter.addListener('getexceloutparam', this.getexceloutparam)
     MKEmitter.addListener('refreshByButtonResult', this.refreshByButtonResult)
+    this.handleTimer()
   }
 
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
    */
   componentWillUnmount () {
+    clearTimeout(this.timer)
     this.setState = () => {
       return
     }
@@ -249,6 +252,65 @@
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
     MKEmitter.removeListener('getexceloutparam', this.getexceloutparam)
     MKEmitter.removeListener('refreshByButtonResult', this.refreshByButtonResult)
+  }
+
+  handleTimer = () => {
+    const { config } = this.state
+
+    if (!config.timer) return
+
+    const _change = {
+      '15s': 15000,
+      '30s': 30000,
+      '1min': 60000,
+      '5min': 300000,
+      '10min': 600000,
+      '15min': 900000,
+      '30min': 1800000,
+      '1hour': 3600000
+    }
+
+    let timer = _change[config.timer]
+
+    if (!timer) return
+
+    let _param = {
+      func: 's_get_timers_role',
+      LText: `select '${window.GLOB.appkey || ''}','${config.uuid}'`,
+      timer_type: config.timer,
+      component_id: config.uuid
+    }
+    
+    _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 鏃堕棿鎴�
+    _param.LText = Utils.formatOptions(_param.LText)                   // 鍏抽敭瀛楃鏇挎崲锛宐ase64鍔犲瘑
+    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5瀵嗛挜
+
+    Api.getSystemConfig(_param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      } else if (result.run_type) {
+        this.setState({timer})
+        this.timer = setTimeout(() => {
+          this.timerTask()
+        }, timer)
+      }
+    })
+  }
+
+  timerTask = () => {
+    const { timer } = this.state
+    if (!timer) return
+    
+    this.loadData(true)
+    
+    this.timer = setTimeout(() => {
+      this.timerTask()
+    }, timer)
   }
 
   /**
@@ -325,7 +387,7 @@
   /**
    * @description 鏁版嵁鍔犺浇
    */
-  async loadData () {
+  async loadData (hastimer) {
     const { mainSearch, menuType } = this.props
     const { config, arr_field, BID, search } = this.state
 
@@ -348,9 +410,11 @@
       })
     }
 
-    this.setState({
-      loading: true
-    })
+    if (!hastimer) {
+      this.setState({
+        loading: true
+      })
+    }
 
     let _orderBy = config.setting.order || ''
     let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID, menuType)
@@ -365,7 +429,8 @@
       })
     } else {
       this.setState({
-        loading: false
+        loading: false,
+        timer: null
       })
       notification.error({
         top: 92,
@@ -1285,8 +1350,7 @@
             ...menu,
             selected: true,
             param: {
-              BID: primaryId,
-              data: data
+              $BID: primaryId
             }
           }
   
@@ -1295,7 +1359,7 @@
             return tab.MenuID !== newtab.MenuID
           })
   
-          if (this.props.tabviews.length !== tabs.length) {
+          if (this.props.tabviews.length > tabs.length) {
             this.props.modifyTabview(fromJS(tabs).toJS())
           }
   
diff --git a/src/tabviews/custom/components/chart/antv-pie/index.jsx b/src/tabviews/custom/components/chart/antv-pie/index.jsx
index df58e29..c7fbd33 100644
--- a/src/tabviews/custom/components/chart/antv-pie/index.jsx
+++ b/src/tabviews/custom/components/chart/antv-pie/index.jsx
@@ -5,6 +5,7 @@
 import { connect } from 'react-redux'
 import DataSet from '@antv/data-set'
 import { Spin, Empty, notification } from 'antd'
+import moment from 'moment'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -115,17 +116,78 @@
   componentDidMount () {
     MKEmitter.addListener('reloadData', this.reloadData)
     MKEmitter.addListener('resetSelectLine', this.resetParentParam)
+    this.handleTimer()
   }
 
   /**
    * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊锛屾竻闄ゅ揩鎹烽敭璁剧疆
    */
   componentWillUnmount () {
+    clearTimeout(this.timer)
     this.setState = () => {
       return
     }
     MKEmitter.removeListener('reloadData', this.reloadData)
     MKEmitter.removeListener('resetSelectLine', this.resetParentParam)
+  }
+
+  handleTimer = () => {
+    const { config } = this.state
+
+    if (!config.timer) return
+
+    const _change = {
+      '15s': 15000,
+      '30s': 30000,
+      '1min': 60000,
+      '5min': 300000,
+      '10min': 600000,
+      '15min': 900000,
+      '30min': 1800000,
+      '1hour': 3600000
+    }
+
+    let timer = _change[config.timer]
+
+    if (!timer) return
+
+    let _param = {
+      func: 's_get_timers_role',
+      LText: `select '${window.GLOB.appkey || ''}','${config.uuid}'`,
+      timer_type: config.timer,
+      component_id: config.uuid
+    }
+    
+    _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')          // 鏃堕棿鎴�
+    _param.LText = Utils.formatOptions(_param.LText)                   // 鍏抽敭瀛楃鏇挎崲锛宐ase64鍔犲瘑
+    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)   // md5瀵嗛挜
+
+    Api.getSystemConfig(_param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        return
+      } else if (result.run_type) {
+        this.setState({timer})
+        this.timer = setTimeout(() => {
+          this.timerTask()
+        }, timer)
+      }
+    })
+  }
+
+  timerTask = () => {
+    const { timer } = this.state
+    if (!timer) return
+    
+    this.loadData(true)
+    
+    this.timer = setTimeout(() => {
+      this.timerTask()
+    }, timer)
   }
 
   reloadData = (menuId) => {
@@ -155,7 +217,7 @@
     this.pierender()
   }
 
-  async loadData () {
+  async loadData (hastimer) {
     const { mainSearch, menuType } = this.props
     const { config, arr_field, search, BID } = this.state
 
@@ -178,9 +240,11 @@
       })
     }
 
-    this.setState({
-      loading: true
-    })
+    if (!hastimer) {
+      this.setState({
+        loading: true
+      })
+    }
 
     let _orderBy = config.setting.order || ''
     let param = UtilsDM.getQueryDataParams(config.setting, arr_field, searches, _orderBy, '', '', BID, menuType)
@@ -195,7 +259,8 @@
       })
     } else {
       this.setState({
-        loading: false
+        loading: false,
+        timer: null
       })
       notification.error({
         top: 92,
@@ -537,8 +602,7 @@
             ...menu,
             selected: true,
             param: {
-              BID: primaryId,
-              data: data
+              $BID: primaryId
             }
           }
   
@@ -547,7 +611,7 @@
             return tab.MenuID !== newtab.MenuID
           })
   
-          if (this.props.tabviews.length !== tabs.length) {
+          if (this.props.tabviews.length > tabs.length) {
             this.props.modifyTabview(fromJS(tabs).toJS())
           }
   
diff --git a/src/tabviews/custom/components/form/normal-form/index.jsx b/src/tabviews/custom/components/form/normal-form/index.jsx
new file mode 100644
index 0000000..8d3fc08
--- /dev/null
+++ b/src/tabviews/custom/components/form/normal-form/index.jsx
@@ -0,0 +1,378 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { is, fromJS } from 'immutable'
+import { connect } from 'react-redux'
+import { Spin, notification, Button } from 'antd'
+// import moment from 'moment'
+
+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 NormalForm 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,            // 鏁版嵁鍔犺浇鐘舵��
+    activeKey: '',             // 閫変腑鏁版嵁
+    sync: false,               // 鏄惁缁熶竴璇锋眰鏁版嵁
+    data: null,                  // 鏁版嵁
+    group: null,
+    step: 0
+  }
+
+  UNSAFE_componentWillMount () {
+    const { data, BID, config } = this.props
+
+    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 = {}
+    }
+
+    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.btnstyle = group.subButton.style
+      group.subButton.OpenType = 'formSubmit'
+      group.subButton.execError = 'never'
+
+      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
+    })
+
+    let _group = config.subcards[0]
+
+    if (_data && 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({
+      sync: _sync,
+      data: _data,
+      group: _group,
+      step: _group.sort - 1,
+      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('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('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] || {}
+        }
+      }
+      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({sync: false, data: _data, group: _group, step: _group.sort - 1,})
+    } else if (nextProps.mainSearch && !is(fromJS(this.props.mainSearch), fromJS(nextProps.mainSearch))) {
+      if (config.wrap.datatype !== 'static' && config.setting.syncRefresh === 'true') {
+        this.setState({}, () => {
+          this.loadData()
+        })
+      }
+    }
+  }
+
+  /**
+   * @description 鎸夐挳鎵ц瀹屾垚鍚庨〉闈㈠埛鏂�
+   * @param {*} menuId     // 鑿滃崟Id
+   * @param {*} position   // 鍒锋柊浣嶇疆
+   * @param {*} btn        // 鎵ц鐨勬寜閽�
+   */
+  refreshByButtonResult = (menuId, position, btn) => {
+    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 (config.subcards.length > group.sort) {
+      let _group = config.subcards.filter(item => item.sort === (group.sort + 1))[0]
+  
+      this.setState({group: null, step: group.sort}, () => {
+        this.setState({group: _group})
+      })
+    } else {
+      this.setState({step: group.sort})
+    }
+
+    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: {}
+      }
+      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)
+      })
+    }
+  }
+
+  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()
+      })
+    }
+  }
+
+  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: {},
+        loading: false
+      })
+      return
+    }
+
+    let searches = []
+    if (mainSearch && mainSearch.length > 0) { // 涓昏〃鎼滅储鏉′欢
+      let keys = searches.map(item => item.key)
+      mainSearch.forEach(item => {
+        if (!keys.includes(item.key)) {
+          searches.push(item)
+        }
+      })
+    }
+
+    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
+      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: _group,
+        step: _group.sort - 1,
+        activeKey: '',
+        data: _data || {},
+        loading: false
+      })
+    } 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})
+    })
+  }
+
+  prevStep = () => {
+    const { config, group } = this.state
+
+    let _group = config.subcards.filter(item => item.sort === (group.sort - 1))[0]
+
+    this.setState({group: null, step: group.sort - 2}, () => {
+      this.setState({group: _group})
+    })
+  }
+
+  nextStep = () => {
+    const { config, group } = this.state
+
+    let _group = config.subcards.filter(item => item.sort === (group.sort + 1))[0]
+
+    this.setState({group: null, step: group.sort}, () => {
+      this.setState({group: _group})
+    })
+  }
+
+  render() {
+    const { config, loading, BID, data, group, dict, step } = this.state
+
+    return (
+      <div className="custom-normal-form-box" style={{...config.style}}>
+        {loading ?
+          <div className="loading-mask">
+            <div className="ant-spin-blur"></div>
+            <Spin />
+          </div> : null
+        }
+        {config.subcards.length > 1 ? <div className="mk-normal-form-title">
+          {config.subcards.map(card => (
+            <div key={card.uuid} className={'form-title' + (card.sort <= step ? ' active' : '')}>
+              <span className="form-sort" style={{background: config.wrap.color}}>{card.sort}</span>
+              <span className="before-line" style={{background: config.wrap.color}}></span>
+              <span className="after-line" style={{background: config.wrap.color}}></span>
+              {card.setting.title}
+            </div>))
+          }
+        </div> : null}
+        {group && data ? <MutilForm
+          BID={BID}
+          dict={dict}
+          data={data}
+          action={group}
+          inputSubmit={this.handleOk}
+          wrappedComponentRef={(inst) => this.formRef = inst}
+        /> : null}
+        {group && data ? <div className="mk-form-action">
+          {group.sort !== 1 ? <Button type="link" className="prev" onClick={this.prevStep} style={group.prevButton.style}>{group.prevButton.label}</Button> : null}
+          <NormalButton
+            BID={BID}
+            position="form"
+            btn={group.subButton}
+            setting={config.setting}
+            columns={config.columns}
+            selectedData={[data]}
+          />
+          {group.nextButton.enable === 'true' && group.sort !== config.subcards.length ? <Button type="link" className="skip" onClick={this.nextStep} style={group.nextButton.style}>{group.nextButton.label}</Button> : null}
+        </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)(NormalForm)
\ No newline at end of file
diff --git a/src/tabviews/custom/components/form/normal-form/index.scss b/src/tabviews/custom/components/form/normal-form/index.scss
new file mode 100644
index 0000000..285b505
--- /dev/null
+++ b/src/tabviews/custom/components/form/normal-form/index.scss
@@ -0,0 +1,108 @@
+.custom-normal-form-box {
+  background: #ffffff;
+  background-position: center center;
+  background-repeat: no-repeat;
+  background-size: cover;
+  position: relative;
+  min-height: 200px;
+
+  .mk-normal-form-title {
+    display: flex;
+    line-height: 30px;
+    min-height: 50px;
+    margin-bottom: 20px;
+    .form-title {
+      position: relative;
+      flex: 1;
+      text-align: center;
+      .form-sort {
+        background: #d8d8d8;
+        display: block;
+        width: 20px;
+        height: 20px;
+        line-height: 20px;
+        border-radius: 20px;
+        text-align: center;
+        color: #ffffff;
+        margin: 10px auto 0px;
+        position: relative;
+        z-index: 1;
+      }
+    }
+    .before-line, .after-line {
+      display: none;
+    }
+    .form-title:not(:first-child) .before-line {
+      position: absolute;
+      display: inline-block;
+      width: 50%;
+      height: 2px;
+      background: #d8d8d8;
+      left: 0%;
+      top: 18px;
+    }
+    .form-title:not(:last-child) .after-line {
+      position: absolute;
+      content: ' ';
+      display: inline-block;
+      width: 50%;
+      height: 2px;
+      background: #d8d8d8;
+      left: 50%;
+      top: 18px;
+    }
+    .form-title:not(.active) {
+      .form-sort {
+        background: #d8d8d8!important;
+      }
+      .before-line, .after-line {
+        background: #d8d8d8!important;
+      }
+    }
+  }
+
+  .mk-form-action {
+    position: relative;
+    text-align: center;
+    padding-bottom: 10px;
+  
+    .prev {
+      margin-right: 15px;
+    }
+    .submit {
+      min-width: 70px;
+      border: none;
+    }
+    .skip {
+      position: absolute;
+      right: 5px;
+    }
+  }
+  
+  .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-card-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 9eadab0..d814a27 100644
--- a/src/tabviews/custom/components/group/normal-group/index.jsx
+++ b/src/tabviews/custom/components/group/normal-group/index.jsx
@@ -152,6 +152,7 @@
     let diffUser = false
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -162,8 +163,8 @@
       let _script = item.script
 
       if (index === 0) {
-        _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-          select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+        _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}
         `
       }
diff --git a/src/tabviews/custom/components/share/normalTable/index.jsx b/src/tabviews/custom/components/share/normalTable/index.jsx
index ca764a7..f471844 100644
--- a/src/tabviews/custom/components/share/normalTable/index.jsx
+++ b/src/tabviews/custom/components/share/normalTable/index.jsx
@@ -201,11 +201,11 @@
       if (col.rowspan === 'true') {
         resProps.rowSpan = record['$$' + col.field]
       }
-
+      
       if (col.linkThdMenu || col.linkurl) {
         content = (
           <div>
-            <div className="link-menu" onDoubleClick={(e) => this.triggerLink(e, col, record)}></div>
+            <div className="link-menu" onDoubleClick={(e) => triggerLink(e, col, record)}></div>
             {content}
           </div>
         )
@@ -255,7 +255,7 @@
       if (col.linkThdMenu || col.linkurl) {
         content = (
           <div>
-            <div className="link-menu" onDoubleClick={(e) => this.triggerLink(e, col, record)}></div>
+            <div className="link-menu" onDoubleClick={(e) => triggerLink(e, col, record)}></div>
             {content}
           </div>
         )
@@ -504,61 +504,70 @@
 
   // 瀛楁閫忚
   triggerLink = (e, item, record) => {
-    const { tabviews, MenuID } = this.props
+    const { tabviews } = this.props
     e.stopPropagation()
+
+    let __param = {
+      $searchkey: item.field,
+      $searchval: record[item.field] || '',
+      $BID: record.$$uuid
+    }
+
+    if (item.linkfields && item.linkfields.length > 0) {
+      item.linkfields.forEach(field => {
+        __param[field] = record[field] || ''
+      })
+    }
 
     if (item.linkThdMenu) {
       let tabmenu = item.linkThdMenu
 
-      tabmenu.param = {
-        searchkey: item.field,
-        searchval: record[item.field] || '',
-        BID: record.$$uuid
-      }
-  
+      tabmenu.param = __param
       tabmenu.selected = true
   
-      let index = 0
-      let isexit = false
-      let tabs = tabviews.map((tab, i) => {
+      let tabs = tabviews.filter((tab, i) => {
         tab.selected = false
-  
-        if (tab.MenuID === MenuID) {
-          index = i
-        } else if (tab.MenuID === tabmenu.MenuID) {
-          tab.param = tabmenu.param
-          tab.selected = true
-          isexit = true
-        }
-  
-        return tab
+        return tab.MenuID !== tabmenu.MenuID
       })
   
-      if (!isexit) {
-        tabs.splice(index + 1, 0, tabmenu)
+      if (tabviews.length > tabs.length) {
+        this.props.modifyTabview(fromJS(tabs).toJS())
       }
   
-      this.props.modifyTabview(tabs)
-    } else {
+      this.setState({}, () => {
+        tabs.push(tabmenu)
+        this.props.modifyTabview(tabs)
+      })
+    } else if (item.linkurl) {
       let src = item.linkurl
 
-      if (item.linkurl.indexOf('paramsmain/') > -1) {
+      if (src.indexOf('paramsmain/') > -1) {
         try {
           let _url = item.linkurl.split('paramsmain/')[0] + 'paramsmain/'
           let _param = JSON.parse(window.decodeURIComponent(window.atob(item.linkurl.split('paramsmain/')[1])))
-          let dataparam = {
-            searchkey: item.field,
-            searchval: record[item.field] || '',
-            BID: record.$$uuid
-          }
           _param.UserID = sessionStorage.getItem('UserID')
           _param.LoginUID = sessionStorage.getItem('LoginUID')
           _param.User_Name = sessionStorage.getItem('User_Name')
-          _param.param = dataparam
+          _param.param = __param
           src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
         } catch {
           console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
         }
+      } else {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        if (item.linkfields && item.linkfields.length > 0) {
+          item.linkfields.forEach(field => {
+            if (field.toLowerCase() === 'id') return
+            con += `${field}=${record[field] || ''}&`
+          })
+        }
+        
+        src = src + `${con}id=${record.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
       }
 
       window.open(src)
@@ -577,7 +586,7 @@
       index = selectedRowKeys.slice(-1)[0]
     }
 
-    if (setting.tableType === 'checkbox') {
+    if (setting.tableType === 'checkbox' || setting.tableType === 'radio') {
       _activeIndex = index === '' ? null : index
     }
 
@@ -603,7 +612,7 @@
     if (setting.tableType === 'radio') {
       newkeys = [index]
       this.changedata(index)
-      this.setState({ selectedRowKeys: newkeys })
+      this.setState({ selectedRowKeys: newkeys, activeIndex: index })
     } else {
       let _index = ''
       if (newkeys.includes(index)) {
diff --git a/src/tabviews/custom/components/share/normalTable/index.scss b/src/tabviews/custom/components/share/normalTable/index.scss
index 45b1a84..19301ac 100644
--- a/src/tabviews/custom/components/share/normalTable/index.scss
+++ b/src/tabviews/custom/components/share/normalTable/index.scss
@@ -93,6 +93,14 @@
         vertical-align: middle;
       }
       .ant-table-tbody > tr > td.ant-table-column-has-actions {
+        .content {
+          position: relative;
+          z-index: 1;
+          word-wrap: break-word;
+          word-break: break-word;
+        }
+      }
+      .ant-table-tbody > tr > td {
         position: relative;
         .link-menu {
           position: absolute;
@@ -102,13 +110,6 @@
           bottom: 0px;
           opacity: 0;
           cursor: pointer;
-        }
-        
-        .content {
-          position: relative;
-          z-index: 1;
-          word-wrap: break-word;
-          word-break: break-word;
         }
       }
       .ant-table-tbody > tr > td .content {
diff --git a/src/tabviews/custom/components/share/tabtransfer/index.jsx b/src/tabviews/custom/components/share/tabtransfer/index.jsx
index 01d2693..03cbe65 100644
--- a/src/tabviews/custom/components/share/tabtransfer/index.jsx
+++ b/src/tabviews/custom/components/share/tabtransfer/index.jsx
@@ -167,6 +167,7 @@
     let diffUser = false
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -177,8 +178,8 @@
       let _script = item.script
 
       if (index === 0) {
-        _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-          select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+        _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}
         `
       }
diff --git a/src/tabviews/custom/components/table/normal-table/index.jsx b/src/tabviews/custom/components/table/normal-table/index.jsx
index 516709b..86035b3 100644
--- a/src/tabviews/custom/components/table/normal-table/index.jsx
+++ b/src/tabviews/custom/components/table/normal-table/index.jsx
@@ -227,19 +227,25 @@
     let result = await Api.genericInterface(param)
     if (result.status) {
       let data = fromJS(this.state.data).toJS()
+      let selectedData = fromJS(this.state.selectedData).toJS()
       if (result.data && result.data[0]) {
-        let _data = result.data[0]
-
+        let _data = result.data[0] || {}
+        _data.$$uuid = _data[setting.primaryKey] || ''
+        _data.$$BID = BID || ''
         try {
           data = data.map(item => {
-            if (item[setting.primaryKey] === _data[setting.primaryKey]) {
+            if (item.$$uuid === _data.$$uuid) {
               _data.key = item.key
-              _data.$$uuid = _data[setting.primaryKey] || ''
-              _data.$$BID = BID || ''
               return _data
             } else {
               return item
             }
+          })
+          selectedData = selectedData.map(item => {
+            if (_data.$$uuid === item.$$uuid) {
+              return _data
+            }
+            return item
           })
         } catch {
           console.warn('鏁版嵁鏌ヨ閿欒')
@@ -247,7 +253,8 @@
       }
 
       this.setState({
-        data: data,
+        data,
+        selectedData,
         loading: false
       })
     } else {
diff --git a/src/tabviews/custom/components/table/normal-table/index.scss b/src/tabviews/custom/components/table/normal-table/index.scss
index 49246df..05a3b2e 100644
--- a/src/tabviews/custom/components/table/normal-table/index.scss
+++ b/src/tabviews/custom/components/table/normal-table/index.scss
@@ -10,7 +10,7 @@
   >.button-list.toolbar-button {
     padding: 0;
     line-height: 55px;
-    padding-right: 110px;
+    padding-right: 60px;
     button {
       margin-right: 0px;
       margin-bottom: 0px;
diff --git a/src/tabviews/custom/index.jsx b/src/tabviews/custom/index.jsx
index f6c7e40..97b6365 100644
--- a/src/tabviews/custom/index.jsx
+++ b/src/tabviews/custom/index.jsx
@@ -23,6 +23,9 @@
 const AntvTabs = asyncComponent(() => import('./components/tabs/antv-tabs'))
 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 CarouselDataCard = asyncComponent(() => import('./components/carousel/data-card'))
+const CarouselPropCard = asyncComponent(() => import('./components/carousel/prop-card'))
 const TableCard = asyncComponent(() => import('./components/card/table-card'))
 const MainSearch = asyncComponent(() => import('@/tabviews/zshare/topSearch'))
 const NormalTable = asyncComponent(() => import('./components/table/normal-table'))
@@ -134,14 +137,8 @@
 
         component.search = component.search.map(item => {
           item.oriInitval = item.initval
-          if (['text', 'select', 'link'].includes(item.type) && param) {
-            if (param.searchkey === item.field) {
-              item.initval = param.searchval
-            } else if (param.BID && item.field.toLowerCase() === 'bid') {
-              item.initval = param.BID
-            } else if (param.data && param.data[item.field]) {
-              item.initval = param.data[item.field]
-            }
+          if (['text', 'select', 'link'].includes(item.type) && param && param.$searchkey === item.field) {
+            item.initval = param.$searchval
           }
 
           return item
@@ -151,7 +148,7 @@
       })
 
       let params = []
-      let BID = param && param.BID ? param.BID : ''
+      let BID = param && param.$BID ? param.$BID : ''
       let inherit = {}
 
       if (config.cacheUseful === 'true') { // 缂撳瓨缁ф壙
@@ -160,7 +157,38 @@
         inherit.cacheTime = config.cacheTime
       }
 
-      config.components = this.formatSetting(config.components, params, mainSearch, inherit)
+      let userName = sessionStorage.getItem('User_Name') || ''
+      let fullName = sessionStorage.getItem('Full_Name') || ''
+      let city = sessionStorage.getItem('city') || ''
+
+      if (sessionStorage.getItem('isEditState') === 'true') {
+        userName = sessionStorage.getItem('CloudUserName') || ''
+        fullName = sessionStorage.getItem('CloudFullName') || ''
+      }
+
+      let regs = [
+        { reg: /@userName@/ig, value: userName },
+        { reg: /@fullName@/ig, value: fullName },
+        { reg: /@login_city@/ig, value: city }
+      ]
+      
+      if (window.GLOB.externalDatabase !== null) {
+        regs.push({
+          reg: /@db@/ig,
+          value: window.GLOB.externalDatabase
+        })
+      }
+      if (config.urlFields) {
+        config.urlFields.forEach(field => {
+          let val = `'${param ? (param[field] || '') : ''}'`
+          regs.push({
+            reg: new RegExp(field, 'ig'),
+            value: val
+          })
+        })
+      }
+
+      config.components = this.formatSetting(config.components, params, mainSearch, inherit, regs)
 
       this.setState({
         BID: BID,
@@ -265,7 +293,7 @@
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
-        if (res.mk_ex_invoke === 'false') {
+        if (res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) {
           if (inters.length > 0) {
             this.loadOutResource(inters)
           }
@@ -307,7 +335,7 @@
       param[key] = result[key]
     })
 
-    Api.directRequest(url, setting.method, param).then(res => {
+    Api.directRequest(url, setting.method, param, setting.cross).then(res => {
       if (typeof(res) !== 'object' || Array.isArray(res)) {
         let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
 
@@ -413,6 +441,16 @@
         ) {
           return false
         }
+
+        item.subtabs = item.subtabs.filter(tab => {
+          if (
+            tab.blacklist && tab.blacklist.length > 0 &&
+            tab.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0
+          ) {
+            return false
+          }
+          return true
+        })
 
         item.subtabs = item.subtabs.map(tab => {
           tab.components = this.filterComponent(tab.components, roleId, permAction, permMenus)
@@ -537,7 +575,7 @@
             return cell.eleType !== 'button' || isHS || permAction[cell.uuid]
           })
         })
-      } else if (item.type === 'table' && item.subtype === 'tablecard') {
+      } else if ((item.type === 'table' && item.subtype === 'tablecard') || item.type === 'carousel') {
         item.subcards.forEach(card => {
           let _hasheight = card.style.height && card.style.height !== 'auto'
           card.elements = card.elements.filter(cell => {
@@ -644,22 +682,22 @@
   }
 
   // 鏍煎紡鍖栭粯璁よ缃�
-  formatSetting = (components, params, mainSearch, inherit) => {
+  formatSetting = (components, params, mainSearch, inherit, regs) => {
     return components.map(component => {
       if (component.type === 'tabs') {
         component.subtabs = component.subtabs.map(tab => {
-          tab.components = this.formatSetting(tab.components, [], [], inherit)
+          tab.components = this.formatSetting(tab.components, [], [], inherit, regs)
           tab = {...tab, ...inherit}
           return tab
         })
         return component
       } else if (component.type === 'group') {
-        component.components = this.formatSetting(component.components, [], [], inherit)
+        component.components = this.formatSetting(component.components, [], [], inherit, regs)
         component = {...component, ...inherit}
         return component
       }
 
-      if (['propcard', 'brafteditor', 'sandbox'].includes(component.subtype) && component.wrap.datatype === 'static') {
+      if (['propcard', 'brafteditor', 'sandbox', 'stepform'].includes(component.subtype) && component.wrap.datatype === 'static') {
         component.format = ''
       }
 
@@ -700,6 +738,11 @@
         component.setting.dataresource = component.setting.dataresource.replace(/@\$|\$@/ig, '')
         _customScript = _customScript.replace(/@\$|\$@/ig, '')
       }
+
+      regs.forEach(cell => {
+        component.setting.dataresource = component.setting.dataresource.replace(cell.reg, cell.value)
+        _customScript = _customScript.replace(cell.reg, cell.value)
+      })
 
       component.setting.customScript = _customScript // 鏁寸悊鍚庤嚜瀹氫箟鑴氭湰
 
@@ -798,6 +841,7 @@
     let diffUser = false
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -808,8 +852,8 @@
       let _script = item.script
 
       if (index === 0) {
-        _script = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-          select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+        _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}
         `
       }
@@ -961,6 +1005,12 @@
             <AntvPie config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'form') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <NormalForm config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
       } else if (item.type === 'search') {
         return (
           <Col span={item.width} key={item.uuid}>
@@ -985,6 +1035,18 @@
             <PropCard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
           </Col>
         )
+      } else if (item.type === 'carousel' && item.subtype === 'datacard') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <CarouselDataCard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
+      } else if (item.type === 'carousel' && item.subtype === 'propcard') {
+        return (
+          <Col span={item.width} key={item.uuid}>
+            <CarouselPropCard config={item} data={data} BID={_bid} mainSearch={mainSearch} menuType={menuType} />
+          </Col>
+        )
       } else if (item.type === 'table' && item.subtype === 'tablecard') {
         return (
           <Col span={item.width} key={item.uuid}>
diff --git a/src/tabviews/formtab/index.jsx b/src/tabviews/formtab/index.jsx
index 6d7a090..ed54cf2 100644
--- a/src/tabviews/formtab/index.jsx
+++ b/src/tabviews/formtab/index.jsx
@@ -198,13 +198,6 @@
             obj_name: 'data',
             arr_field: _option.field
           }
-
-          if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-            param.LText = param.LText.replace(/\$@/ig, '/*')
-            param.LText = param.LText.replace(/@\$/ig, '*/')
-          } else {
-            param.LText = param.LText.replace(/@\$|\$@/ig, '')
-          }
   
           param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
           param.secretkey = Utils.encrypt(param.LText, param.timestamp)
diff --git a/src/tabviews/iframe/index.jsx b/src/tabviews/iframe/index.jsx
index 2dea067..391c0de 100644
--- a/src/tabviews/iframe/index.jsx
+++ b/src/tabviews/iframe/index.jsx
@@ -15,7 +15,7 @@
 
   state = {
     visible: true,
-    dict: localStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? mzhCN : menUS,
     debug: sessionStorage.getItem('debug') === 'true'
   }
 
diff --git a/src/tabviews/rolemanage/index.jsx b/src/tabviews/rolemanage/index.jsx
index 7730ca1..99b8022 100644
--- a/src/tabviews/rolemanage/index.jsx
+++ b/src/tabviews/rolemanage/index.jsx
@@ -117,6 +117,11 @@
       let _openKeys = []
       // MenuID锛堣彍鍗旾d锛夈�丮enuName锛堣彍鍗曞悕绉帮級銆丱nlySelf锛堝�间负true锛岃〃绀轰笁绾ц彍鍗曪紝澧炲姞-浠呴〉闈級銆乀ype锛堣彍鍗曠骇鍒級
       // ParentID锛堢埗绾d锛夈�丼elected锛堟槸鍚﹂�変腑-宸插け鏁堬級銆乀abs锛� 鏍囩绫诲瀷锛夈�乀ypeCharOne 鑿滃崟绫诲瀷PC鎴栧叾浠�
+      // result.data.forEach(item => { // 娴嬭瘯
+      //   if (item.ParentID === '0') {
+      //     item.ParentID = item.TypeCharOne
+      //   }
+      // })
       let _tree = this.getTree(fromJS(mainMenus).toJS(), result.data)
       if (_tree[0]) {
         if (_tree[0].key === 'PC' && _tree[0].children) {
diff --git a/src/tabviews/scriptmanage/actionList/index.jsx b/src/tabviews/scriptmanage/actionList/index.jsx
index a17cc72..0aa7502 100644
--- a/src/tabviews/scriptmanage/actionList/index.jsx
+++ b/src/tabviews/scriptmanage/actionList/index.jsx
@@ -2,13 +2,15 @@
 import PropTypes from 'prop-types'
 import moment from 'moment'
 import { Button, Modal, notification, message } from 'antd'
-import MutilForm from '@/tabviews/zshare/mutilform'
+
+import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import options from '@/store/options.js'
 import Utils from '@/utils/utils.js'
 import Api from '@/api'
 import './index.scss'
 
 const { confirm } = Modal
+const MutilForm = asyncSpinComponent(() => import('@/tabviews/zshare/mutilform'))
 
 class MainAction extends Component {
   static propTpyes = {
diff --git a/src/tabviews/scriptmanage/config.jsx b/src/tabviews/scriptmanage/config.jsx
index 5402b14..e9ccbfc 100644
--- a/src/tabviews/scriptmanage/config.jsx
+++ b/src/tabviews/scriptmanage/config.jsx
@@ -58,12 +58,11 @@
     tables:[
       {"TbName":"s_custom_script","Remark":"鑷畾涔夎剼鏈�"}
     ],
-    groups:[],
     fields:[
-      {"label":"鍑芥暟","field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
-      {"label":"鎺掑簭","field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"15870101796149403f2pqfpviuo415m2"},
-      {"label":"鎻忚堪","field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
-      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
+      {"label":"鍑芥暟",span:12,labelwidth: 33.3,"field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
+      {"label":"鎺掑簭",span:12,labelwidth: 33.3,"field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"15870101796149403f2pqfpviuo415m2"},
+      {"label":"鎻忚堪",span:24,labelwidth: 16.3,"field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
+      {"label":"鑴氭湰",span:24,labelwidth: 16.3,"field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
     ]
   },
   '1587007258155ut4nbggg4r66t9uhut2': {
@@ -79,12 +78,11 @@
       "display":"modal"
     },
     tables: [{"TbName":"s_custom_script","Remark":"鑷畾涔夎剼鏈�"}],
-    groups: [],
     fields: [
-      {"label":"鍑芥暟","field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
-      {"label":"鎺掑簭","field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"1587010196675i9m6ie3tv9kg2rhgfi0"},
-      {"label":"鎻忚堪","field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
-      {"label":"鑴氭湰","field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
+      {"label":"鍑芥暟",span:12,labelwidth: 33.3,"field":"func","type":"text","initval":"","readonly":"false","required":"true","hidden":"false","readin":"true","fieldlength":50,"regular":"funcname","supField":"","blacklist":[],"uuid":"1587006164634l397q15t49u2pfq02f5"},
+      {"label":"鎺掑簭",span:12,labelwidth: 33.3,"field":"Sort","type":"number","initval":0,"decimal":0,"min":"","max":"","readonly":"false","hidden":"false","readin":"true","supField":"","blacklist":[],"uuid":"1587010196675i9m6ie3tv9kg2rhgfi0"},
+      {"label":"鎻忚堪",span:24,labelwidth: 16.3,"field":"Remark","type":"textarea","initval":"","readonly":"false","required":"false","hidden":"false","readin":"true","fieldlength":512,"maxRows":6,"supField":"","blacklist":[],"uuid":"1587006199263k8hm45cmtomgu6hd881"},
+      {"label":"鑴氭湰",span:24,labelwidth: 16.3,"field":"LongParam","type":"textarea","initval":"","readonly":"false","required":"true","encryption":"true","hidden":"false","readin":"true","fieldlength":12000,"maxRows":20,"supField":"","blacklist":[],"uuid":"1587006209935qbkle15h4d9i9lg9tcu"}
     ]
   }
 }
diff --git a/src/tabviews/scriptmanage/index.jsx b/src/tabviews/scriptmanage/index.jsx
index ed0910c..68a4ce8 100644
--- a/src/tabviews/scriptmanage/index.jsx
+++ b/src/tabviews/scriptmanage/index.jsx
@@ -120,6 +120,7 @@
    * @description 涓昏〃鏁版嵁鍔犺浇
    */ 
   async loadmaindata () {
+    const { setting } = this.state
     let param = this.getDefaultParam()
 
     this.setState({
@@ -136,6 +137,7 @@
         data: result.data.map((item, index) => {
           // item.LongParam = this.UnformatOptions(item.LongParam)
           item.key = index
+          item.$$uuid = item[setting.primaryKey] || ''
           return item
         }),
         total: result.total,
diff --git a/src/tabviews/subtable/index.jsx b/src/tabviews/subtable/index.jsx
index 07dbc4c..b31d3b2 100644
--- a/src/tabviews/subtable/index.jsx
+++ b/src/tabviews/subtable/index.jsx
@@ -11,7 +11,6 @@
 import options from '@/store/options.js'
 import UtilsDM from '@/utils/utils-datamanage.js'
 import { updateSubTable } from '@/utils/utils-update.js'
-import { modifyTabview } from '@/store/action'
 import asyncComponent from '@/utils/asyncComponent'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import NotFount from '@/components/404'
@@ -180,6 +179,13 @@
         if (col.field) {
           _arrField.push(col.field)
 
+          if (col.linkmenu && col.linkmenu.length > 0) {
+            let menu_id = col.linkmenu.slice(-1)[0]
+            col.linkThdMenu = permMenus.filter(m => m.MenuID === menu_id)[0] || ''
+          } else {
+            col.linkThdMenu = ''
+          }
+
           col.nameField && _arrField.push(col.nameField) // 閾炬帴鍚嶅瓧娈�
           if (col.Hide !== 'true' && col.type === 'number' && col.sum === 'true') {
             statFields.push(col)
@@ -197,13 +203,6 @@
       // 鐢熸垚鏄剧ず鍒楋紝澶勭悊鍚堝苟鍒椾腑鐨勫瓧娈�
       config.columns.forEach((col, index) => {
         if (_hideCol.includes(col.uuid)) return
-
-        if (col.linkmenu && col.linkmenu.length > 0) {
-          let menu_id = col.linkmenu.slice(-1)[0]
-          col.linkThdMenu = permMenus.filter(m => m.MenuID === menu_id)[0] || ''
-        } else {
-          col.linkThdMenu = ''
-        }
 
         col.sort = index
 
@@ -418,7 +417,7 @@
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
-        if (res.mk_ex_invoke === 'false') {
+        if (res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) {
           this.loadmaindata()
         } else {
           this.customOuterRequest(res)
@@ -466,7 +465,7 @@
       param[key] = result[key]
     })
 
-    Api.directRequest(url, setting.method, param).then(res => {
+    Api.directRequest(url, setting.method, param, setting.cross).then(res => {
       if (typeof(res) !== 'object' || Array.isArray(res)) {
         let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
 
@@ -657,8 +656,9 @@
     let result = await Api.genericInterface(param)
     if (result.status) {
       let data = fromJS(this.state.data).toJS()
+      let selectedData = fromJS(this.state.selectedData).toJS()
       if (result.data && result.data[0]) {
-        let _data = result.data[0]
+        let _data = result.data[0] || {}
 
         if (absFields.length) {
           absFields.forEach(field => {
@@ -669,17 +669,23 @@
           })
         }
 
+        _data.$$uuid = _data[setting.primaryKey] || ''
+        _data.$$BID = BID || ''
+
         try {
           data = data.map(item => {
-            if (item[setting.primaryKey] === _data[setting.primaryKey]) {
+            if (item.$$uuid === _data.$$uuid) {
               _data.key = item.key
-              _data.$$uuid = _data[setting.primaryKey] || ''
-              _data.$$BID = BID || ''
-
               return _data
             } else {
               return item
             }
+          })
+          selectedData = selectedData.map(item => {
+            if (_data.$$uuid === item.$$uuid) {
+              return _data
+            }
+            return item
           })
         } catch {
           console.warn('鏁版嵁鏌ヨ閿欒')
@@ -687,7 +693,8 @@
       }
 
       this.setState({
-        data: data,
+        data,
+        selectedData,
         loading: false
       })
     } else {
@@ -1031,17 +1038,14 @@
 
 const mapStateToProps = (state) => {
   return {
-    tabviews: state.tabviews,
     menuType: state.editLevel,
     permAction: state.permAction,
     permMenus: state.permMenus,
   }
 }
 
-const mapDispatchToProps = (dispatch) => {
-  return {
-    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews))
-  }
+const mapDispatchToProps = () => {
+  return {}
 }
 
 export default connect(mapStateToProps, mapDispatchToProps)(SubTabViewTable)
\ No newline at end of file
diff --git a/src/tabviews/subtabtable/index.jsx b/src/tabviews/subtabtable/index.jsx
index 58ecb38..7cda524 100644
--- a/src/tabviews/subtabtable/index.jsx
+++ b/src/tabviews/subtabtable/index.jsx
@@ -379,7 +379,7 @@
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
-        if (res.mk_ex_invoke === 'false') {
+        if (res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) {
           this.loadmaindata()
         } else {
           this.customOuterRequest(res)
@@ -427,7 +427,7 @@
       param[key] = result[key]
     })
 
-    Api.directRequest(url, setting.method, param).then(res => {
+    Api.directRequest(url, setting.method, param, setting.cross).then(res => {
       if (typeof(res) !== 'object' || Array.isArray(res)) {
         let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
 
diff --git a/src/tabviews/treepage/index.jsx b/src/tabviews/treepage/index.jsx
index 76e31fa..5590efc 100644
--- a/src/tabviews/treepage/index.jsx
+++ b/src/tabviews/treepage/index.jsx
@@ -174,7 +174,7 @@
       }
 
       this.setState({
-        BID: param && param.BID ? param.BID : '',
+        BID: param && param.$BID ? param.$BID : '',
         loadingview: false,
         config: config,
         setting: config.setting,
diff --git a/src/tabviews/verupmanage/actionList/index.jsx b/src/tabviews/verupmanage/actionList/index.jsx
index 5671678..6947633 100644
--- a/src/tabviews/verupmanage/actionList/index.jsx
+++ b/src/tabviews/verupmanage/actionList/index.jsx
@@ -2,13 +2,16 @@
 import PropTypes from 'prop-types'
 import moment from 'moment'
 import { Button, Modal, notification, message } from 'antd'
-import MutilForm from '@/tabviews/zshare/mutilform'
+
+import asyncSpinComponent from '@/utils/asyncSpinComponent'
 import Utils, { getSysDefaultSql } from '@/utils/utils.js'
 import options from '@/store/options.js'
+import { updateForm } from '@/utils/utils-update.js'
 import Api from '@/api'
 import './index.scss'
 
 const { confirm } = Modal
+const MutilForm = asyncSpinComponent(() => import('@/tabviews/zshare/mutilform'))
 
 class MainAction extends Component {
   static propTpyes = {
@@ -109,6 +112,7 @@
         this.setState({loadingUuid: ''})
       })
     } else if (item.OpenType === 'pop') {
+      item = updateForm(item)
       if (item.setting.display === 'prompt') { // 濡傛灉琛ㄥ崟浠ユ槸鍚︽灞曠ず锛屼笉璇锋眰涓嬫媺鑿滃崟淇℃伅
         this.setState({
           execAction: item,
@@ -648,17 +652,10 @@
     const { BData } = this.props
     const { execAction, tabledata } = this.state
     let _this = this
-    let _fields = []
-    
-    if (execAction.groups.length > 0) {
-      execAction.groups.forEach(group => {
-        _fields = [..._fields, ...group.sublist]
-      })
-    } else {
-      _fields = execAction.fields
-    }
 
-    let result = _fields.map(item => {
+    let result = []
+    execAction.fields.forEach(item => {
+      if (!item.field) return
       let _readin = item.readin !== 'false'
       let _initval = item.initval
 
@@ -679,14 +676,14 @@
         _fieldlen = item.decimal ? item.decimal : 0
       }
 
-      return {
+      result.push({
         key: item.field,
         readonly: item.readonly === 'true',
         readin: _readin,
         fieldlen: _fieldlen,
         type: item.type,
         value: _initval
-      }
+      })
     })
 
     confirm({
diff --git a/src/tabviews/verupmanage/config.jsx b/src/tabviews/verupmanage/config.jsx
index 269c5cf..37a7348 100644
--- a/src/tabviews/verupmanage/config.jsx
+++ b/src/tabviews/verupmanage/config.jsx
@@ -55,11 +55,11 @@
 export const buttonConfig = {
   '1583979660949vpssdb2p2lsqff9abkr': {
     type: 'Modal',
-    setting:{title: '娣诲姞',width:45,focus:'VersionName',cols:'1',finish:'close',clickouter:'unclose',container:'tab',display:'modal'},
+    version: '1.0',
+    setting:{title: '娣诲姞',width:45,focus:'VersionName',finish:'close',clickouter:'unclose',container:'tab',display:'modal'},
     tables:[],
-    groups:[],
     fields:[
-      {label:'浼犺緭鍙�',field:'VersionName',type:'text',initval:'',regular:'letter&number',readonly:'false',required:'true',hidden:'false',fieldlength:20,readin:'true',uuid:'1581738428097qgoe876i5u0866373uu'}
+      {label:'浼犺緭鍙�',field:'VersionName',span:24,labelwidth: 33.3,type:'text',initval:'',regular:'letter&number',readonly:'false',required:'true',hidden:'false',fieldlength:20,readin:'true',uuid:'1581738428097qgoe876i5u0866373uu'}
     ]
   },
   '1583983849299g1qfd28g3c9n9e0e57a': {
diff --git a/src/tabviews/verupmanage/index.jsx b/src/tabviews/verupmanage/index.jsx
index e069f0f..8aa29d5 100644
--- a/src/tabviews/verupmanage/index.jsx
+++ b/src/tabviews/verupmanage/index.jsx
@@ -152,6 +152,7 @@
       this.setState({
         data: result.data.map((item, index) => {
           item.key = index
+          item.$$uuid = item[setting.primaryKey] || ''
           return item
         }),
         total: result.total,
diff --git a/src/tabviews/verupmanage/subtabtable/index.jsx b/src/tabviews/verupmanage/subtabtable/index.jsx
index 553cb4a..f33788b 100644
--- a/src/tabviews/verupmanage/subtabtable/index.jsx
+++ b/src/tabviews/verupmanage/subtabtable/index.jsx
@@ -183,6 +183,7 @@
       this.setState({
         data: result.data.map((item, index) => {
           item.key = index
+          item.$$uuid = item[setting.primaryKey] || ''
           item.$$BID = _BID || ''
           return item
         }),
diff --git a/src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx b/src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx
index b4481c0..b2c76db 100644
--- a/src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx
+++ b/src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx
@@ -43,6 +43,7 @@
 
         let errors = null
         let sheetName = btn.verify.sheet
+        let errDetail = ''
 
         if (Object.keys(workbook.Sheets).length === 1) {
           sheetName = Object.keys(workbook.Sheets)[0]
@@ -58,14 +59,15 @@
           } else {
             let iserror = false
             btn.verify.columns.forEach(op => {
-              if (header[op.Column] !== op.Text) {
+              let _name = typeof(header[op.Column]) === 'string' ? header[op.Column].replace(/(^\s*|\s*$)/g, '') : header[op.Column]
+              let _text = op.Text ? op.Text.replace(/(^\s*|\s*$)/g, '') : op.Text
+              
+              if (_name !== _text && !iserror) {
                 iserror = true
+                errors = 'headerError'
+                errDetail = `Excel涓紙${_name}锛変笌鎸夐挳鍒椾俊鎭紙${_text}锛変笉涓�鑷达紒`
               }
             })
-
-            if (iserror) {
-              errors = 'headerError'
-            }
           }
         }
 
@@ -76,7 +78,7 @@
         }
 
         // 鏈�缁堣幏鍙栧埌骞朵笖鏍煎紡鍖栧悗鐨� json 鏁版嵁
-        this.props.returndata(data, errors, sheetName)
+        this.props.returndata(data, errors, sheetName, errDetail)
         this.setState({
           excelId: '',
         }, () => {
diff --git a/src/tabviews/zshare/actionList/excelInbutton/index.jsx b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
index 1312e0d..0d53a35 100644
--- a/src/tabviews/zshare/actionList/excelInbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/excelInbutton/index.jsx
@@ -194,7 +194,7 @@
   /**
    * @description Excel 瀵煎叆
    */
-  getexceldata = (data, errors, sheetName) => {
+  getexceldata = (data, errors, sheetName, errDetail) => {
     const { btn } = this.props
 
     if (errors) {
@@ -213,7 +213,7 @@
       } else if (errors === 'headerError') {
         notification.warning({
           top: 92,
-          message: '宸ヤ綔琛ㄣ��' + sheetName + '銆嬭〃澶磋缃敊璇紝璇锋鏌ヨ〃澶翠腑鐨勫悕绉板強椤哄簭锛屼笌鎸夐挳Excel鍒椾俊鎭槸鍚︿竴鑷达紒',
+          message: `宸ヤ綔琛ㄣ��${sheetName}銆嬭〃澶撮敊璇紝${errDetail}`,
           duration: 5
         })
       }
diff --git a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
index f2a67e7..121fc6f 100644
--- a/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/exceloutbutton/index.jsx
@@ -433,12 +433,17 @@
       let _header = []
       let _topRow = {}
       let colwidth = []
+      let abses = []
 
       btn.verify.columns.forEach(col => {
         if (_topRow[col.Column]) return
 
         _header.push(col.Column)
         _topRow[col.Column] = col.Text
+
+        if (col.abs === 'true') {
+          abses.push(col.Column)
+        }
 
         colwidth.push({width: col.Width || 20})
       })
@@ -447,14 +452,29 @@
 
       table.push(_topRow)
 
-      data && data.forEach(item => {
-        let _row = {}
-        _header.forEach(field => {
-          _row[field] = item[field]
+      if (data && abses.length > 0) {
+        data.forEach(item => {
+          let _row = {}
+          _header.forEach(field => {
+            if (item[field] && abses.includes(field)) {
+              _row[field] = Math.abs(item[field])
+            } else {
+              _row[field] = item[field]
+            }
+          })
+  
+          table.push(_row)
         })
-
-        table.push(_row)
-      })
+      } else if (data) {
+        data.forEach(item => {
+          let _row = {}
+          _header.forEach(field => {
+            _row[field] = item[field]
+          })
+  
+          table.push(_row)
+        })
+      }
 
       const ws = XLSX.utils.json_to_sheet(table, {header: _header, skipHeader: true})
 
@@ -594,6 +614,7 @@
       let allSearch = Utils.getAllSearchOptions(search)
       let userName = sessionStorage.getItem('User_Name') || ''
       let fullName = sessionStorage.getItem('Full_Name') || ''
+      let city = sessionStorage.getItem('city') || ''
 
       if (sessionStorage.getItem('isEditState') === 'true') {
         userName = sessionStorage.getItem('CloudUserName') || ''
@@ -608,6 +629,9 @@
       })
 
       regoptions.push({
+        reg: new RegExp('@login_city@', 'ig'),
+        value: city
+      }, {
         reg: new RegExp('@userName@', 'ig'),
         value: userName
       }, {
diff --git a/src/tabviews/zshare/actionList/index.jsx b/src/tabviews/zshare/actionList/index.jsx
index 217f87d..412f9cb 100644
--- a/src/tabviews/zshare/actionList/index.jsx
+++ b/src/tabviews/zshare/actionList/index.jsx
@@ -97,7 +97,7 @@
             selectedData={selectedData}
           />
         )
-      } else if (item.OpenType === 'tab' || item.OpenType === 'blank') {
+      } else if (item.OpenType === 'tab') {
         return (
           <TabButton
             key={item.uuid}
diff --git a/src/tabviews/zshare/actionList/normalbutton/index.jsx b/src/tabviews/zshare/actionList/normalbutton/index.jsx
index 792b7ef..076f844 100644
--- a/src/tabviews/zshare/actionList/normalbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/normalbutton/index.jsx
@@ -11,6 +11,7 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
+import { updateForm } from '@/utils/utils-update.js'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
@@ -53,6 +54,8 @@
 
     if (position === 'toolbar') {
       MKEmitter.addListener('triggerBtnId', this.actionTrigger)
+    } else if (position === 'form') {
+      MKEmitter.addListener('triggerFormSubmit', this.actionSubmit)
     }
   }
 
@@ -61,6 +64,19 @@
       return
     }
     MKEmitter.removeListener('triggerBtnId', this.actionTrigger)
+    MKEmitter.removeListener('triggerFormSubmit', this.actionSubmit)
+  }
+
+  actionSubmit = (res) => {
+    const { btn } = this.props
+
+    if (btn.uuid !== res.menuId) return
+
+    this.setState({
+      loading: true
+    })
+
+    this.execSubmit(this.state.tabledata, () => {}, res.form)
   }
 
   /**
@@ -116,7 +132,7 @@
         duration: 5
       })
       return
-    } else if (!setting.primaryKey) {
+    } else if (btn.OpenType !== 'formSubmit' && !setting.primaryKey) {
       // 闇�瑕侀�夋嫨琛屾椂锛屾牎楠屾槸鍚﹁缃富閿�
       notification.warning({
         top: 92,
@@ -178,7 +194,13 @@
       return
     }
 
-    if (btn.OpenType === 'prompt') {
+    if (btn.OpenType === 'formSubmit') {
+      this.setState({
+        tabledata: data
+      })
+      MKEmitter.emit('mkFormSubmit', btn.uuid)
+      return
+    } else if (btn.OpenType === 'prompt') {
       this.updateStatus('start')
       confirm({
         title: this.state.dict['main.action.confirm.tip'],
@@ -197,9 +219,14 @@
     } else if (btn.OpenType === 'pop') {
       this.updateStatus('start')
 
+      let modal = this.state.btnconfig
+      if (!modal && btn.modal) {
+        modal = this.handleModelConfig(btn.modal)
+      }
+
       this.setState({
         tabledata: data,
-        btnconfig: btn.modal ? btn.modal : this.state.btnconfig
+        btnconfig: modal
       }, () => {
         this.improveAction()
       })
@@ -269,7 +296,7 @@
         }
 
         param.LText = Utils.formatOptions(param.LText)
-      } else if (btn.OpenType === 'pop') { // 琛ㄥ崟
+      } else if (btn.OpenType === 'pop' || btn.OpenType === 'formSubmit') { // 琛ㄥ崟
         if (btn.sqlType === 'insert') { // 绯荤粺鍑芥暟娣诲姞鏃讹紝鐢熸垚uuid
           primaryId = ''
 
@@ -493,7 +520,7 @@
 
       param[setting.primaryKey] = primaryId // 璁剧疆涓婚敭鍙傛暟
 
-      if (btn.OpenType === 'pop') { // 琛ㄥ崟
+      if (btn.OpenType === 'pop' || btn.OpenType === 'formSubmit') { // 琛ㄥ崟
         formdata.forEach(_data => {
           param[_data.key] = _data.value
         })
@@ -604,7 +631,7 @@
           param.BID = this.props.BID
         }
 
-        if (btn.OpenType === 'pop' && formdata) { // 琛ㄥ崟
+        if ((btn.OpenType === 'pop' || btn.OpenType === 'formSubmit') && formdata) { // 琛ㄥ崟
           formdata.forEach(_data => {
             param[_data.key] = _data.value
           })
@@ -690,10 +717,10 @@
 
     Api.genericInterface(param).then(res => {
       if (res.status) {
-        if (res.mk_ex_invoke === 'false' && params.length === 0) {
+        if ((res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) && params.length === 0) {
           this.execSuccess(res)
           _resolve()
-        } else if (res.mk_ex_invoke === 'false' && params.length > 0) {
+        } else if ((res.mk_ex_invoke === 'false' || res.mk_ex_invoke === false) && params.length > 0) {
           this.customLoopRequest(params, _resolve)
         } else {
           this.customOuterRequest(params, res, record, _resolve)
@@ -707,10 +734,10 @@
             return new Promise(resolve => {
               Api.genericInterface(_this.state.checkParam).then((result) => {
                 if (result.status) {
-                  if (result.mk_ex_invoke === 'false' && params.length === 0) {
+                  if ((result.mk_ex_invoke === 'false' || result.mk_ex_invoke === false) && params.length === 0) {
                     _this.execSuccess(result)
                     _resolve()
-                  } else if (result.mk_ex_invoke === 'false' && params.length > 0) {
+                  } else if ((result.mk_ex_invoke === 'false' || result.mk_ex_invoke === false) && params.length > 0) {
                     _this.customLoopRequest(params, _resolve)
                   } else {
                     _this.customOuterRequest(params, result, record, _resolve)
@@ -772,7 +799,7 @@
       param[key] = result[key]
     })
 
-    Api.directRequest(url, btn.method, param).then(res => {
+    Api.directRequest(url, btn.method, param, btn.cross).then(res => {
       if (typeof(res) !== 'object' || Array.isArray(res)) {
         let error = '鏈煡鐨勮繑鍥炵粨鏋滐紒'
 
@@ -1195,7 +1222,7 @@
     }
 
     if (btn.execSuccess !== 'never') {
-      MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execSuccess, btn)
+      MKEmitter.emit('refreshByButtonResult', btn.$menuId, btn.execSuccess || '', btn)
     }
   }
 
@@ -1397,6 +1424,29 @@
     })
   }
 
+  handleModelConfig = (config) => {
+    let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
+    config.fields = config.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 || cell.blacklist.length === 0) return cell
+      if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+        cell.hidden = 'true'
+      }
+
+      return cell
+    })
+    return config
+  }
+
   /**
    * @description 鑾峰彇鎸夐挳閰嶇疆淇℃伅
    */
@@ -1443,67 +1493,9 @@
           })
           this.updateStatus('over')
         } else {
-          let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
-          if (_LongParam.groups.length > 0) {
-            _LongParam.groups.forEach(group => {
-              group.sublist = group.sublist.map(cell => {
-                // 鏁版嵁婧恠ql璇彞锛岄澶勭悊锛� 鏉冮檺榛戝悕鍗曞瓧娈佃缃负闅愯棌琛ㄥ崟
-                if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
-                  let _option = Utils.getSelectQueryOptions(cell)
-
-                  if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                    _option.sql = _option.sql.replace(/\$@/ig, '/*')
-                    _option.sql = _option.sql.replace(/@\$/ig, '*/')
-                  } else {
-                    _option.sql = _option.sql.replace(/@\$|\$@/ig, '')
-                  }
-                  // 澶栬仈鏁版嵁搴撴浛鎹�
-                  if (window.GLOB.externalDatabase !== null) {
-                    _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
-                  }
-
-                  cell.data_sql = Utils.formatOptions(_option.sql)
-                  cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
-                  cell.arr_field = _option.field
-                }
-
-                // 瀛楁鏉冮檺榛戝悕鍗�
-                if (!cell.blacklist || cell.blacklist.length === 0) return cell
-                if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-                  cell.hidden = 'true'
-                }
-
-                return cell
-              })
-            })
-          } else {
-            _LongParam.fields = _LongParam.fields.map(cell => {
-              // 鏁版嵁婧恠ql璇彞锛岄澶勭悊锛屾潈闄愰粦鍚嶅崟瀛楁璁剧疆涓洪殣钘忚〃鍗�
-              if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
-                let _option = Utils.getSelectQueryOptions(cell)
-
-                if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                  _option.sql = _option.sql.replace(/\$@/ig, '/*')
-                  _option.sql = _option.sql.replace(/@\$/ig, '*/')
-                } else {
-                  _option.sql = _option.sql.replace(/@\$|\$@/ig, '')
-                }
-
-                cell.data_sql = Utils.formatOptions(_option.sql)
-                cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
-                cell.arr_field = _option.field
-              }
-
-              // 瀛楁鏉冮檺榛戝悕鍗�
-              if (!cell.blacklist || cell.blacklist.length === 0) return cell
-              if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-                cell.hidden = 'true'
-              }
-
-              return cell
-            })
-          }
-
+          _LongParam = updateForm(_LongParam)
+          _LongParam = this.handleModelConfig(_LongParam)
+          
           this.setState({
             btnconfig: _LongParam
           }, () => {
@@ -1552,17 +1544,10 @@
     const { BData } = this.props
     const { btnconfig, tabledata } = this.state
     let _this = this
-    let _fields = []
-    
-    if (btnconfig.groups.length > 0) {
-      btnconfig.groups.forEach(group => {
-        _fields = [..._fields, ...group.sublist]
-      })
-    } else {
-      _fields = btnconfig.fields
-    }
 
-    let result = _fields.map(item => {
+    let result = []
+    btnconfig.fields.forEach(item => {
+      if (!item.field) return
       let _readin = item.readin !== 'false'
       let _initval = item.initval
 
@@ -1589,14 +1574,19 @@
         _fieldlen = item.decimal ? item.decimal : 0
       }
 
-      return {
+      if (_initval === undefined) {
+        _initval = ''
+      }
+
+      result.push({
         key: item.field,
         readonly: item.readonly === 'true',
         readin: _readin,
         fieldlen: _fieldlen,
+        writein: item.writein !== 'false',
         type: item.type,
         value: _initval
-      }
+      })
     })
 
     confirm({
diff --git a/src/tabviews/zshare/actionList/printbutton/index.jsx b/src/tabviews/zshare/actionList/printbutton/index.jsx
index 5557e08..323d5ab 100644
--- a/src/tabviews/zshare/actionList/printbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/printbutton/index.jsx
@@ -11,6 +11,7 @@
 import zhCN from '@/locales/zh-CN/main.js'
 import enUS from '@/locales/en-US/main.js'
 import asyncSpinComponent from '@/utils/asyncSpinComponent'
+import { updateForm } from '@/utils/utils-update.js'
 import MKEmitter from '@/utils/events.js'
 import './index.scss'
 
@@ -140,9 +141,14 @@
 
     if (btn.execMode === 'pop') {
       this.updateStatus('start')
+      let modal = this.state.btnconfig
+      if (!modal && btn.modal) {
+        modal = this.handleModelConfig(btn.modal)
+      }
+
       this.setState({
         tabledata: data,
-        btnconfig: btn.modal ? btn.modal : this.state.btnconfig
+        btnconfig: modal
       }, () => {
         this.improveAction()
       })
@@ -1022,6 +1028,29 @@
     })
   }
 
+  handleModelConfig = (config) => {
+    let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
+    config.fields = config.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 || cell.blacklist.length === 0) return cell
+      if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
+        cell.hidden = 'true'
+      }
+
+      return cell
+    })
+    return config
+  }
+
   /**
    * @description 鑾峰彇鎸夐挳閰嶇疆淇℃伅
    */
@@ -1068,66 +1097,8 @@
           })
           this.updateStatus('over')
         } else {
-          let roleId = sessionStorage.getItem('role_id') || '' // 瑙掕壊ID
-          if (_LongParam.groups.length > 0) {
-            _LongParam.groups.forEach(group => {
-              group.sublist = group.sublist.map(cell => {
-                // 鏁版嵁婧恠ql璇彞锛岄澶勭悊
-                if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
-                  let _option = Utils.getSelectQueryOptions(cell)
-
-                  if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                    _option.sql = _option.sql.replace(/\$@/ig, '/*')
-                    _option.sql = _option.sql.replace(/@\$/ig, '*/')
-                  } else {
-                    _option.sql = _option.sql.replace(/@\$|\$@/ig, '')
-                  }
-                  // 澶栬仈鏁版嵁搴撴浛鎹�
-                  if (window.GLOB.externalDatabase !== null) {
-                    _option.sql = _option.sql.replace(/@db@/ig, window.GLOB.externalDatabase)
-                  }
-
-                  cell.data_sql = Utils.formatOptions(_option.sql)
-                  cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
-                  cell.arr_field = _option.field
-                }
-
-                // 瀛楁鏉冮檺榛戝悕鍗�
-                if (!cell.blacklist || cell.blacklist.length === 0) return cell
-                if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-                  cell.hidden = 'true'
-                }
-
-                return cell
-              })
-            })
-          } else {
-            _LongParam.fields = _LongParam.fields.map(cell => {
-              // 鏁版嵁婧恠ql璇彞锛岄澶勭悊
-              if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(cell.type) && cell.resourceType === '1') {
-                let _option = Utils.getSelectQueryOptions(cell)
-
-                if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-                  _option.sql = _option.sql.replace(/\$@/ig, '/*')
-                  _option.sql = _option.sql.replace(/@\$/ig, '*/')
-                } else {
-                  _option.sql = _option.sql.replace(/@\$|\$@/ig, '')
-                }
-
-                cell.data_sql = Utils.formatOptions(_option.sql)
-                cell.base_sql = window.btoa(window.encodeURIComponent(_option.sql))
-                cell.arr_field = _option.field
-              }
-
-              // 瀛楁鏉冮檺榛戝悕鍗�
-              if (!cell.blacklist || cell.blacklist.length === 0) return cell
-              if (cell.blacklist.filter(v => roleId.indexOf(v) > -1).length > 0) {
-                cell.hidden = 'true'
-              }
-
-              return cell
-            })
-          }
+          _LongParam = updateForm(_LongParam)
+          _LongParam = this.handleModelConfig(_LongParam)
 
           this.setState({
             btnconfig: _LongParam
@@ -1171,17 +1142,10 @@
     const { BData } = this.props
     const { btnconfig, tabledata } = this.state
     let _this = this
-    let _fields = []
+    let result = []
     
-    if (btnconfig.groups.length > 0) {
-      btnconfig.groups.forEach(group => {
-        _fields = [..._fields, ...group.sublist]
-      })
-    } else {
-      _fields = btnconfig.fields
-    }
-
-    let result = _fields.map(item => {
+    btnconfig.fields.forEach(item => {
+      if (!item.field) return
       let _readin = item.readin !== 'false'
       let _initval = item.initval
 
@@ -1208,14 +1172,18 @@
         _fieldlen = item.decimal ? item.decimal : 0
       }
 
-      return {
+      if (_initval === undefined) {
+        _initval = ''
+      }
+
+      result.push({
         key: item.field,
         readonly: item.readonly === 'true',
         readin: _readin,
         fieldlen: _fieldlen,
         type: item.type,
         value: _initval
-      }
+      })
     })
 
     confirm({
diff --git a/src/tabviews/zshare/actionList/tabbutton/index.jsx b/src/tabviews/zshare/actionList/tabbutton/index.jsx
index 9223ef1..28bf3f2 100644
--- a/src/tabviews/zshare/actionList/tabbutton/index.jsx
+++ b/src/tabviews/zshare/actionList/tabbutton/index.jsx
@@ -73,11 +73,9 @@
     }
 
     let primaryId = ''
-    let _data = null
 
     if (btn.Ot === 'requiredSgl') {
       primaryId = data[0][setting.primaryKey] || ''
-      _data = data[0]
     }
 
     let newtab = {}
@@ -117,8 +115,7 @@
         ...menu,
         selected: true,
         param: {
-          BID: primaryId,
-          data: _data
+          $BID: primaryId
         }
       }
     }
@@ -134,18 +131,22 @@
       return tab.MenuID !== newtab.MenuID
     })
 
-    if (tabviews.length !== tabs.length) {
-      this.props.modifyTabview(fromJS(tabs).toJS())
-    }
-
-    this.setState({}, () => {
-      if (MenuID) {
-        tabs.splice(index + 1, 0, newtab)
-      } else {
-        tabs.push(newtab)
+    if (['linkage_navigation', 'linkage'].includes(window.GLOB.navBar)) {
+      this.props.modifyTabview([newtab])
+    } else {
+      if (tabviews.length !== tabs.length) {
+        this.props.modifyTabview(fromJS(tabs).toJS())
       }
-      this.props.modifyTabview(tabs)
-    })
+  
+      this.setState({}, () => {
+        if (MenuID) {
+          tabs.splice(index + 1, 0, newtab)
+        } else {
+          tabs.push(newtab)
+        }
+        this.props.modifyTabview(tabs)
+      })
+    }
 
     MKEmitter.emit('openNewTab')
     if (window.GLOB.systemType === 'production') {
diff --git a/src/tabviews/zshare/cardcomponent/index.jsx b/src/tabviews/zshare/cardcomponent/index.jsx
index 4007fa6..ab96602 100644
--- a/src/tabviews/zshare/cardcomponent/index.jsx
+++ b/src/tabviews/zshare/cardcomponent/index.jsx
@@ -159,7 +159,7 @@
             selectedData={[data]}
           />
         )
-      } else if (item.OpenType === 'tab' || item.OpenType === 'blank') {
+      } else if (item.OpenType === 'tab') {
         return (
           <TabButton
             key={item.uuid}
diff --git a/src/tabviews/zshare/chartcomponent/index.jsx b/src/tabviews/zshare/chartcomponent/index.jsx
index 7aaa703..83628bc 100644
--- a/src/tabviews/zshare/chartcomponent/index.jsx
+++ b/src/tabviews/zshare/chartcomponent/index.jsx
@@ -25,7 +25,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, // 瀛楀吀
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS, // 瀛楀吀
     empty: true,               // 鍥捐〃鏁版嵁涓虹┖
     actions: [],               // 鍥捐〃缁戝畾鐨勬寜閽粍
     chartId: Utils.getuuid(),  // 鍥捐〃Id
diff --git a/src/tabviews/zshare/dategroup/quarterpicker/index.jsx b/src/tabviews/zshare/dategroup/quarterpicker/index.jsx
index 91dff77..d81e866 100644
--- a/src/tabviews/zshare/dategroup/quarterpicker/index.jsx
+++ b/src/tabviews/zshare/dategroup/quarterpicker/index.jsx
@@ -58,7 +58,7 @@
     } else if (quarter === 3) {
       _stime = year + '-07-01'
       _etime = year + '-09-30'
-    } else if (quarter === 2) {
+    } else if (quarter === 4) {
       _stime = year + '-10-01'
       _etime = year + '-12-31'
     }
diff --git a/src/tabviews/zshare/dategroup/quarterpicker/index.scss b/src/tabviews/zshare/dategroup/quarterpicker/index.scss
index 62fab02..921d781 100644
--- a/src/tabviews/zshare/dategroup/quarterpicker/index.scss
+++ b/src/tabviews/zshare/dategroup/quarterpicker/index.scss
@@ -16,13 +16,13 @@
       }
     }
   }
-  tr:hover {
+  tr:not(.ant-quarter-selected):hover {
     font-weight: bold;
     background: #e6f7ff;
   }
   tr.ant-quarter-selected {
     font-weight: bold;
-    background: #bae7ff!important;
+    background: #bae7ff;
   }
 }
 .quarter-picker-tooltip.bottomRight {
diff --git a/src/tabviews/zshare/fileupload/index.jsx b/src/tabviews/zshare/fileupload/index.jsx
index e8cf594..df0cf6f 100644
--- a/src/tabviews/zshare/fileupload/index.jsx
+++ b/src/tabviews/zshare/fileupload/index.jsx
@@ -68,8 +68,12 @@
   }
 
   onDelete = (msg) => {
-    let filelist = this.state.filelist.filter(v => !v.url && !v.response)
-
+    let filelist = this.state.filelist.map(item => {
+      if (!item.url && !item.response && !item.status) {
+        item.status = 'error'
+      }
+      return item
+    })
     this.setState({filelist, showprogress: false})
     this.props.onChange(filelist)
 
diff --git a/src/tabviews/zshare/fileupload/index.scss b/src/tabviews/zshare/fileupload/index.scss
index 00b4ba5..5841f7b 100644
--- a/src/tabviews/zshare/fileupload/index.scss
+++ b/src/tabviews/zshare/fileupload/index.scss
@@ -16,6 +16,14 @@
 }
 .fileupload-form-container.limit-fileupload {
   > .ant-upload {
-    display: none;
+    display: inline;
+    >.ant-upload {
+      >input {
+        display: none;
+      }
+      >button {
+        display: none;
+      }
+    }
   }
 }
\ No newline at end of file
diff --git a/src/tabviews/zshare/mutilform/checkCard/index.jsx b/src/tabviews/zshare/mutilform/checkCard/index.jsx
index 8f45c60..c8ceec6 100644
--- a/src/tabviews/zshare/mutilform/checkCard/index.jsx
+++ b/src/tabviews/zshare/mutilform/checkCard/index.jsx
@@ -67,7 +67,12 @@
       paddingTop = '56.25%'
     }
 
-    if (display !== 'picture') {
+    if (!options || options.length === 0) {
+      return null
+    } else if (display !== 'picture') {
+      if (!fields || fields.length === 0) {
+        return null
+      }
       return options.map(item => {
         let _active = false
         if (multiple === 'true' && selectKeys.includes(item.$value)) {
diff --git a/src/tabviews/zshare/mutilform/customTextArea/index.jsx b/src/tabviews/zshare/mutilform/customTextArea/index.jsx
index caed9bf..457e5f2 100644
--- a/src/tabviews/zshare/mutilform/customTextArea/index.jsx
+++ b/src/tabviews/zshare/mutilform/customTextArea/index.jsx
@@ -14,19 +14,19 @@
 
   state = {
     value: '',
-    encryption: 'false'
+    encryption: false
   }
 
   UNSAFE_componentWillMount () {
     let value = ''
-    let encryption = 'false'
+    let encryption = false
 
     if (this.props['data-__meta']) {
       value = this.props['data-__meta'].initialValue || ''
     }
 
     if (this.props.Item && this.props.Item.encryption === 'true') {
-      encryption = 'true'
+      encryption = true
       if (value) {
         try {
           value = window.decodeURIComponent(window.atob(value))
@@ -42,6 +42,22 @@
     })
   }
 
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    const { value, encryption } = this.state
+
+    if (!encryption && value !== nextProps.value) {
+      this.setState({ value: nextProps.value || '' })
+    } else if (encryption && window.btoa(window.encodeURIComponent(value)) !== nextProps.value) {
+      let _value = nextProps.value || ''
+      try {
+        _value = window.decodeURIComponent(window.atob(_value))
+      } catch {
+        _value = nextProps.value || ''
+      }
+      this.setState({ value: _value })
+    }
+  }
+
   onChange = (e) => {
     const { encryption } = this.state
     let val = e.target.value
@@ -49,7 +65,7 @@
     this.setState({ value: val })
 
     let _val = val
-    if (encryption === 'true') {
+    if (encryption) {
       try {
         _val = window.btoa(window.encodeURIComponent(_val))
       } catch {
diff --git a/src/tabviews/zshare/mutilform/index.jsx b/src/tabviews/zshare/mutilform/index.jsx
index 2c79f04..1efef2a 100644
--- a/src/tabviews/zshare/mutilform/index.jsx
+++ b/src/tabviews/zshare/mutilform/index.jsx
@@ -33,7 +33,6 @@
   }
 
   state = {
-    cols: 2,         // 鏄剧ず涓哄灏戝垪
     datatype: null,  // 鏁版嵁绫诲瀷
     readtype: null,  // 鏄惁鍙
     readin: null,    // 琛屾暟鎹槸鍚﹀啓鍏�
@@ -44,54 +43,21 @@
     record: {}       // 璁板綍涓嬫媺琛ㄥ崟鍏宠仈瀛楁锛岀敤浜庢暟鎹啓鍏�
   }
 
-  UNSAFE_componentWillMount () {
-    let cols = 2
-    if (this.props.action.setting && this.props.action.setting.cols) {
-      cols = parseInt(this.props.action.setting.cols)
-      if (cols > 4 || cols < 1) {
-        cols = 2
-      }
-    }
-
-    this.setState({
-      cols: cols
-    })
-  }
-
   componentDidMount () {
-    const { data, BData } = this.props
-    let action = fromJS(this.props.action).toJS()
-    
+    const { data, BData, action } = this.props
+
     let datatype = {}
     let readtype = {}
     let readin = {}
     let writein = {}
     let fieldlen = {}
-    let formlist = []
     let intercepts = []
     let _inputfields = []
-
-    if (action.groups.length > 0) {
-      action.groups.forEach(group => {
-        if (group.sublist.length === 0) return
-
-        if (!group.default) {
-          formlist.push({
-            type: 'title',
-            label: group.label,
-            uuid: group.uuid
-          })
-        }
-
-        formlist.push(...group.sublist)
-      })
-    } else {
-      formlist = action.fields
-    }
-
     let linkFields = {} // 鍏宠仈鑿滃崟
     let supItemVal = {} // 涓婄骇鑿滃崟鍒濆鍊�
     let deForms = []    // 闇�瑕佸姩鎬佽幏鍙栦笅鎷夎彍鍗曠殑琛ㄥ崟
+
+    let formlist = fromJS(action.fields).toJS()
 
     formlist.forEach(item => {
       if (item.type === 'text' || item.type === 'number') {              // 鐢ㄤ簬杩囨护涓嬫媺鑿滃崟鍏宠仈琛ㄥ崟
@@ -108,7 +74,11 @@
     })
 
     formlist = formlist.map(item => {
-      if (item.type === 'title') return item
+      if (item.labelwidth) {
+        item.labelCol = {style: {width: item.labelwidth + '%'}}
+        item.wrapperCol = {style: {width: (100 - item.labelwidth) + '%'}}
+      }
+      if (item.type === 'split' || item.type === 'hint') return item
 
       // 鏁版嵁鑷姩濉厖
       let _readin = item.readin !== 'false'
@@ -153,11 +123,11 @@
 
       if (item.type === 'linkMain') {
         newval = BData && BData[item.field] ? BData[item.field] : ''
-      } else if (_readin && !/^date/.test(item.type) && this.props.data && this.props.data.hasOwnProperty(item.field)) {
-        newval = this.props.data[item.field]
+      } else if (_readin && !/^date/.test(item.type) && data && data.hasOwnProperty(item.field)) {
+        newval = data[item.field]
       } else if (item.type === 'date') { // 鏃堕棿鎼滅储
-        if (_readin && this.props.data && this.props.data.hasOwnProperty(item.field)) {
-          newval = this.props.data[item.field]
+        if (_readin && data && data.hasOwnProperty(item.field)) {
+          newval = data[item.field]
         }
         if (newval) {
           newval = moment(newval, 'YYYY-MM-DD')
@@ -169,8 +139,8 @@
           newval = null
         }
       } else if (item.type === 'datemonth') {
-        if (_readin && this.props.data && this.props.data.hasOwnProperty(item.field)) {
-          newval = this.props.data[item.field]
+        if (_readin && data && data.hasOwnProperty(item.field)) {
+          newval = data[item.field]
         }
         if (newval) {
           newval = moment(newval, 'YYYY-MM')
@@ -182,8 +152,8 @@
           newval = null
         }
       } else if (item.type === 'datetime') {
-        if (_readin && this.props.data && this.props.data.hasOwnProperty(item.field)) {
-          newval = this.props.data[item.field]
+        if (_readin && data && data.hasOwnProperty(item.field)) {
+          newval = data[item.field]
         }
         if (newval) {
           newval = moment(newval, 'YYYY-MM-DD HH:mm:ss')
@@ -229,7 +199,7 @@
       if (['select', 'link', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type) && item.resourceType === '1') {
         deForms.push(item)
       } else if (['select', 'link', 'radio'].includes(item.type) && item.resourceType !== '1') { // 閫変腑绗竴椤�
-        if (item.initval && item.initval.indexOf('$first') > -1) {
+        if (typeof(item.initval) === 'string' && item.initval.indexOf('$first') > -1) {
           item.initval = item.options[0] ? item.options[0].Value : ''
         }
       }
@@ -262,7 +232,9 @@
       formlist
     }, () => {
       if (action.setting && action.setting.focus) {
-        this.selectInput(action.setting.focus, 'init')
+        setTimeout(() => {
+          this.selectInput(action.setting.focus)
+        }, 500)
       }
       // 鐢ㄦ潵鏇存柊state锛岄槻姝㈠彈鎺ц〃鍗曞垵濮嬫椂涓嶆樉绀�
       this.setState({
@@ -272,7 +244,7 @@
     })
   }
 
-  selectInput = (selectId, type) => {
+  selectInput = (selectId) => {
     try {
       let _form = document.getElementById('main-form-box')
       let _inputs = _form.getElementsByTagName('input')
@@ -280,7 +252,7 @@
       _inputs.forEach(input => {
         if (!input || input.id !== selectId) return
 
-        if (input.className === 'ant-select-search__field' && type !== 'init') {
+        if (input.className === 'ant-select-search__field') {
           let div = input.parentNode
           while (div && div.parentNode) {
             div = div.parentNode
@@ -462,7 +434,7 @@
           } else if (['select', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type)) {
             item.options = item.oriOptions
           }
-          if (['select', 'link', 'radio'].includes(item.type) && item.initval && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
+          if (['select', 'link', 'radio'].includes(item.type) && typeof(item.initval) === 'string' && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
             item.initval = item.options[0] ? item.options[0].Value : ''
             values.push({field: item.field, value: item.initval})
           }
@@ -569,7 +541,7 @@
           } else if (['select', 'multiselect', 'radio', 'checkbox', 'checkcard'].includes(item.type)) {
             item.options = item.oriOptions
           }
-          if (['select', 'link', 'radio'].includes(item.type) && item.initval && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
+          if (['select', 'link', 'radio'].includes(item.type) && typeof(item.initval) === 'string' && item.initval.indexOf('$first') > -1) { // 閫変腑绗竴椤�
             item.initval = item.options[0] ? item.options[0].Value : ''
             values.push({field: item.field, value: item.initval})
           }
@@ -640,7 +612,7 @@
     // 琛ㄥ崟鍒囨崲鏃讹紝鏇存柊鍏宠仈瀛楁
     if (_field.linkSubField) {
       let _data = _field.options.filter(op => op.Value === value)[0]
-
+      
       if (_data) {
         _field.linkSubField.forEach(subfield => {
           if (this.props.form.getFieldValue(subfield) !== undefined) {
@@ -716,13 +688,13 @@
 
   getFields() {
     const { getFieldDecorator } = this.props.form
-    const { cols, formlist } = this.state
+    const { formlist } = this.state
 
     const fields = []
     let filtration = {}
 
     formlist.forEach((item, index) => {
-      if ((!item.field && item.type !== 'title' && item.type !== 'hint') || item.hidden === 'true' || item.type === 'funcvar') return
+      if ((!item.field && item.type !== 'split' && item.type !== 'hint') || item.hidden === 'true' || item.type === 'funcvar') return
       if (item.supField) { // 澶氬眰琛ㄥ崟鎺у埗
         let _supVal = this.props.form.getFieldValue(item.supField)
 
@@ -738,21 +710,22 @@
         }
       }
 
-      let _colspan = 24 / cols
-      if (item.entireLine === 'true') {
-        _colspan = 24
-      }
-
-      if (item.type === 'title') {
+      if (item.type === 'split') {
         fields.push(
           <Col span={24} key={index}>
-            <p>{item.label}</p>
+            <p className="mk-form-split-line">{item.label}</p>
           </Col>
         )
       } else if (item.type === 'hint') {
         fields.push(
-          <Col span={24} key={index}>
-            <Form.Item colon={!!item.label} label={item.label || ' '} className="hint">
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              colon={!!item.label}
+              label={item.label}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              className="hint"
+            >
               <div className="message">{item.message}</div>
             </Form.Item>
           </Col>
@@ -789,13 +762,18 @@
         }
 
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval + '',
                 rules: [
@@ -817,13 +795,18 @@
         let precision = (item.decimal || item.decimal === 0) ? item.decimal : null
 
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -845,13 +828,18 @@
         )
       } else if (item.type === 'color') { // 棰滆壊閫夋嫨
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval || 'transparent',
                 rules: [
@@ -868,13 +856,19 @@
         )
       } else if (item.type === 'checkcard') { // 澶氶�夋
         fields.push(
-          <Col span={24} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            } className="checkcard">
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              className="checkcard"
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -889,13 +883,18 @@
         )
       } else if (item.type === 'switch') { // 澶氶�夋
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -912,13 +911,18 @@
         let _initval = item.initval ? item.initval.split(',').filter(Boolean) : []
         
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: _initval,
                 rules: [
@@ -937,13 +941,18 @@
         )
       } else if (item.type === 'radio') { // 鍗曢�夋
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -962,13 +971,18 @@
         )
       } else if (item.type === 'select' || item.type === 'link') { // 涓嬫媺鎼滅储
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -996,13 +1010,18 @@
       } else if (item.type === 'multiselect') { // 澶氶��
         let _initval = item.initval ? item.initval.split(',').filter(Boolean) : []
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: _initval,
                 rules: [
@@ -1028,13 +1047,18 @@
         )
       } else if (item.type === 'date') { // 鏃堕棿鎼滅储
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -1051,13 +1075,18 @@
         )
       } else if (item.type === 'datemonth') {
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -1074,13 +1103,18 @@
         )
       } else if (item.type === 'datetime') {
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -1116,13 +1150,18 @@
         }
 
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: filelist,
                 rules: [
@@ -1139,13 +1178,18 @@
         )
       } else if (item.type === 'linkMain') {
         fields.push(
-          <Col span={_colspan} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -1170,13 +1214,18 @@
           }]
         }
         fields.push(
-          <Col span={24} key={index}>
-            <Form.Item label={item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : item.label
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : item.label
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval,
                 rules: [
@@ -1198,13 +1247,18 @@
         let _max = item.fieldlength || 512
 
         fields.push(
-          <Col span={24} key={index}>
-            <Form.Item label={item.hidelabel !== 'true' && item.tooltip ?
-              <Tooltip placement="topLeft" title={item.tooltip}>
-                <Icon type="question-circle" />
-                {item.label}
-              </Tooltip> : (item.hidelabel !== 'true' ? item.label : '')
-            }>
+          <Col span={item.span || 24} key={index}>
+            <Form.Item
+              extra={item.extra || null}
+              labelCol={item.labelCol}
+              wrapperCol={item.wrapperCol}
+              label={item.hidelabel !== 'true' && item.tooltip ?
+                <Tooltip placement="topLeft" title={item.tooltip}>
+                  <Icon type="question-circle" />
+                  {item.label}
+                </Tooltip> : (item.hidelabel !== 'true' ? item.label : '')
+              }
+            >
               {getFieldDecorator(item.field, {
                 initialValue: item.initval || '',
                 rules: [
@@ -1389,21 +1443,16 @@
   }
 
   render() {
-    const { cols } = this.state
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
+    const { action } = this.props
+
+    let _align = 'left_right'
+    if (action.setting && action.setting.align) {
+      _align = action.setting.align
     }
 
     return (
-      <Form {...formItemLayout} className="ant-advanced-search-form main-form-field" id="main-form-box">
-        <Row className={'cols' + cols} gutter={24}>{this.getFields()}</Row>
+      <Form className={'main-form-field ' + _align} id="main-form-box">
+        <Row gutter={24}>{this.getFields()}</Row>
       </Form>
     )
   }
diff --git a/src/tabviews/zshare/mutilform/index.scss b/src/tabviews/zshare/mutilform/index.scss
index 384c310..829d2a9 100644
--- a/src/tabviews/zshare/mutilform/index.scss
+++ b/src/tabviews/zshare/mutilform/index.scss
@@ -1,10 +1,12 @@
-.ant-advanced-search-form.main-form-field {
+.main-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;
@@ -44,6 +46,7 @@
     display: inline-block;
     text-overflow: ellipsis;
     white-space: nowrap;
+    width: 33.3%;
   }
   >.ant-row.cols2 .ant-col-24 {
     .ant-form-item {
@@ -88,7 +91,9 @@
   }
   p {
     color: #1890ff;
-    border-bottom: 1px solid #d9d9d9;
+    font-size: 15px;
+    padding-left: 10px;
+    border-bottom: 1px solid #e9e9e9;
   }
   .ant-input-disabled {
     color: rgba(0, 0, 0, 0.65)!important;
@@ -104,4 +109,17 @@
       color: rgba(0, 0, 0, 0.65)!important;
     }
   }
+}
+
+.main-form-field.up_down {
+  .ant-form-item {
+    display: block;
+    .ant-form-item-label {
+      width: 100%!important;
+      text-align: left;
+    }
+    .ant-form-item-control-wrapper {
+      width: 100%!important;
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/tabviews/zshare/normalTable/index.jsx b/src/tabviews/zshare/normalTable/index.jsx
index 77e74d5..3746355 100644
--- a/src/tabviews/zshare/normalTable/index.jsx
+++ b/src/tabviews/zshare/normalTable/index.jsx
@@ -211,62 +211,81 @@
 
   // 瀛楁閫忚
   triggerLink = (e, item, record) => {
-    const { tabviews, MenuID, setting } = this.props
+    const { tabviews, MenuID } = this.props
 
     e.stopPropagation()
+
+    let __param = {
+      $searchkey: item.field,
+      $searchval: record[item.field] || '',
+      $BID: record.$$uuid
+    }
+
+    if (item.linkfields && item.linkfields.length > 0) {
+      item.linkfields.forEach(field => {
+        __param[field] = record[field] || ''
+      })
+    }
 
     if (item.linkThdMenu) {
       let tabmenu = item.linkThdMenu
 
-      tabmenu.param = {
-        searchkey: item.field,
-        searchval: record[item.field] || '',
-        BID: record[setting.primaryKey]
-      }
-  
+      tabmenu.param = __param
       tabmenu.selected = true
   
       let index = 0
-      let isexit = false
-      let tabs = tabviews.map((tab, i) => {
+      let tabs = tabviews.filter((tab, i) => {
         tab.selected = false
   
         if (tab.MenuID === MenuID) {
           index = i
-        } else if (tab.MenuID === tabmenu.MenuID) {
-          tab.param = tabmenu.param
-          tab.selected = true
-          isexit = true
         }
   
-        return tab
+        return tab.MenuID !== tabmenu.MenuID
       })
   
-      if (!isexit) {
-        tabs.splice(index + 1, 0, tabmenu)
+      if (tabviews.length > tabs.length) {
+        this.props.modifyTabview(fromJS(tabs).toJS())
       }
   
-      this.props.modifyTabview(tabs)
-    } else {
+      this.setState({}, () => {
+        if (MenuID) {
+          tabs.splice(index + 1, 0, tabmenu)
+        } else {
+          tabs.push(tabmenu)
+        }
+        this.props.modifyTabview(tabs)
+      })
+    } else if (item.linkurl) {
       let src = item.linkurl
 
-      if (item.linkurl.indexOf('paramsmain/') > -1) {
+      if (src.indexOf('paramsmain/') > -1) {
         try {
           let _url = item.linkurl.split('paramsmain/')[0] + 'paramsmain/'
           let _param = JSON.parse(window.decodeURIComponent(window.atob(item.linkurl.split('paramsmain/')[1])))
-          let dataparam = {
-            searchkey: item.field,
-            searchval: record[item.field] || '',
-            BID: record[setting.primaryKey]
-          }
           _param.UserID = sessionStorage.getItem('UserID')
           _param.LoginUID = sessionStorage.getItem('LoginUID')
           _param.User_Name = sessionStorage.getItem('User_Name')
-          _param.param = dataparam
+          _param.param = __param
           src = _url + window.btoa(window.encodeURIComponent(JSON.stringify(_param)))
         } catch {
           console.warn('鑿滃崟鍙傛暟瑙f瀽閿欒锛�')
         }
+      } else {
+        let con = '?'
+
+        if (/\?/ig.test(src)) {
+          con = '&'
+        }
+
+        if (item.linkfields && item.linkfields.length > 0) {
+          item.linkfields.forEach(field => {
+            if (field.toLowerCase() === 'id') return
+            con += `${field}=${record[field] || ''}&`
+          })
+        }
+        
+        src = src + `${con}id=${record.$$uuid}&appkey=${window.GLOB.appkey}&userid=${sessionStorage.getItem('UserID')}&LoginUID=${sessionStorage.getItem('LoginUID') || ''}`
       }
 
       window.open(src)
@@ -636,7 +655,7 @@
                   setting={this.props.setting}
                 />
               )
-            } else if (btn.OpenType === 'tab' || btn.OpenType === 'blank') {
+            } else if (btn.OpenType === 'tab') {
               return (
                 <TabButton
                   key={btn.uuid}
@@ -922,7 +941,7 @@
       index = selectedRowKeys.slice(-1)[0]
     }
 
-    if (setting.tableType === 'checkbox') {
+    if (setting.tableType === 'checkbox' || setting.tableType === 'radio') {
       _activeIndex = index === '' ? null : index
     }
 
@@ -946,7 +965,7 @@
     if (this.props.setting.tableType === 'radio') {
       newkeys = [index]
       this.changedata(index)
-      this.setState({ selectedRowKeys: newkeys })
+      this.setState({ selectedRowKeys: newkeys, activeIndex: index })
     } else {
       let _index = ''
       if (newkeys.includes(index)) {
@@ -1155,7 +1174,7 @@
           dataSource={_data}
           rowClassName={(record) => {
             let className = ''
-            if (setting.tableType === 'checkbox' && record.key === activeIndex) {
+            if ((setting.tableType === 'checkbox' || setting.tableType === 'radio') && record.key === activeIndex) {
               className = 'mk-row-active '
             }
 
diff --git a/src/tabviews/zshare/normalTable/index.scss b/src/tabviews/zshare/normalTable/index.scss
index 3d4d957..5c27e47 100644
--- a/src/tabviews/zshare/normalTable/index.scss
+++ b/src/tabviews/zshare/normalTable/index.scss
@@ -54,6 +54,19 @@
     table {
       .ant-table-tbody > tr > td {
         vertical-align: top;
+        position: relative;
+
+        .baseboard {
+          position: absolute;
+          top: 0px;
+          left: 0px;
+          right: 0px;
+          bottom: 0px;
+        }
+        .link-menu {
+          cursor: pointer;
+        }
+
         .content {
           position: relative;
           z-index: 1;
@@ -112,6 +125,14 @@
               width: calc(50% - 5px);
             }
           }
+
+          p {
+            margin-bottom: 2px;
+          }
+          span {
+            display: inline-block;
+            margin-right: 5px;
+          }
         }
         .picture-col {
           img {
@@ -153,35 +174,7 @@
       .ant-table-tbody > tr > td[rowspan] {
         vertical-align: middle;
       }
-      .ant-table-tbody > tr > td.ant-table-column-has-actions {
-        position: relative;
-        .baseboard {
-          position: absolute;
-          top: 0px;
-          left: 0px;
-          right: 0px;
-          bottom: 0px;
-        }
-        .link-menu {
-          cursor: pointer;
-        }
-        
-        .content {
-          position: relative;
-          z-index: 1;
-          word-wrap: break-word;
-          word-break: break-word;
-        }
-      }
-      .ant-table-tbody > tr > td .content {
-        p {
-          margin-bottom: 2px;
-        }
-        span {
-          display: inline-block;
-          margin-right: 5px;
-        }
-      }
+
       .ant-table-tbody > tr > td .button {
         .ant-btn {
           margin-bottom: 10px;
diff --git a/src/tabviews/zshare/settingcomponent/editTable/index.jsx b/src/tabviews/zshare/settingcomponent/editTable/index.jsx
index 214f0ad..f12eace 100644
--- a/src/tabviews/zshare/settingcomponent/editTable/index.jsx
+++ b/src/tabviews/zshare/settingcomponent/editTable/index.jsx
@@ -7,7 +7,7 @@
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
-let eTDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+let eTDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 const EditableContext = React.createContext()
 const shortkeycode = {
   65: 'A', 66: 'B', 67: 'C', 68: 'D', 69: 'E', 70: 'F', 71: 'G', 72: 'H', 73: 'I', 74: 'J', 75: 'K', 76: 'L', 77: 'M',
@@ -21,7 +21,7 @@
     if (inputType === 'select') {
       let _options = []
       if (record.$port) {
-        _options = window.GLOB.UserCacheMap.get(record.$port)
+        _options = window.GLOB.UserCacheMap.get(record.$port) || []
       }
       return (
         <Select allowClear>
diff --git a/src/tabviews/zshare/topSearch/index.jsx b/src/tabviews/zshare/topSearch/index.jsx
index 2b5ffe7..79a77e4 100644
--- a/src/tabviews/zshare/topSearch/index.jsx
+++ b/src/tabviews/zshare/topSearch/index.jsx
@@ -95,13 +95,6 @@
         if (item.resourceType === '1' && item.dataSource) {
           let _option = Utils.getSelectQueryOptions(item)
 
-          if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
-            _option.sql = _option.sql.replace(/\$@/ig, '/*')
-            _option.sql = _option.sql.replace(/@\$/ig, '*/')
-          } else {
-            _option.sql = _option.sql.replace(/@\$|\$@/ig, '')
-          }
-
           // 娴嬭瘯绯荤粺鍗曚釜璇锋眰
           if (menuType !== 'HS' && options.sysType === 'local' && !window.GLOB.systemType) {
             deForms.push({
diff --git a/src/templates/calendarconfig/calcomponent/index.jsx b/src/templates/calendarconfig/calcomponent/index.jsx
index 935fa1d..d295d36 100644
--- a/src/templates/calendarconfig/calcomponent/index.jsx
+++ b/src/templates/calendarconfig/calcomponent/index.jsx
@@ -16,7 +16,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     calendar: null,  // 鏃ュ巻璁剧疆
     visible: false,  // 妯℃�佹鎺у埗
   }
diff --git a/src/templates/calendarconfig/index.jsx b/src/templates/calendarconfig/index.jsx
index 506fe3d..10dec96 100644
--- a/src/templates/calendarconfig/index.jsx
+++ b/src/templates/calendarconfig/index.jsx
@@ -23,6 +23,7 @@
 const { Panel } = Collapse
 const { confirm } = Modal
 
+const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
 const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
 const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/settingcalcomponent'))
 const TabComponent = asyncComponent(() => import('./tabcomponent'))
@@ -39,10 +40,9 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
     config: null,            // 椤甸潰閰嶇疆
     visible: false,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪锛屾ā鎬佹鏄剧ず鎺у埗
-    tableFields: [],         // 宸查�夎〃瀛楁闆�
     fields: null,            // 鎼滅储鏉′欢鍙婃樉绀哄垪锛屽彲閫夊瓧娈�
     formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
     menuloading: false,      // 鑿滃崟淇濆瓨涓�
@@ -710,18 +710,6 @@
       config: config
     })
   }
-  
-  /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
-
-    this.setState({
-      config: config,
-      tableFields: fields ? fields : tableFields
-    })
-  }
 
   // 骞村垏鎹㈡椂閲嶆柊鐢熸垚鏁版嵁
   changeDate = (year) => {
@@ -749,11 +737,15 @@
                   dict={this.state.dict}
                   updatemenu={this.updateconfig}
                 />
+                {config ? <UrlFieldComponent
+                  config={config}
+                  updateConfig={this.updateconfig}
+                /> : null}
                 {/* 琛ㄥ悕娣诲姞 */}
                 <TableComponent
                   config={config}
                   containerId="subtable-basedata"
-                  updatetable={this.updatetable}
+                  updatetable={this.updateconfig}
                 />
               </Panel>
               {/* 鎼滅储鏉′欢娣诲姞 */}
@@ -766,7 +758,6 @@
                 <FieldsComponent
                   config={config}
                   type="search"
-                  tableFields={this.state.tableFields}
                   updatefield={this.updateconfig}
                 />
               </Panel>
@@ -789,7 +780,6 @@
               <SettingComponent
                 config={config}
                 MenuID={menu.MenuID}
-                tableFields={this.state.tableFields}
                 updateConfig={this.updateconfig}
               />
               <SearchComponent
diff --git a/src/templates/calendarconfig/source.jsx b/src/templates/calendarconfig/source.jsx
index f078981..061fc0c 100644
--- a/src/templates/calendarconfig/source.jsx
+++ b/src/templates/calendarconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class CalendarBaseData {
   baseConfig = {
diff --git a/src/templates/calendarconfig/tabcomponent/index.jsx b/src/templates/calendarconfig/tabcomponent/index.jsx
index 700b5d4..68ee2df 100644
--- a/src/templates/calendarconfig/tabcomponent/index.jsx
+++ b/src/templates/calendarconfig/tabcomponent/index.jsx
@@ -20,7 +20,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     menu: null,          // 鑿滃崟淇℃伅
     formlist: null,      // 琛ㄥ崟淇℃伅
     visible: false       // 妯℃�佹鎺у埗
diff --git a/src/templates/comtableconfig/index.jsx b/src/templates/comtableconfig/index.jsx
index 2c00e6f..2b88631 100644
--- a/src/templates/comtableconfig/index.jsx
+++ b/src/templates/comtableconfig/index.jsx
@@ -25,6 +25,7 @@
 
 const { Panel } = Collapse
 const { confirm } = Modal
+const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
 const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
 const SettingComponent = asyncComponent(() => import('@/templates/sharecomponent/settingcomponent'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
@@ -42,9 +43,8 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     config: null,            // 椤甸潰閰嶇疆
-    tableFields: [],         // 宸查�夌殑甯哥敤琛�
     formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
     menuloading: false,      // 鑿滃崟淇濆瓨涓�
     menucloseloading: false, // 鑿滃崟鍏抽棴鏃讹紝閫夋嫨淇濆瓨
@@ -101,17 +101,17 @@
           item.linkTab = ''
         }
 
-        if (item.OpenType === 'pop') { // 鍚湁瀛愰厤缃」鐨勬寜閽�(琛ㄥ崟)
+        if (item.OpenType === 'pop' || item.execMode === 'pop') { // 鍚湁瀛愰厤缃」鐨勬寜閽�(琛ㄥ崟)
           _oriActions.push({
             prebtn: fromJS(item).toJS(),
             curuuid: uuid,
             Template: 'Modal'
           })
-        } else if (item.OpenType === 'tab' || item.OpenType === 'blank') { // 鍚湁瀛愰厤缃」鐨勬寜閽�(鏍囩鍚庡綋鍓嶉〉鎵撳紑)
+        } else if (item.OpenType === 'tab' && item.tabTemplate === 'FormTab') { // 鍚湁瀛愰厤缃」鐨勬寜閽�(鏍囩椤垫墦寮�)
           _oriActions.push({
             prebtn: fromJS(item).toJS(),
             curuuid: uuid,
-            Template: item.tabTemplate
+            Template: 'FormTab'
           })
         }
 
@@ -616,6 +616,8 @@
         let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 鏌ョ湅鍒濆鍖栨寜閽槸鍚﹀瓨鍦�
         if (!curBtn) return
         if (curBtn.OpenType !== item.prebtn.OpenType) return
+        if (curBtn.OpenType === 'tab' && curBtn.tabTemplate !== 'FormTab') return
+        if (curBtn.OpenType === 'funcbutton' && curBtn.execMode !== 'pop') return
 
         oriActions.push({
           prebtn: item.prebtn,
@@ -775,16 +777,6 @@
         duration: 5
       })
     } else {
-      // 鍩烘湰淇℃伅楠岃瘉
-      if (!config.fstMenuId || !config.ParentId || !config.MenuName || !config.MenuNo) {
-        notification.warning({
-          top: 92,
-          message: this.state.dict['model.menu.basemsg'],
-          duration: 5
-        })
-        this.setState({activeKey: '0'})
-        return
-      }
       if (!is(fromJS(originMenu), fromJS(config))) { // 鑿滃崟淇℃伅鍙樺寲鏃讹紝鎻愮ず淇濆瓨
         notification.warning({
           top: 92,
@@ -794,7 +786,7 @@
         return
       }
 
-      let submenu = menu.fstMenuList.filter(item => item.MenuID === config.fstMenuId)[0]
+      let submenu = menu.fstMenuList.filter(_menu => _menu.MenuID === config.fstMenuId)[0]
 
       let _Menu = {
         ...menu,
@@ -813,17 +805,17 @@
       let isbutton = true
       let _btnTab = null
       
-      if (type === 'button' && item.OpenType === 'pop') {
-        _view = 'Modal'             // 琛ㄥ崟椤甸潰
-      } else if (type === 'button' && (item.OpenType === 'tab' || item.OpenType === 'blank')) {
-        _view = item.tabTemplate    // 鏂版爣绛鹃〉妯℃澘
+      if (type === 'button' && (item.OpenType === 'pop' || item.execMode === 'pop')) {
+        _view = 'Modal'      // 琛ㄥ崟椤甸潰
+      } else if (type === 'button' && item.OpenType === 'tab') {
+        _view = 'FormTab'    // 琛ㄥ崟鏍囩椤垫ā鏉�
         _btnTab = item
       } else if (type === 'button' && item.OpenType === 'popview') {
-        _view = item.tabType || 'SubTable' // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
+        _view = 'SubTable'   // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
         uuid = item.linkTab
         isbutton = false
       } else if (type === 'tab') {
-        _view = item.type           // 鏍囩妯℃澘
+        _view = 'SubTable'   // 鏍囩妯℃澘
         uuid = item.linkTab
         isbutton = false
       }
@@ -1082,28 +1074,6 @@
   }
   
   /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
-
-    this.setState({
-      config: config,
-      tableFields: fields ? fields : tableFields
-    })
-  }
-
-  /**
-   * @description 鏇存柊鏍囩閰嶇疆淇℃伅
-   */
-  updatetabs = (config) => {
-
-    this.setState({
-      config: config
-    })
-  }
-
-  /**
    * @description 鏇存柊閰嶇疆淇℃伅
    */
   updateconfig = (config) => {
@@ -1116,7 +1086,7 @@
     const { menu } = this.props
     const { activeKey, config, chartview } = this.state
 
-    const confActions = config.action.filter(_action => !_action.origin && (['pop', 'popview', 'blank'].includes(_action.OpenType) || (_action.OpenType === 'tab' && _action.tabTemplate === 'FormTab')))
+    const confActions = config.action.filter(_action => !_action.origin && (['pop', 'popview'].includes(_action.OpenType) || (_action.OpenType === 'tab' && _action.tabTemplate === 'FormTab') || (_action.OpenType === 'funcbutton' && _action.execMode === 'pop')))
 
     let configTabs = []
     config.tabgroups.forEach(group => {
@@ -1138,11 +1108,15 @@
                   dict={this.state.dict}
                   updatemenu={this.updateconfig}
                 />
+                {config ? <UrlFieldComponent
+                  config={config}
+                  updateConfig={this.updateconfig}
+                /> : null}
                 {/* 琛ㄥ悕娣诲姞 */}
                 <TableComponent
                   config={config}
                   containerId="main-basedata"
-                  updatetable={this.updatetable}
+                  updatetable={this.updateconfig}
                 />
               </Panel>
               {/* 鎼滅储鏉′欢娣诲姞 */}
@@ -1153,7 +1127,6 @@
                 <FieldsComponent
                   config={config}
                   type="search"
-                  tableFields={this.state.tableFields}
                   updatefield={this.updateconfig}
                 />
               </Panel>
@@ -1193,7 +1166,6 @@
                 <FieldsComponent
                   config={config}
                   type="columns"
-                  tableFields={this.state.tableFields}
                   updatefield={this.updateconfig}
                 />
               </Panel>
@@ -1304,7 +1276,7 @@
                 config={config}
                 tabs={this.state.tabviews}
                 setSubConfig={(item) => this.setSubConfig(item, 'tab')}
-                updatetabs={this.updatetabs}
+                updatetabs={this.updateconfig}
               />
             </Card>
           </div>
diff --git a/src/templates/comtableconfig/source.jsx b/src/templates/comtableconfig/source.jsx
index 4c2c87e..6a33c64 100644
--- a/src/templates/comtableconfig/source.jsx
+++ b/src/templates/comtableconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class CommonTableBaseData {
   baseConfig = {
diff --git a/src/templates/formtabconfig/index.jsx b/src/templates/formtabconfig/index.jsx
index abd4a79..47db6ac 100644
--- a/src/templates/formtabconfig/index.jsx
+++ b/src/templates/formtabconfig/index.jsx
@@ -10,8 +10,7 @@
 import Api from '@/api'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
-import Utils from '@/utils/utils.js'
-import DevUtils from '@/utils/devutils.js'
+import Utils, { FuncUtils } from '@/utils/utils.js'
 import { getModalForm, getActionForm } from '@/templates/zshare/formconfig'
 import { queryTableSql } from '@/utils/option.js'
 
@@ -44,7 +43,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
     config: null,            // 椤甸潰閰嶇疆
     modaltype: '',           // 妯℃�佹绫诲瀷锛屾帶鍒舵ā鎬佹鏄剧ず
     tableVisible: false,     // 鏁版嵁琛ㄥ瓧娈垫ā鎬佹
@@ -731,8 +730,8 @@
         menuNo: menu.MenuNo
       }
 
-      newLText = Utils.formatOptions(DevUtils.getfunc(_param, btn, menu, _config))
-      DelText = Utils.formatOptions(DevUtils.dropfunc(_param.funcName))
+      newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
+      DelText = Utils.formatOptions(FuncUtils.dropfunc(_param.funcName))
 
       this.refs.btnCreatFunc.exec(btn.innerFunc, newLText, DelText)
     })
@@ -771,8 +770,8 @@
       }
 
       let _config = {...config, setting: setting}
-      let newLText = Utils.formatOptions(DevUtils.getTableFunc(setting, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
-      let DelText = Utils.formatOptions(DevUtils.dropfunc(setting.innerFunc))          // 鍒犻櫎瀛樺偍杩囩▼sql
+      let newLText = Utils.formatOptions(FuncUtils.getTableFunc(setting, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
+      let DelText = Utils.formatOptions(FuncUtils.dropfunc(setting.innerFunc))          // 鍒犻櫎瀛樺偍杩囩▼sql
 
       this.refs.tableCreatFunc.exec(setting.innerFunc, newLText, DelText).then(result => {
         if (result === 'success') {
@@ -906,7 +905,7 @@
         if (_ismutil && group.sublist.length === 0) {
           _config.enabled = false
         }
-        let arr = group.sublist.filter(item => item.field.toLowerCase() === _primary)
+        let arr = group.sublist.filter(item => item.field && item.field.toLowerCase() === _primary)
 
         if (arr.length > 0) {
           _config.enabled = false
@@ -1620,7 +1619,7 @@
     let primaryrepeat = false
 
     config.groups.forEach(group => {
-      let arr = group.sublist.filter(item => item.field.toLowerCase() === _primary)
+      let arr = group.sublist.filter(item => item.field && item.field.toLowerCase() === _primary)
 
       if (arr.length > 0) {
         primaryrepeat = true
@@ -2014,7 +2013,6 @@
           visible={this.state.tableVisible}
           width={'65vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           cancelText={this.state.dict['model.close']}
           onOk={this.addFieldSubmit}
           onCancel={() => { // 鍙栨秷娣诲姞
@@ -2038,7 +2036,6 @@
           visible={this.state.profileVisible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={this.state.dict['model.submit']}
           onOk={this.verifySubmit}
           onCancel={() => { this.setState({ profileVisible: false }) }}
diff --git a/src/templates/formtabconfig/source.jsx b/src/templates/formtabconfig/source.jsx
index 8306f96..148e0ab 100644
--- a/src/templates/formtabconfig/source.jsx
+++ b/src/templates/formtabconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class CommonTableBaseData {
   baseConfig = {
diff --git a/src/templates/menuconfig/editfirstmenu/index.jsx b/src/templates/menuconfig/editfirstmenu/index.jsx
index f1f6290..3df062c 100644
--- a/src/templates/menuconfig/editfirstmenu/index.jsx
+++ b/src/templates/menuconfig/editfirstmenu/index.jsx
@@ -38,7 +38,7 @@
     editMvisible: false, // 缂栬緫鑿滃崟妯℃�佹
     thawMvisible: false, // 瑙i櫎鍐荤粨妯℃�佹
     confirmLoading: false, // 鎻愪氦涓�傘�傘��
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
   }
 
   handlePreviewList = (List) => {
@@ -336,7 +336,7 @@
           <div className="tipcard card5" style={{backgroundImage: 'url(' + card5 + ')'}}>
             <p>缂栬緫鐘舵�佷腑锛岃彍鍗曚箣澶栧尯鍩熶細閿佸畾锛屾煡鐪嬬郴缁熸暟鎹鐐瑰嚮銆�</p>
             <div>
-              <a target="blank" href="#/main" >鏂伴〉闈�</a>
+              <span className="new-view" onClick={() => {window.open('#/main')}} >鏂伴〉闈�</span>
             </div>
           </div>
         </div>
diff --git a/src/templates/menuconfig/editfirstmenu/index.scss b/src/templates/menuconfig/editfirstmenu/index.scss
index b38e001..ac8a533 100644
--- a/src/templates/menuconfig/editfirstmenu/index.scss
+++ b/src/templates/menuconfig/editfirstmenu/index.scss
@@ -12,7 +12,7 @@
       border-radius: 6px;
       background-size: 100% 100%;
       background-repeat: no-repeat;
-      padding: 15px;  
+      padding: 15px;
     }
     .card1 {
       left: 30%;
@@ -61,8 +61,9 @@
       }
       div {
         text-align: center;
-        a {
+        .new-view {
           display: inline-block;
+          cursor: pointer;
           color: #fff;
           padding: 2px 10px;
           border-radius: 4px;
diff --git a/src/templates/menuconfig/editsecmenu/index.jsx b/src/templates/menuconfig/editsecmenu/index.jsx
index d520e69..6d9733e 100644
--- a/src/templates/menuconfig/editsecmenu/index.jsx
+++ b/src/templates/menuconfig/editsecmenu/index.jsx
@@ -32,7 +32,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     menulist: null,       // 鑿滃崟鍒楄〃
     type: '',             // 缂栬緫绫诲瀷锛宎dd or edit
     title: '',            // 妯℃�佹鏍囬
@@ -415,7 +415,7 @@
           <div className="tipcard card5" style={{backgroundImage: 'url(' + card5 + ')'}}>
             <p>缂栬緫鐘舵�佷腑锛岃彍鍗曚箣澶栧尯鍩熶細閿佸畾锛屾煡鐪嬬郴缁熸暟鎹鐐瑰嚮銆�</p>
             <div>
-              <a target="blank" href="#/main" >鏂伴〉闈�</a>
+              <span className="new-view" onClick={() => {window.open('#/main')}} >鏂伴〉闈�</span>
             </div>
           </div>
         </div>
diff --git a/src/templates/menuconfig/editsecmenu/index.scss b/src/templates/menuconfig/editsecmenu/index.scss
index 43866ca..59277cb 100644
--- a/src/templates/menuconfig/editsecmenu/index.scss
+++ b/src/templates/menuconfig/editsecmenu/index.scss
@@ -1,6 +1,6 @@
 .second-edit-box {
   position: relative;
-  z-index: 1060;
+  z-index: 10;
   .mask {
     position: fixed;
     top: 0px;
@@ -63,8 +63,9 @@
       }
       div {
         text-align: center;
-        a {
+        .new-view {
           display: inline-block;
+          cursor: pointer;
           color: #fff;
           padding: 2px 10px;
           border-radius: 4px;
diff --git a/src/templates/menuconfig/editthdmenu/index.jsx b/src/templates/menuconfig/editthdmenu/index.jsx
index 1dc074d..72cc5e2 100644
--- a/src/templates/menuconfig/editthdmenu/index.jsx
+++ b/src/templates/menuconfig/editthdmenu/index.jsx
@@ -21,7 +21,7 @@
 import MenuForm from './menuform'
 import TransferForm from '@/templates/zshare/basetransferform'
 import Utils from '@/utils/utils.js'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import DragElement from '../menuelement'
 import asyncLoadComponent from '@/utils/asyncLoadComponent'
 import './index.scss'
@@ -53,7 +53,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     thawmenulist: null,     // 宸插喕缁撶殑浜岀骇鑿滃崟
     type: '',               // 鎿嶄綔绫诲瀷锛屾柊寤烘垨缂栬緫鑿滃崟
     thawMvisible: false,    // 瑙i櫎鍐荤粨妯℃�佹
@@ -786,7 +786,7 @@
             <div className="tipcard card5" style={{backgroundImage: 'url(' + card5 + ')'}}>
               <p>缂栬緫鐘舵�佷腑锛岃彍鍗曚箣澶栧尯鍩熶細閿佸畾锛屾煡鐪嬬郴缁熸暟鎹鐐瑰嚮銆�</p>
               <div>
-                <a target="blank" href="#/main" >鏂伴〉闈�</a>
+                <span className="new-view" onClick={() => {window.open('#/main')}} >鏂伴〉闈�</span>
               </div>
             </div>
           </div> : null
diff --git a/src/templates/menuconfig/editthdmenu/index.scss b/src/templates/menuconfig/editthdmenu/index.scss
index 1172444..53a842c 100644
--- a/src/templates/menuconfig/editthdmenu/index.scss
+++ b/src/templates/menuconfig/editthdmenu/index.scss
@@ -1,6 +1,6 @@
 .third-edit-box {
   position: relative;
-  z-index: 1060;
+  z-index: 10;
   .mask {
     position: fixed;
     top: 0px;
@@ -63,8 +63,9 @@
       }
       div {
         text-align: center;
-        a {
+        .new-view {
           display: inline-block;
+          cursor: pointer;
           color: #fff;
           padding: 2px 10px;
           border-radius: 4px;
diff --git a/src/templates/menuconfig/editthdmenu/menuform/index.jsx b/src/templates/menuconfig/editthdmenu/menuform/index.jsx
index 4448a11..c86cde8 100644
--- a/src/templates/menuconfig/editthdmenu/menuform/index.jsx
+++ b/src/templates/menuconfig/editthdmenu/menuform/index.jsx
@@ -3,6 +3,7 @@
 import { Form, Row, Col, Input, Select } from 'antd'
 import './index.scss'
 
+const { TextArea } = Input
 
 class MainSearch extends Component {
   static propTpyes = {
@@ -158,7 +159,7 @@
                     message: '鍦板潃鏈�闀夸负1024涓瓧绗�!'
                   }
                 ]
-              })(<Input placeholder="" autoComplete="off" />)}
+              })(<TextArea rows={2} />)}
             </Form.Item>
           </Col> : null}
         </Row>
diff --git a/src/templates/menuconfig/menuelement/card.jsx b/src/templates/menuconfig/menuelement/card.jsx
index 3777966..36a5981 100644
--- a/src/templates/menuconfig/menuelement/card.jsx
+++ b/src/templates/menuconfig/menuelement/card.jsx
@@ -52,7 +52,7 @@
       </div>
       {/* 鑷畾涔夋ā鏉匡紝鍦ㄦ柊椤甸潰缂栬緫 */}
       {!card.forbidden && card.type !== 'CustomPage' ? <Icon className="edit" type="edit" onClick={edit} /> : null}
-      {card.type === 'CustomPage' ? <a href={`#/menudesign/${_param}`} target="_blank" rel="noopener noreferrer"><Icon className="edit" type="edit" /></a> : null}
+      {card.type === 'CustomPage' ? <Icon className="edit" onClick={() => {window.open(`#/menudesign/${_param}`)}} type="edit" /> : null}
       <Icon className="close" type="close" onClick={close} />
     </div>
   )
diff --git a/src/templates/modalconfig/checkCard/index.jsx b/src/templates/modalconfig/checkCard/index.jsx
index ea21875..63c3164 100644
--- a/src/templates/modalconfig/checkCard/index.jsx
+++ b/src/templates/modalconfig/checkCard/index.jsx
@@ -30,6 +30,16 @@
     }
 
     if (display !== 'picture') {
+      if (!options || options.length === 0) {
+        return <Col span={width}>
+          <div className="card-cell">
+            {fields ? fields.map(col => {
+              return <span key={col.key} style={{color: col.color, fontSize: col.fontSize + 'px', height: col.fontSize * 1.5 + 'px', textAlign: col.align}}>{col.field}</span>
+            }) : null}
+            {!fields || fields.length === 0 ? <span style={{color: '#000000', fontSize: '14px', height: '21px'}}>绀轰緥</span> : null}
+          </div>
+        </Col>
+      }
       return options.map(item => {
         return <Col span={width} key={item.key}>
           <div className="card-cell">
@@ -40,6 +50,12 @@
         </Col>
       })
     } else {
+      if (!options || options.length === 0) {
+        return <Col span={width}>
+          <div className="card-pic-cell" style={{paddingTop, background: '#91d5ff'}}>
+          </div>
+        </Col>
+      }
       return options.map(item => {
         return <Col span={width} key={item.key}>
           <div className="card-pic-cell" style={{paddingTop, backgroundImage: `url(${item.$url})`}}>
diff --git a/src/templates/modalconfig/dragelement/card.jsx b/src/templates/modalconfig/dragelement/card.jsx
index 75afee1..bfb0c5b 100644
--- a/src/templates/modalconfig/dragelement/card.jsx
+++ b/src/templates/modalconfig/dragelement/card.jsx
@@ -1,6 +1,6 @@
 import React from 'react'
 import { useDrag, useDrop } from 'react-dnd'
-import { Icon, Select, DatePicker, Input, InputNumber, Button, Popover, Switch, Radio, Checkbox } from 'antd'
+import { Icon, Select, DatePicker, Input, InputNumber, Button, Popover, Switch, Radio, Checkbox, Form } from 'antd'
 import moment from 'moment'
 
 import asyncComponent from '@/utils/asyncComponent'
@@ -13,7 +13,7 @@
 const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
 const CheckCard = asyncComponent(() => import('../checkCard'))
 
-const Card = ({ id, card, cols, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
+const Card = ({ id, card, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
   const originalIndex = findCard(id).index
   const [{ isDragging }, drag] = useDrag({
     item: { type: 'form', id, originalIndex },
@@ -60,29 +60,6 @@
       }
     } else if (card.setAll === 'true') {
       selectval = card.emptyText || '绌�'
-    }
-  }
-  let labelCol = 'ant-col-sm-8'
-  let wrapCol = 'ant-col-sm-16'
-  let isEntireLine = false
-
-  if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
-    isEntireLine = true
-  }
-
-  if (isEntireLine) {
-    if (cols === '2') {
-      labelCol = 'ant-col-sm-4'
-      wrapCol = 'ant-col-sm-20'
-    } else if (cols === '3') {
-      labelCol = 'ant-col-cuslabel'
-      wrapCol = 'ant-col-cuswrap'
-    } else if (cols === '4') {
-      labelCol = 'ant-col-sm-2'
-      wrapCol = 'ant-col-sm-22'
-    }
-    if (card.hidelabel === 'true') {
-      wrapCol = 'ant-col-sm-24'
     }
   }
 
@@ -133,9 +110,19 @@
       <Checkbox value="D">D</Checkbox>
     </Checkbox.Group>)
   } else if (card.type === 'hint') {
-    formItem = <div style={{marginTop: '10px', color: 'rgba(0, 0, 0, 0.85)'}}>{card.message}</div>
+    formItem = <div style={{marginTop: '10px', color: 'rgba(0, 0, 0, 0.85)', lineHeight: '1.5'}}>{card.message}</div>
+  } else if (card.type === 'split') {
+    formItem = <div className="split-line">{card.label}</div>
   } else if (card.type === 'checkcard') {
     formItem = <CheckCard width={card.width} ratio={card.ratio} display={card.display} fields={card.fields} options={card.options} />
+  }
+
+  let _label = card.label
+  if (card.tooltip) {
+    _label = <span><Icon type="question-circle" />{card.label}</span>
+  }
+  if (card.type === 'brafteditor' && card.hidelabel === 'true') {
+    _label = null
   }
 
   return (
@@ -148,17 +135,18 @@
     } trigger="hover">
       <div className="page-card" style={{ opacity: opacity}}>
         <div ref={node => drag(drop(node))}>
-          {<div className="ant-row ant-form-item">
-            {card.hidelabel !== 'true' ? <div className={'ant-col ant-form-item-label ant-col-xs-24 ' + labelCol}>
-              {card.label ? <label className={card.required === 'true' ? 'required' : ''}>{card.tooltip ? 
-                <Icon type="question-circle" /> : null}
-                {card.label}</label> : null}
-            </div> : null}
-            <div className={'ant-col ant-form-item-control-wrapper ant-col-xs-24 ' + wrapCol}>
-              {formItem}
-              {showField ? card.field : ''}
-            </div>
-          </div>}
+          {card.type === 'split' ? formItem : <Form.Item
+            className="ant-form-item"
+            colon={!!_label}
+            label={_label}
+            required={card.required === 'true'}
+            extra={card.extra || null}
+            labelCol={card.labelwidth ? {style: {width: card.labelwidth + '%'}} : null}
+            wrapperCol={card.labelwidth ? {style: {width: (100 - card.labelwidth) + '%'}} : null}
+          >
+            {formItem}
+            {showField ? <div className="field-name">{card.field}</div> : ''}
+          </Form.Item>}
         </div>
       </div>
     </Popover>
diff --git a/src/templates/modalconfig/dragelement/index.jsx b/src/templates/modalconfig/dragelement/index.jsx
index bcfe294..42ded36 100644
--- a/src/templates/modalconfig/dragelement/index.jsx
+++ b/src/templates/modalconfig/dragelement/index.jsx
@@ -7,7 +7,7 @@
 import Card from './card'
 import './index.scss'
 
-const Container = ({list, group, setting, placeholder, handleList, handleForm, closeForm, showField }) => {
+const Container = ({list, setting, placeholder, handleList, handleForm, closeForm, showField }) => {
   const [cards, setCards] = useState(list)
   const moveCard = (id, atIndex) => {
     const { card, index } = findCard(id)
@@ -16,11 +16,7 @@
 
     const _cards = update(cards, { $splice: [[index, 1], [atIndex, 0, card]] })
 
-    if (!group) {
-      handleList(_cards)
-    } else {
-      handleList(_cards, group)
-    }
+    handleList(_cards)
   }
 
   if (!is(fromJS(cards), fromJS(list))) {
@@ -69,22 +65,12 @@
 
     setCards(_cards)
 
-    if (!group) {
-      handleList(_cards, null, null, _card)
-    } else {
-      handleList(_cards, group, null, _card)
-    }
+    handleList(_cards, _card)
   }
 
   const [, drop] = useDrop({
     accept: 'form',
     drop(item) {
-      if (item.hasOwnProperty('originalIndex') && group) {
-        const { card } = findCard(item.id)
-        if (!card) {
-          handleList(cards, group, item.id)
-        }
-      }
       if (item.hasOwnProperty('originalIndex')) {
         return
       }
@@ -113,29 +99,16 @@
 
       setCards(_cards)
 
-      if (!group) {
-        handleList(_cards, null, null, newcard)
-      } else {
-        handleList(_cards, group, null, newcard)
-      }
+      handleList(_cards, newcard)
     }
   })
 
-  let _cols = 24 / (setting.cols || 2)
-  
   return (
-    <div ref={drop} className="ant-row modal-fields-row">
+    <div ref={drop} className={'ant-row modal-fields-row ' + (setting.align || 'left_right')} >
       {cards.map(card => {
-        let isEntireLine = false
-
-        if (card.entireLine === 'true' || ['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type)) {
-          isEntireLine = true
-        }
-        
-        return <Col key={card.uuid} className={isEntireLine ? 'textarea' + setting.cols : ''} span={isEntireLine ? 24 : _cols}>
+        return <Col key={card.uuid} span={card.span || 24}>
           <Card
             id={card.uuid}
-            cols={setting.cols}
             card={card}
             showField={showField}
             moveCard={moveCard}
diff --git a/src/templates/modalconfig/dragelement/index.scss b/src/templates/modalconfig/dragelement/index.scss
index d68b01a..ad41b4d 100644
--- a/src/templates/modalconfig/dragelement/index.scss
+++ b/src/templates/modalconfig/dragelement/index.scss
@@ -15,13 +15,113 @@
 }
 .modal-fields-row {
   padding-bottom: 35px;
-  .ant-col {
-    padding-left: 12px;
-    padding-right: 12px;
-  }
   >.ant-col {
     display: inline-block;
     float: none;
     vertical-align: top;
+    padding-left: 1.2%;
+    padding-right: 1.2%;
+  }
+  .split-line {
+    color: #1890ff;
+    font-size: 15px;
+    padding-left: 10px;
+    border-bottom: 1px solid #e9e9e9;
+  }
+  .ant-form-item {
+    cursor: move;
+    display: flex;
+    margin-bottom: 0px;
+    .ant-form-item-label {
+      overflow: visible;
+      position: relative;
+      cursor: move;
+      height: 40px;
+      width: 33.3%;
+      label {
+        width: 100%;
+        cursor: move;
+        overflow: hidden;
+        display: inline-block;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+      .anticon-question-circle {
+        color: #c49f47;
+        margin-right: 3px;
+        line-height: 40px;
+      }
+    }
+    .ant-form-item-control-wrapper {
+      position: relative;
+      flex: 1;
+      .ant-select {
+        width: 100%;
+        margin-top: 4px;
+      }
+      .field-name {
+        line-height: 1.3;
+        float: left;
+      }
+      .ant-checkbox-group {
+        line-height: 40px;
+        .ant-checkbox-wrapper {
+          margin-right: 8px;
+        }
+        .ant-checkbox-wrapper + .ant-checkbox-wrapper {
+          margin-left: 0px;
+        }
+      }
+      .ant-radio-group {
+        line-height: 40px;
+      }
+      .ant-calendar-picker {
+        width: 100%;
+        margin-top: 4px;
+      }
+      .ant-input-number {
+        width: 100%;
+        margin-top: 4px;
+      }
+      .color-sketch-block {
+        margin-top: 7px;
+        overflow: hidden;
+        .color-sketch-block-box {
+          min-width: 100px;
+        }
+      }
+      .normal-braft-editor {
+        border: 1px solid #d9d9d9;
+        border-radius: 4px;
+      }
+    }
+    .ant-form-item-control-wrapper::after {
+      content: '';
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+      bottom: 0;
+      opacity: 0;
+      z-index: 1;
+    }
+    .ant-col-cuslabel {
+      width: 10.5%;
+    }
+    .ant-col-cuswrap {
+      width: 89.5%;
+    }
+  }
+}
+.modal-fields-row.up_down {
+  .ant-form-item {
+    display: block;
+    .ant-form-item-label {
+      width: 100%!important;
+      text-align: left;
+    }
+    .ant-form-item-control-wrapper {
+      width: 100%!important;
+    }
   }
 }
\ No newline at end of file
diff --git a/src/templates/modalconfig/groupform/index.jsx b/src/templates/modalconfig/groupform/index.jsx
deleted file mode 100644
index 423e7a5..0000000
--- a/src/templates/modalconfig/groupform/index.jsx
+++ /dev/null
@@ -1,135 +0,0 @@
-import React, {Component} from 'react'
-import PropTypes from 'prop-types'
-import { Form, Row, Col, Input, InputNumber } from 'antd'
-import { formRule } from '@/utils/option.js'
-import TransferForm from '../transferform'
-import Utils from '@/utils/utils.js'
-import './index.scss'
-
-class SettingForm extends Component {
-  static propTpyes = {
-    dict: PropTypes.object, // 瀛楀吀椤�
-    group: PropTypes.object, // 瀛楀吀椤�
-    config: PropTypes.object,
-    inputSubmit: PropTypes.any  // 鍥炶溅鎻愪氦浜嬩欢
-  }
-
-  state = {
-    source: null,
-    selectds: null,
-    default: null
-  }
-
-  UNSAFE_componentWillMount () {
-    const { config, group } = this.props
-
-    let _source = null
-    let _default = config.groups.slice(-1)[0]
-    let _selectds = []
-
-    if (!_default) {
-      _default = {
-        label: '鏈垎缁�',
-        default: true,
-        sort: 101,
-        uuid: Utils.getuuid(),
-        sublist: []
-      }
-    }
-
-    if (config.groups.length === 0) {
-      _source = config.fields.filter(item => !item.origin)
-    } else {
-      _source = [..._default.sublist, ...group.sublist]
-      _selectds = group.sublist.map(item => { return item.uuid })
-    }
-
-    this.setState({
-      source: _source,
-      selectds: _selectds,
-      default: _default
-    })
-  }
-
-  handleConfirm = () => {
-    // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
-    return new Promise((resolve, reject) => {
-      this.props.form.validateFieldsAndScroll((err, values) => {
-        if (!err) {
-          let targetKeys = this.refs['fields-transfer'].state.targetKeys
-          let defaultlist = this.state.source.filter(item => !targetKeys.includes(item.uuid))
-
-          values.sublist = targetKeys.map(item => {
-            return this.state.source.filter(cell => cell.uuid === item)[0]
-          })
-
-          resolve({
-            default: {...this.state.default, sublist: defaultlist},
-            target: values
-          })
-        } else {
-          reject(err)
-        }
-      })
-    })
-  }
-
-  handleSubmit = (e) => {
-    e.preventDefault()
-
-    if (this.props.inputSubmit) {
-      this.props.inputSubmit()
-    }
-  }
-
-  render() {
-    const { group, config } = this.props
-    const { getFieldDecorator } = this.props.form
-
-    const formItemLayout = {
-      labelCol: {
-        xs: { span: 24 },
-        sm: { span: 8 }
-      },
-      wrapperCol: {
-        xs: { span: 24 },
-        sm: { span: 16 }
-      }
-    }
-    return (
-      <Form {...formItemLayout} className="ant-advanced-search-form modal-setting-form">
-        <Row gutter={24}>
-          <Col span={12}>
-            <Form.Item label="鍒嗙粍鍚嶇О">
-              {getFieldDecorator('label', {
-                initialValue: group.label,
-                rules: [
-                  {
-                    required: true,
-                    message: this.props.dict['form.required.input'] + '鍒嗙粍鍚嶇О!'
-                  },
-                  {
-                    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('sort', {
-                initialValue: group.hasOwnProperty('sort') ? group.sort : config.groups.length
-              })(<InputNumber min={0} max={100} precision={0} />)}
-            </Form.Item>
-          </Col>
-          <Col span={24}>
-            <TransferForm fields={this.state.source} ref="fields-transfer" selected={this.state.selectds}/>
-          </Col>
-        </Row>
-      </Form>
-    )
-  }
-}
-
-export default Form.create()(SettingForm)
\ No newline at end of file
diff --git a/src/templates/modalconfig/index.jsx b/src/templates/modalconfig/index.jsx
index 857474c..8934dad 100644
--- a/src/templates/modalconfig/index.jsx
+++ b/src/templates/modalconfig/index.jsx
@@ -5,7 +5,7 @@
 import { DndProvider } from 'react-dnd'
 import HTML5Backend from 'react-dnd-html5-backend'
 import moment from 'moment'
-import { Button, Card, Modal, Collapse, notification, Icon, Popover, Switch } from 'antd'
+import { Button, Card, Modal, Collapse, notification, Icon, Switch } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
@@ -14,19 +14,19 @@
 import { getModalForm } from '@/templates/zshare/formconfig'
 
 import ModalForm from '@/templates/zshare/modalform'
-import DragElement from './dragelement'
 import SourceElement from './dragelement/source'
 import SettingForm from './settingform'
-import GroupForm from './groupform'
 import MenuForm from './menuform'
 import asyncComponent from '@/utils/asyncComponent'
 import { BaseConfig, SearchItems } from './source'
+import { updateForm } from '@/utils/utils-update.js'
 import './index.scss'
 
 const { Panel } = Collapse
 const { confirm } = Modal
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
+const DragElement = asyncComponent(() => import('./dragelement'))
 const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
 const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
 
@@ -47,7 +47,6 @@
   state = {
     menu: null,            // 涓婄骇鑿滃崟锛屼笁绾ц彍鍗曟垨鏍囩
     dict: CommonDict,      // 瀛楀吀
-    tableFields: [],       // 宸查�夌殑甯哥敤琛�
     config: null,          // 椤甸潰閰嶇疆锛屽寘鎷ā鏉跨被鍨嬨�佹ā鎬佹璁剧疆銆佹坊鍔犺〃鍚嶃�佽〃鍗曞垪琛�
     visible: false,        // 琛ㄥ崟缂栬緫妯℃�佹锛屾樉绀烘帶鍒�
     modalformlist: null,   // 鍩烘湰淇℃伅琛ㄥ崟瀛楁
@@ -58,8 +57,6 @@
     settingVisible: false, // 鍏ㄥ眬閰嶇疆妯℃�佹
     closeVisible: false,   // 鍏抽棴妯℃�佹
     originConfig: null,    // 鍘熷鑿滃崟
-    groupVisible: false,   // 鍏ㄥ眬閰嶇疆妯℃�佹
-    curgroup: null,        // 褰撳墠缁勶紝鏂板缓鎴栫紪杈�
     sqlVerifing: false,    // sql楠岃瘉
     openEdition: '',       // 缂栬緫鐗堟湰鏍囪锛岄槻姝㈠浜烘搷浣�
     showField: false       // 鏄剧ず琛ㄥ崟瀛楁鍊�
@@ -108,6 +105,7 @@
         url: ''
       })
     }
+    _config = updateForm(_config)
 
     this.setState({
       openEdition: editAction.open_edition || '',
@@ -173,78 +171,20 @@
    * 2銆佽〃鍗曠Щ鍔ㄥ悗锛屼繚瀛樼Щ鍔ㄥ悗鐨勯『搴�
    * 3銆佹柊澧炶〃鍗曟椂锛岀洿鎺ユ墦寮�缂栬緫妗�
    */
-  handleList = (list, group, elementId, newcard) => {
+  handleList = (list, newcard) => {
     let _config = fromJS(this.state.config).toJS()
 
-    if (!group && !elementId) {
-      // 娌℃湁鍒嗙粍鏃讹紙鎷栨嫿娣诲姞锛�
-      if (list.length > _config.fields.length) {
-        _config.fields = list.filter(item => !item.origin)
-  
-        this.setState({
-          config: _config
-        }, () => {
-          this.handleForm(newcard)
-        })
-      } else {
-        _config.fields = list
-        this.setState({config: _config})
-      }
-    } else if (group && !elementId) {
-      // 瀛樺湪鍒嗙粍鏃讹紝鎷栨嫿娣诲姞
-      if (list.length > group.sublist.length) {
-        group.sublist = list
-        _config.groups = _config.groups.map(item => {
-          if (item.uuid === group.uuid) {
-            return group
-          } else {
-            return item
-          }
-        })
-  
-        this.setState({
-          config: _config
-        }, () => {
-          this.handleForm(newcard)
-        })
-      } else {
-        group.sublist = list
-        _config.groups = _config.groups.map(item => {
-          if (item.uuid === group.uuid) {
-            return group
-          } else {
-            return item
-          }
-        })
-        this.setState({config: _config})
-      }
-    } else if (group && elementId) {
-      // 淇敼宸叉湁鍏冪礌鐨勫垎缁�
-      let element = null
-      _config.groups.forEach(item => {
-        item.sublist = item.sublist.filter(cell => {
-          if (cell.uuid !== elementId) {
-            return true
-          } else {
-            element = cell
-            return false
-          }
-        })
-      })
-
-      group.sublist.push(element)
-
-      _config.groups = _config.groups.map(item => {
-        if (item.uuid === group.uuid) {
-          return group
-        } else {
-          return item
-        }
-      })
+    if (list.length > _config.fields.length) {
+      _config.fields = list.filter(item => !item.origin)
 
       this.setState({
         config: _config
+      }, () => {
+        this.handleForm(newcard)
       })
+    } else {
+      _config.fields = list
+      this.setState({config: _config})
     }
   }
 
@@ -256,8 +196,8 @@
    */
   handleForm = (_card) => {
     const { menu, tabConfig, subTabConfig } = this.props
-    let card = fromJS(_card).toJS()
     const { config } = this.state
+    let card = fromJS(_card).toJS()
     let _inputfields = []
     let _tabfields = []
     let _linkableFields = []
@@ -265,24 +205,19 @@
       value: '',
       text: '绌�'
     }]
-    let _formfields = []
+    let standardform = null
 
-    // 璁剧疆涓嬫媺鑿滃崟鍙叧鑱斿瓧娈�(涓婄骇涓庝笅绾�)
-    if (config.groups.length > 0) {
-      config.groups.forEach(group => {
-        _formfields = [..._formfields, ...group.sublist]
-      })
-    } else {
-      _formfields = config.fields
-    }
-
-    _inputfields = _formfields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
-    _tabfields = _formfields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
+    _inputfields = config.fields.filter(item => item.type === 'text' || item.type === 'number' || item.type === 'textarea' || item.type === 'color')
+    _tabfields = config.fields.filter(item => card.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
     _tabfields.unshift({field: '', text: '鍘熻〃鍗�'})
 
     let uniq = new Map()
     uniq.set(card.field, true)
-    _formfields.forEach(item => {
+    let index = null
+    config.fields.forEach((item, i) => {
+      if (card.uuid === item.uuid) {
+        index = i
+      }
       if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
       if (item.field && !uniq.has(item.field)) {
         uniq.set(item.field, true)
@@ -297,6 +232,14 @@
         })
       }
     })
+
+    if (index !== null) {
+      if (index === 0) {
+        standardform = config.fields[index + 1] || null
+      } else {
+        standardform = config.fields[index - 1] || null
+      }
+    }
 
     if (subTabConfig) {
       subTabConfig.columns.forEach(col => {
@@ -338,7 +281,13 @@
       card.linkSubField = card.linkSubField.filter(item => fields.includes(item))
     }
 
+    if (!card.span && standardform && standardform.span) {
+      card.span = standardform.span
+      card.labelwidth = standardform.labelwidth
+    }
+
     this.setState({
+      standardform,
       visible: true,
       card: card,
       formlist: getModalForm(card, _inputfields, _tabfields, _linkableFields, _linksupFields, !!this.props.editTab)
@@ -357,37 +306,19 @@
       let fieldrepet = false // 瀛楁閲嶅
       let labelrepet = false // 鎻愮ず鏂囧瓧閲嶅
 
-      if (_config.groups.length > 0) {
-        _config.groups.forEach(group => {
-          group.sublist = group.sublist.map(item => {
-            if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
-              fieldrepet = true
-            } else if (item.uuid !== res.uuid && item.label === res.label) {
-              labelrepet = true
-            }
+      _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
-            }
-          })
-        })
-      } else {
-        _config.fields = _config.fields.map(item => {
-          if (item.uuid !== res.uuid && item.field.toLowerCase() === res.field.toLowerCase()) {
-            fieldrepet = true
-          } else if (item.uuid !== res.uuid && item.label === res.label) {
-            labelrepet = true
-          }
-
-          if (item.uuid === res.uuid) {
-            return res
-          } else {
-            return item
-          }
-        })
-      }
+        if (item.uuid === res.uuid) {
+          return res
+        } else {
+          return item
+        }
+      })
 
       if (fieldrepet) {
         notification.warning({
@@ -461,17 +392,10 @@
     let _this = this
 
     confirm({
-      content: `纭畾鍒犻櫎<<${card.label}>>鍚楋紵`,
+      content: `纭畾鍒犻櫎${card.label ? `<<${card.label}>>` : ''}鍚楋紵`,
       onOk() {
         let _config = fromJS(_this.state.config).toJS()
-
-        if (_config.groups.length > 0) {
-          _config.groups.forEach(group => {
-            group.sublist = group.sublist.filter(item => !(item.uuid === card.uuid))
-          })
-        } else {
-          _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
-        }
+        _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
 
         _this.setState({
           config: _config,
@@ -485,7 +409,7 @@
     const { editAction } = this.props
     const { config, menu, openEdition } = this.state
 
-    if ((!config.groups[0] && !config.fields[0]) || (config.fields[0] && config.fields[0].origin)) {
+    if (config.fields[0] && config.fields[0].origin) {
       notification.warning({
         top: 92,
         message: '璇锋坊鍔犺〃鍗�',
@@ -588,18 +512,6 @@
   }
 
   /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
-
-    this.setState({
-      config: config,
-      tableFields: fields ? fields : tableFields
-    })
-  }
-
-  /**
    * @description 鍏ㄥ眬璁剧疆妯℃�佹
    */
   changeSetting = () => {
@@ -621,115 +533,12 @@
     })
   }
 
-  handleGroup = (group) => {
-    let curgroup = ''
-
-    if (group) {
-      curgroup = group
-    } else {
-      curgroup = {
-        isnew: true,
-        label: '',
-        default: false,
-        uuid: Utils.getuuid(),
-        sublist: []
-      }
-    }
-
-    this.setState({
-      groupVisible: true,
-      curgroup: curgroup
-    })
-  }
-
-  closeGroup = (group) => {
-    let _this = this
-
-    confirm({
-      content: `纭畾鍒犻櫎鍒嗙粍<<${group.label}>>鍚楋紵`,
-      onOk() {
-        let _config = fromJS(_this.state.config).toJS()
-        _config.groups = _config.groups.filter(item => !(item.uuid === group.uuid))
-        let _length = _config.groups.length
-        
-        if (_length === 1) {
-          _config.fields = [...group.sublist, ..._config.groups[0].sublist]
-          _config.groups = []
-        } else {
-          _config.groups[_length - 1].sublist = [...group.sublist, ..._config.groups[_length - 1].sublist]
-        }
-
-        _this.setState({
-          config: _config
-        })
-      },
-      onCancel() {}
-    })
-  }
-
-  handleGroupSave = () => {
-    let _group = fromJS(this.state.curgroup).toJS()
-    let config = fromJS(this.state.config).toJS()
-
-    this.groupRef.handleConfirm().then(res => {
-      _group = {..._group, ...res.target}
-
-      if (_group.isnew) {
-        delete _group.isnew
-        config.groups.unshift(_group)
-
-        if (config.groups.length > 1) {
-          config.groups = config.groups.map(item => {
-            if (item.default) {
-              return res.default
-            } else {
-              return item
-            }
-          })
-        } else {
-          config.groups.push(res.default)
-        }
-      } else {
-        config.groups = config.groups.map(item => {
-          if (item.uuid === _group.uuid) {
-            return _group
-          } else if (item.default) {
-            return res.default
-          } else {
-            return item
-          }
-        })
-      }
-
-      config.fields = []
-
-      config.groups = config.groups.sort((a, b) => {
-        return a.sort - b.sort
-      })
-      
-      this.setState({
-        groupVisible: false,
-        curgroup: '',
-        config: config
-      })
-    })
-  }
-
   editModalCancel = () => {
     const { config, card } = this.state
 
     if (card.focus) {
-      let _config = null
-      if (config.groups.length > 0) {
-        let _groups = config.groups.map(group => {
-          group.sublist = group.sublist.filter(item => item.uuid !== card.uuid)
-          return group
-        })
-        _config = {...config, groups: _groups}
-      } else {
-        let _fields = config.fields.filter(item => item.uuid !== card.uuid)
-        _config = {...config, fields: _fields}
-      }
+      let _fields = config.fields.filter(item => item.uuid !== card.uuid)
+      let _config = {...config, fields: _fields}
 
       this.setState({
         card: null,
@@ -765,6 +574,40 @@
     })
   }
 
+  changecols = (type) => {
+    let config = fromJS(this.state.config).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() {
+        _this.setState({config})
+      },
+      onCancel() {}
+    })
+  }
+
   render () {
     const { config, source, dict } = this.state
 
@@ -782,7 +625,7 @@
                 <TableComponent
                   config={config}
                   containerId="modal-basedata"
-                  updatetable={this.updatetable}
+                  updatetable={this.updateconfig}
                 />
               </Panel>
               <Panel header={dict['header.menu.form']} key="1">
@@ -794,10 +637,8 @@
                 <FieldsComponent
                   config={config}
                   type="form"
-                  tableFields={this.state.tableFields}
                   updatefield={this.updateconfig}
                 />
-                <Button type="primary" block onClick={() => this.handleGroup()}>{dict['header.menu.group.add']}</Button>
               </Panel>
             </Collapse>
           </div>
@@ -813,50 +654,23 @@
               <div className="ant-modal-content" style={{width: config.setting.width + '%'}}>
                 <div className="ant-modal-header">
                   <div className="ant-modal-title">{config.setting.title}</div>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3鍒�</Button>
+                  <Button className="mk-cols-change" onClick={() => this.changecols(4)}>4鍒�</Button>
                   <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
                 </div>
                 <div className="ant-modal-body">
                   <div className="modal-form">
-                    {config.groups.length > 0 &&
-                      config.groups.map(group => {
-                        return (
-                          <div key={group.uuid}>
-                            <div className="group-title">
-                              {!group.default ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
-                                <div className="mk-popover-control">
-                                  <Icon className="edit" type="edit" onClick={() => {this.handleGroup(group)}} />
-                                  <Icon className="edit close" type="close" onClick={() => {this.closeGroup(group)}} />
-                                </div>
-                              } trigger="hover">
-                                <span>{group.label}</span>
-                              </Popover> : null}
-                              {group.default ? <span style={{color: '#bcbcbc'}}>{group.label}</span> : null}
-                            </div>
-                            <DragElement
-                              group={group}
-                              list={group.sublist}
-                              setting={config.setting}
-                              showField={this.state.showField}
-                              placeholder={dict['header.form.modal.placeholder']}
-                              handleList={this.handleList}
-                              handleForm={this.handleForm}
-                              closeForm={this.closeForm}
-                            />
-                          </div>
-                        )
-                      })
-                    }
-                    {config.groups.length === 0 ?
-                      <DragElement
-                        list={config.fields}
-                        setting={config.setting}
-                        showField={this.state.showField}
-                        placeholder={dict['header.form.modal.placeholder']}
-                        handleList={this.handleList}
-                        handleForm={this.handleForm}
-                        closeForm={this.closeForm}
-                      /> : null
-                    }
+                    <DragElement
+                      list={config.fields}
+                      setting={config.setting}
+                      showField={this.state.showField}
+                      placeholder={dict['header.form.modal.placeholder']}
+                      handleList={this.handleList}
+                      handleForm={this.handleForm}
+                      closeForm={this.closeForm}
+                    />
                   </div>
                 </div>
                 <div className="ant-modal-footer">
@@ -889,6 +703,7 @@
             card={this.state.card}
             formlist={this.state.formlist}
             inputSubmit={this.handleSubmit}
+            standardform={this.state.standardform}
             wrappedComponentRef={(inst) => this.formRef = inst}
           />
         </Modal>
@@ -923,23 +738,6 @@
           destroyOnClose
         >
           {dict['header.menu.config.placeholder']}
-        </Modal>
-        <Modal
-          title={dict['header.menu.group.manage']}
-          visible={this.state.groupVisible}
-          width={700}
-          maskClosable={false}
-          onOk={this.handleGroupSave}
-          onCancel={() => { this.setState({ groupVisible: false }) }}
-          destroyOnClose
-        >
-          <GroupForm
-            dict={dict}
-            config={config}
-            group={this.state.curgroup}
-            inputSubmit={this.handleGroupSave}
-            wrappedComponentRef={(inst) => this.groupRef = inst}
-          />
         </Modal>
       </div>
     )
diff --git a/src/templates/modalconfig/index.scss b/src/templates/modalconfig/index.scss
index 9dd5236..24849c9 100644
--- a/src/templates/modalconfig/index.scss
+++ b/src/templates/modalconfig/index.scss
@@ -136,10 +136,19 @@
           z-index: 10;
           background: transparent;
           min-height: 50px;
+          padding-right: 75px;
+          .ant-modal-title {
+            display: inline-block;
+          }
           .ant-switch {
             position: absolute;
             top: 15px;
             right: 10px;
+          }
+          .mk-cols-change {
+            float: right;
+            height: 25px;
+            margin-right: 10px;
           }
         }
         .ant-modal-footer {
@@ -174,9 +183,6 @@
         > .ant-row {
           min-height: 120px;
         }
-        .ant-row .ant-col-6 {
-          padding: 0 12px!important;
-        }
         .ant-row.ant-form-item .ant-col {
           padding: 0;
         }
@@ -188,92 +194,6 @@
           background: #ffffff;
           border-radius: 2px;
           margin-bottom: 15px;
-          .ant-form-item {
-            cursor: move;
-            display: flex;
-            margin-bottom: 0px;
-            .ant-form-item-label {
-              overflow: visible;
-              position: relative;
-              height: 40px;
-              label {
-                width: 100%;
-                cursor: move;
-                overflow: hidden;
-                display: inline-block;
-                text-overflow: ellipsis;
-                white-space: nowrap;
-              }
-              label.required::before {
-                display: inline-block;
-                margin-right: 4px;
-                color: #f5222d;
-                font-size: 14px;
-                font-family: SimSun, sans-serif;
-                line-height: 1;
-                content: '*';
-              }
-              .anticon-question-circle {
-                color: #c49f47;
-                margin-right: 3px;
-                line-height: 40px;
-              }
-            }
-            .ant-form-item-control-wrapper {
-              position: relative;
-              .ant-select {
-                width: 100%;
-                margin-top: 4px;
-              }
-              .ant-checkbox-group {
-                line-height: 40px;
-                .ant-checkbox-wrapper {
-                  margin-right: 8px;
-                }
-                .ant-checkbox-wrapper + .ant-checkbox-wrapper {
-                  margin-left: 0px;
-                }
-              }
-              .ant-radio-group {
-                line-height: 40px;
-              }
-              .ant-calendar-picker {
-                width: 100%;
-                margin-top: 4px;
-              }
-              .ant-input-number {
-                width: 100%;
-                margin-top: 4px;
-              }
-              .color-sketch-block {
-                margin-top: 7px;
-                overflow: hidden;
-                .color-sketch-block-box {
-                  min-width: 100px;
-                }
-              }
-              .normal-braft-editor {
-                border: 1px solid #d9d9d9;
-                border-radius: 4px;
-              }
-            }
-            .ant-form-item-control-wrapper::after {
-              content: '';
-              position: absolute;
-              top: 0;
-              left: 0;
-              right: 0;
-              bottom: 0;
-              opacity: 0;
-              z-index: 1;
-            }
-            .ant-col-cuslabel {
-              width: 10.5%;
-            }
-            .ant-col-cuswrap {
-              width: 89.5%;
-            }
-          }
         }
         .ant-calendar-picker {
           min-width: 100px!important;
diff --git a/src/templates/modalconfig/settingform/index.jsx b/src/templates/modalconfig/settingform/index.jsx
index c5af7aa..14517aa 100644
--- a/src/templates/modalconfig/settingform/index.jsx
+++ b/src/templates/modalconfig/settingform/index.jsx
@@ -13,28 +13,19 @@
   }
 
   state = {
-    fields: null
+    fields: null,
+    appType: sessionStorage.getItem('appType')
   }
 
   UNSAFE_componentWillMount () {
     const { config } = this.props
     let fields = []
 
-    if (config.groups.length > 0) {
-      config.groups.forEach(group => {
-        group.sublist.forEach(item => {
-          if (item.field && (item.type === 'text' || item.type === 'number') && item.hidden !== 'true' && item.readonly !== 'true') {
-            fields.push(item)
-          }
-        })
-      })
-    } else if (config.fields.length > 0) {
-      config.fields.forEach(f => {
-        if (f.field && (f.type === 'text' || f.type === 'number') && f.hidden !== 'true' && f.readonly !== 'true') {
-          fields.push(f)
-        }
-      })
-    }
+    config.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
@@ -65,7 +56,7 @@
 
   render() {
     const { config, dict } = this.props
-    const { fields } = this.state
+    const { fields, appType } = this.state
     const { getFieldDecorator } = this.props.form
 
     const formItemLayout = {
@@ -124,16 +115,14 @@
             </Form.Item>
           </Col>
           <Col span={12}>
-            <Form.Item label="鍒楁暟">
-              {getFieldDecorator('cols', {
-                initialValue: config.setting.cols || '2'
+            <Form.Item label="琛ㄥ崟鎺掑垪">
+              {getFieldDecorator('align', {
+                initialValue: config.setting.align || 'left_right'
               })(
-                <Select>
-                  <Select.Option value="1">1鍒�</Select.Option>
-                  <Select.Option value="2">2鍒�</Select.Option>
-                  <Select.Option value="3">3鍒�</Select.Option>
-                  <Select.Option value="4">4鍒�</Select.Option>
-                </Select>
+                <Radio.Group>
+                  <Radio value="left_right">宸﹀彸</Radio>
+                  <Radio value="up_down">涓婁笅</Radio>
+                </Radio.Group>
               )}
             </Form.Item>
           </Col>
@@ -161,7 +150,7 @@
               )}
             </Form.Item>
           </Col>
-          {!this.props.isSubTab ? <Col span={12}>
+          {!this.props.isSubTab && appType !== 'pc' ? <Col span={12}>
             <Form.Item label="鎸傝浇瀵硅薄">
               {getFieldDecorator('container', {
                 initialValue: config.setting.container || 'tab'
diff --git a/src/templates/modalconfig/source.jsx b/src/templates/modalconfig/source.jsx
index 9edd2ae..6e831de 100644
--- a/src/templates/modalconfig/source.jsx
+++ b/src/templates/modalconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 export const BaseConfig = {
   type: 'Modal',
@@ -17,7 +17,6 @@
     display: 'modal'
   },
   tables: [],
-  groups: [],
   fields: [
     {
       origin: true,
@@ -31,6 +30,8 @@
       options: [],
       orderType: 'asc',
       decimal: 0,
+      span: 12,
+      labelwidth: 33.3,
       min: '',
       max: '',
       readonly: 'false',
@@ -47,6 +48,8 @@
       options: [],
       orderType: 'asc',
       decimal: 0,
+      span: 12,
+      labelwidth: 33.3,
       min: '',
       max: '',
       readonly: 'false',
@@ -63,6 +66,8 @@
       options: [],
       orderType: 'asc',
       decimal: 0,
+      span: 12,
+      labelwidth: 33.3,
       min: '',
       max: '',
       readonly: 'false',
@@ -158,12 +163,6 @@
   },
   {
     type: 'form',
-    label: '鎻愮ず',
-    subType: 'hint',
-    url: ''
-  },
-  {
-    type: 'form',
     label: CommonDict['model.form.color'],
     subType: 'color',
     url: ''
@@ -179,6 +178,18 @@
     label: CommonDict['header.form.funcvar'],
     subType: 'funcvar',
     url: ''
+  },
+  {
+    type: 'form',
+    label: '鎻愮ず',
+    subType: 'hint',
+    url: ''
+  },
+  {
+    type: 'form',
+    label: '鍒嗗壊绾�',
+    subType: 'split',
+    url: ''
   }
 ]
 
diff --git a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
index 464e035..1addfe3 100644
--- a/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/actionform/index.jsx
@@ -101,8 +101,6 @@
     if (_opentype === 'outerpage') {
       card.pageTemplate = 'custom'
       _opentype = 'innerpage'
-    } else if (_opentype === 'blank') {
-      _opentype = 'tab'
     }
 
     let _tabs = this.props.tabs.filter(tab => tab.type === 'SubTable')
@@ -130,7 +128,7 @@
             item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
           } else if (_opentype === 'innerpage' && _pageTemplate === 'billprint') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
-          } else if (['innerpage', 'blank', 'tab', 'popview', 'excelIn'].includes(_opentype)) {
+          } else if (['innerpage', 'tab', 'popview', 'excelIn'].includes(_opentype)) {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
           } else if (card.sqlType === 'insert') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
@@ -183,7 +181,7 @@
       } else if (_pageTemplate === 'billprint') {
         _options.push('printTemp')
       }
-    } else if (_opentype === 'blank' || _opentype === 'tab') { // 鏂版爣绛炬垨褰撳墠椤甸潰鏇挎崲
+    } else if (_opentype === 'tab') {         // 鏍囩椤�
       if (_tabTemplate === 'ThdMenu') {
         _options.push('linkmenu')
       }
@@ -210,7 +208,7 @@
       }
     } else if (_opentype !== 'popview') { // 鎵撳紑鏂瑰紡涓嶆槸寮圭獥椤甸潰鏃�
       if (_intertype === 'custom') {
-        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method')
+        _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
         if (_procMode === 'system') {
           _options.push('sql', 'sqlType')
         } else {
@@ -261,7 +259,7 @@
           } else if (value === 'innerpage' && this.state.pageTemplate === 'billprint') {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
             _fieldval.Ot = 'requiredSgl'
-          } else if (['innerpage', 'blank', 'tab', 'popview'].includes(value)) {
+          } else if (['innerpage', 'tab', 'popview'].includes(value)) {
             item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
             _fieldval.Ot = 'requiredSgl'
           } else if (value === 'excelIn') {
@@ -312,7 +310,7 @@
             } else if (openType === 'innerpage' && this.state.pageTemplate === 'billprint') {
               item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl', 'required'].includes(op.value))
               _fieldval.Ot = 'requiredSgl'
-            } else if (['innerpage', 'blank', 'tab', 'popview'].includes(openType)) {
+            } else if (['innerpage', 'tab', 'popview'].includes(openType)) {
               item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
               _fieldval.Ot = 'requiredSgl'
             } else {
@@ -613,7 +611,12 @@
       } else if (item.type === 'radio') {
         fields.push(
           <Col span={12} key={index}>
-            <Form.Item label={item.label}>
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" overlayClassName={item.tooltipClass} title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
               {getFieldDecorator(item.key, {
                 initialValue: item.initVal,
                 rules: [
@@ -665,7 +668,7 @@
                   }
                 ]
               })(
-                <Cascader options={item.options || []} placeholder="" />
+                <Cascader allowClear options={item.options || []} placeholder="" />
               )}
             </Form.Item>
           </Col>
@@ -676,13 +679,13 @@
   }
 
   handleConfirm = () => {
-    const { setting } = this.props
+    const { setting, card } = this.props
     // 琛ㄥ崟鎻愪氦鏃舵鏌ヨ緭鍏ュ�兼槸鍚︽纭�
     return new Promise((resolve, reject) => {
       this.props.form.validateFieldsAndScroll((err, values) => {
         if (!err) {
-          values.uuid = this.props.card.uuid
-          values.verify = this.props.card.verify || null
+          values.uuid = card.uuid
+          values.verify = card.verify || null
 
           if (values.OpenType === 'excelIn') {
             values.position = 'toolbar'
@@ -700,14 +703,18 @@
             values.Ot = 'notRequired'
           } else if (values.OpenType === 'popview' && !values.linkTab) { // 娌℃湁鍏宠仈鏍囩锛堟柊寤烘椂锛夛紝鍒涘缓鏂版爣绛綢d
             values.linkTab = Utils.getuuid()
-            values.createTab = true // 鐢ㄤ簬鏍囪鎸夐挳澶嶅埗鏃讹紝鏄惁澶嶅埗鍘熸湁鏍囩
           } else if (values.OpenType === 'funcbutton') { // 杞崲鎵撳嵃鏃舵墦寮�鏂瑰紡
             values.position = 'toolbar'
           } else if (['pop', 'prompt', 'exec'].includes(values.OpenType) && values.verify) {
-            if (values.Ot === 'requiredOnce' && ['notRequired', 'requiredSgl', 'required'].includes(this.props.card.Ot)) {
+            if ((values.Ot === 'requiredOnce' || card.Ot === 'requiredOnce') && card.Ot !== values.Ot) {
               values.verify.uniques = []
-            } else if (this.props.card.Ot === 'requiredOnce' && ['notRequired', 'requiredSgl', 'required'].includes(values.Ot)) {
-              values.verify.uniques = []
+            }
+            if (card.Ot !== values.Ot) {
+              if (values.Ot === 'notRequired') {
+                values.verify.invalid = 'false'
+              } else if (card.Ot === 'notRequired' && values.Ot !== 'notRequired') {
+                values.verify.invalid = 'true'
+              }
             }
           }
 
diff --git a/src/templates/sharecomponent/actioncomponent/index.jsx b/src/templates/sharecomponent/actioncomponent/index.jsx
index 4392c36..fba939a 100644
--- a/src/templates/sharecomponent/actioncomponent/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/index.jsx
@@ -5,8 +5,7 @@
 import moment from 'moment'
 
 import Api from '@/api'
-import Utils from '@/utils/utils.js'
-import DevUtils from '@/utils/devutils.js'
+import Utils, { FuncUtils } from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 import { getActionForm } from '@/templates/zshare/formconfig'
@@ -15,6 +14,7 @@
 import ActionForm from './actionform'
 import CreateFunc from '@/templates/zshare/createfunc'
 import CreateInterface from '@/templates/zshare/createinterface'
+import { updateForm } from '@/utils/utils-update.js'
 import DragElement from './dragaction'
 import './index.scss'
 
@@ -35,7 +35,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     card: null,          // 缂栬緫涓厓绱�
     formlist: null,      // 琛ㄥ崟淇℃伅
     actionlist: null,    // 鎸夐挳缁�
@@ -263,12 +263,12 @@
       new Promise(resolve => {
         if (
           !card.originCard ||
-          (btn.OpenType === 'pop' && card.originCard.OpenType !== 'pop') ||
-          (['tab', 'blank'].includes(btn.OpenType) && !['tab', 'blank'].includes(card.originCard.OpenType)) ||
-          (btn.OpenType === 'popview' && (!btn.createTab || card.originCard.OpenType !== 'popview' || !card.originCard.linkTab))
+          (btn.OpenType !== card.originCard.OpenType) ||
+          (btn.OpenType === 'tab' && btn.tabTemplate !== 'FormTab') ||
+          (btn.OpenType === 'funcbutton' && btn.execMode !== 'pop')
         ) { // 鎸夐挳涓嶆槸澶嶅埗锛屾垨鎸夐挳鍓嶅悗绫诲瀷涓嶄竴鑷存椂锛岀洿鎺ヤ繚瀛�
           resolve('save')
-        } else if (btn.OpenType === 'pop' || btn.OpenType === 'tab' || btn.OpenType === 'blank') {
+        } else if (btn.OpenType === 'pop' || btn.OpenType === 'tab' || btn.execMode === 'pop') {
           resolve('subconf')
         } else if (btn.OpenType === 'popview') {
           resolve('subtab')
@@ -298,20 +298,21 @@
             }
           }
 
+          if (!_LongParam) return 'save'
+
           let _temp = '' // 閰嶇疆淇℃伅绫诲瀷
 
           // 淇敼妯℃�佹鏍囬鍚嶇О
-          if (btn.OpenType === 'pop' && _LongParam && _LongParam.type === 'Modal') {
+          if ((btn.OpenType === 'pop' || btn.execMode === 'pop') && _LongParam.type === 'Modal') {
             try {
               _LongParam.setting.title = btn.label
               _LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_LongParam)))
               _temp = 'Modal'
             } catch {
               console.warn('Stringify Failure')
-              _LongParam = ''
               _temp = ''
             }
-          } else if (['tab', 'blank'].includes(btn.OpenType) && _LongParam && _LongParam.type === 'FormTab') {
+          } else if (btn.OpenType === 'tab' && _LongParam.type === 'FormTab') {
             try {
               _LongParam.action = _LongParam.action.map(_btn => {
                 _btn.uuid = Utils.getuuid()
@@ -329,7 +330,6 @@
               _temp = 'FormTab'
             } catch {
               console.warn('Stringify Failure')
-              _LongParam = ''
               _temp = ''
             }
           }
@@ -485,7 +485,7 @@
       _tab.action = _tab.action.map((item, index) => {
         let uuid = Utils.getuuid()
 
-        if (item.OpenType === 'pop') {
+        if (item.OpenType === 'pop' || item.execMode === 'pop') {
           _oriActions.push({
             prebtn: JSON.parse(JSON.stringify(item)),
             curuuid: uuid,
@@ -745,23 +745,16 @@
             }
 
             if (_LongParam) {
-              let fields = []
-              if (_LongParam.groups.length > 0) {
-                _LongParam.groups.forEach(group => {
-                  fields = [...fields, ...group.sublist]
-                })
-              } else {
-                fields = _LongParam.fields
-              }
+              _LongParam = updateForm(_LongParam)
 
               let _param = {
                 funcName: btn.innerFunc,
                 name: _config.setting.tableName || '',
-                fields: fields,
+                fields: _LongParam.fields,
                 menuNo: menu.MenuNo
               }
-              newLText = Utils.formatOptions(DevUtils.getfunc(_param, btn, menu, _config))
-              DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+              newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
+              DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
               resolve(true)
             } else {
               notification.warning({
@@ -778,8 +771,8 @@
               funcName: btn.innerFunc,
               menuNo: menu.MenuNo
             }
-            newLText = Utils.formatOptions(DevUtils.getexcelInfunc(_param, btn, menu))
-            DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+            newLText = Utils.formatOptions(FuncUtils.getexcelInfunc(_param, btn, menu))
+            DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
             resolve(true)
           } else {
             notification.warning({
@@ -794,8 +787,8 @@
             innerFunc: btn.innerFunc
           }
 
-          newLText = Utils.formatOptions(DevUtils.getTableFunc(_param, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
-          DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+          newLText = Utils.formatOptions(FuncUtils.getTableFunc(_param, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
+          DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
 
           resolve(true)
         } else {
@@ -805,8 +798,8 @@
             fields: '',
             menuNo: menu.MenuNo
           }
-          newLText = Utils.formatOptions(DevUtils.getfunc(_param, btn, menu, _config))
-          DelText = Utils.formatOptions(DevUtils.dropfunc(btn.innerFunc))
+          newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
+          DelText = Utils.formatOptions(FuncUtils.dropfunc(btn.innerFunc))
           resolve(true)
         }
       }).then(res => {
@@ -838,9 +831,10 @@
   /**
    * @description 鎸夐挳鍙屽嚮瑙﹀彂瀛愰厤缃�
    */
-  btnDoubleClick = (element) => {
-    if (!element.origin && (element.OpenType === 'pop' || element.OpenType === 'popview' || element.OpenType === 'blank' || (element.OpenType === 'tab' && element.tabTemplate === 'FormTab'))) {
-      this.props.setSubConfig(element)
+  btnDoubleClick = (el) => {
+    // execMode === 'pop' 涓� OpenType === 'funcbutton'
+    if (!el.origin && (el.OpenType === 'pop' || el.execMode === 'pop' || el.OpenType === 'popview' || (el.OpenType === 'tab' && el.tabTemplate === 'FormTab'))) {
+      this.props.setSubConfig(el)
     } else {
       notification.warning({
         top: 92,
@@ -915,7 +909,6 @@
           visible={profVisible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.verifySubmit}
           onCancel={() => { this.setState({ profVisible: false }) }}
diff --git a/src/templates/sharecomponent/actioncomponent/index.scss b/src/templates/sharecomponent/actioncomponent/index.scss
index 2226cde..d5643e2 100644
--- a/src/templates/sharecomponent/actioncomponent/index.scss
+++ b/src/templates/sharecomponent/actioncomponent/index.scss
@@ -49,7 +49,7 @@
       min-height: calc(100vh - 300px);
       overflow-y: auto;
       .ant-empty {
-        margin: 15vh 8px;
+        margin: 5vh 8px;
       }
     }
     .ant-modal-body::-webkit-scrollbar {
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx
index 3268132..26c87fe 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),@ErrorCode nvarchar(50), @retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50), @retmsg nvarchar(4000),@tbid Nvarchar(512)
       Select @ErrorCode='', @retmsg=''
     `
     
@@ -147,6 +147,25 @@
           }
         })
 
+        if (!values.uuid) {
+          if (values.position === 'init') {
+            _initCustomScript += `
+            /* 鍒濆鍖栬剼鏈� */
+            ${values.sql}
+            `
+          } else if (values.position === 'front') {
+            _prevCustomScript += `
+            /* 榛樿sql鍓嶈剼鏈� */
+            ${values.sql}
+            `
+          } else {
+            _backCustomScript += `
+            /* 榛樿sql鍚庤剼鏈� */
+            ${values.sql}
+            `
+          }
+        }
+
         let param = {
           func: 's_debug_sql',
           exec_type: 'y',
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
index adca1a9..d5b0024 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx
@@ -673,9 +673,7 @@
           if (this.scriptsForm && this.scriptsForm.state.editItem) {
             _loading = true
             this.setState({activeKey: 'scripts'})
-          }
-
-          if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+          } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
             _loading = true
             this.setState({activeKey: 'scripts'})
           }
diff --git a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
index e71b802..8fb4902 100644
--- a/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
+++ b/src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx
@@ -50,6 +50,25 @@
         max: 200,
         editable: true,
         width: '25%'
+      },
+      {
+        title: '鍙栫粷瀵瑰��',
+        dataIndex: 'abs',
+        inputType: 'select',
+        editable: true,
+        required: false,
+        width: '25%',
+        render: (text) => {
+          if (text === 'true') {
+            return '鏄�'
+          } else {
+            return '鍚�'
+          }
+        },
+        options: [
+          {value: 'true', text: '鏄�'},
+          {value: 'false', text: '鍚�'}
+        ]
       }
     ]
   }
@@ -64,21 +83,6 @@
 
     _verify.enable = _verify.enable || 'false'
     _verify.columns = _verify.columns || []
-
-    // 鍚屾鏄剧ず鍒�
-    // if (!_verify.columns || _verify.columns.length === 0) {
-    //   _verify.columns = []
-    //   config.columns.forEach(item => {
-    //     if (!item.field) return
-  
-    //     _verify.columns.push({
-    //       Column: item.field,
-    //       Text: item.label,
-    //       Width: 20,
-    //       uuid: Utils.getuuid()
-    //     })
-    //   })
-    // }
 
     if (card.intertype !== 'system') {
       _verify.enable = 'false'
@@ -159,6 +163,7 @@
       return
     }
     values.uuid = Utils.getuuid()
+    values.abs = 'false'
     verify.columns.push(values)
 
     this.setState({
@@ -364,6 +369,7 @@
         Column: item.field,
         Text: item.label,
         Width: 20,
+        abs: 'false',
         uuid: Utils.getuuid()
       })
     })
diff --git a/src/templates/sharecomponent/cardcomponent/index.jsx b/src/templates/sharecomponent/cardcomponent/index.jsx
index 4e4dd82..f893b7c 100644
--- a/src/templates/sharecomponent/cardcomponent/index.jsx
+++ b/src/templates/sharecomponent/cardcomponent/index.jsx
@@ -23,7 +23,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     modaltype: '',
     formlist: null,
     cardwidth: 0,
diff --git a/src/templates/sharecomponent/chartcomponent/index.jsx b/src/templates/sharecomponent/chartcomponent/index.jsx
index da2b46f..8c356fb 100644
--- a/src/templates/sharecomponent/chartcomponent/index.jsx
+++ b/src/templates/sharecomponent/chartcomponent/index.jsx
@@ -17,7 +17,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     visible: true
   }
 
diff --git a/src/templates/sharecomponent/chartgroupcomponent/index.jsx b/src/templates/sharecomponent/chartgroupcomponent/index.jsx
index 8596cd8..1e2117b 100644
--- a/src/templates/sharecomponent/chartgroupcomponent/index.jsx
+++ b/src/templates/sharecomponent/chartgroupcomponent/index.jsx
@@ -21,7 +21,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     chartlist: null,      // 鍥捐〃闆�
     card: null,           // 缂栬緫涓厓绱�
     formlist: null,       // 琛ㄥ崟淇℃伅
diff --git a/src/templates/sharecomponent/columncomponent/columnform/index.jsx b/src/templates/sharecomponent/columncomponent/columnform/index.jsx
index a955bd2..8bfffcb 100644
--- a/src/templates/sharecomponent/columncomponent/columnform/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/columnform/index.jsx
@@ -5,6 +5,7 @@
 import { formRule } from '@/utils/option.js'
 import './index.scss'
 
+const { TextArea } = Input
 const columnTypeOptions = {
   text: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'prefix', 'postfix', 'textFormat', 'fieldlength', 'blacklist', 'perspective', 'rowspan'],
   number: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'decimal', 'format', 'prefix', 'postfix', 'blacklist', 'perspective', 'sum', 'rowspan'],
@@ -34,10 +35,10 @@
 
     let _options = JSON.parse(JSON.stringify(columnTypeOptions[card.type]))
     if (card.type === 'text' || card.type === 'number') {
-      if (card.perspective !== 'linkurl') {
-        _options.push('linkmenu')
-      } else {
-        _options.push('linkurl')
+      if (card.perspective === 'linkmenu') {
+        _options.push('linkmenu', 'linkfields')
+      } else if (card.perspective === 'linkurl') {
+        _options.push('linkurl', 'linkfields')
       }
     }
 
@@ -74,10 +75,10 @@
       let _options = JSON.parse(JSON.stringify(columnTypeOptions[value]))
 
       if (card.type === 'text' || card.type === 'number') {
-        if (card.perspective !== 'linkurl') {
-          _options.push('linkmenu')
-        } else {
-          _options.push('linkurl')
+        if (card.perspective === 'linkmenu') {
+          _options.push('linkmenu', 'linkfields')
+        } else if (card.perspective === 'linkurl') {
+          _options.push('linkurl', 'linkfields')
         }
       }
 
@@ -107,11 +108,10 @@
   changeRadio = (key, value) => {
     if (key === 'perspective') {
       let _options = JSON.parse(JSON.stringify(columnTypeOptions[this.state.type]))
-
-      if (value !== 'linkurl') {
-        _options.push('linkmenu')
-      } else {
-        _options.push('linkurl')
+      if (value === 'linkmenu') {
+        _options.push('linkmenu', 'linkfields')
+      } else if (value === 'linkurl') {
+        _options.push('linkurl', 'linkfields')
       }
 
       this.setState({
@@ -173,6 +173,27 @@
                   ...rules
                 ]
               })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        )
+      } else if (item.type === 'textarea') { // 鏂囨湰鎼滅储
+        fields.push(
+          <Col span={24} key={index} className="textarea">
+            <Form.Item label={item.tooltip ?
+              <Tooltip placement="topLeft" title={item.tooltip}>
+                <Icon type="question-circle" />
+                {item.label}
+              </Tooltip> : item.label
+            }>
+              {getFieldDecorator(item.key, {
+                initialValue: item.initVal || '',
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.input'] + item.label + '!'
+                  }
+                ]
+              })(<TextArea rows={2} disabled={item.readonly} onPressEnter={this.handleSubmit} />)}
             </Form.Item>
           </Col>
         )
@@ -283,7 +304,13 @@
           <Col span={12} key={index}>
             <Form.Item label={item.label}>
               {getFieldDecorator(item.key, {
-                initialValue: item.initVal || []
+                initialValue: item.initVal || [],
+                rules: [
+                  {
+                    required: !!item.required,
+                    message: this.props.dict['form.required.select'] + item.label + '!'
+                  }
+                ]
               })(
                 <Cascader
                   options={this.state.menulist}
diff --git a/src/templates/sharecomponent/columncomponent/columnform/index.scss b/src/templates/sharecomponent/columncomponent/columnform/index.scss
index fcf59ac..e450762 100644
--- a/src/templates/sharecomponent/columncomponent/columnform/index.scss
+++ b/src/templates/sharecomponent/columncomponent/columnform/index.scss
@@ -16,4 +16,12 @@
       padding-right: 3px;
     }
   }
+  .textarea {
+    .ant-form-item-label {
+      width: 12%;
+    }
+    .ant-form-item-control-wrapper {
+      width: 88%;
+    }
+  }
 }
diff --git a/src/templates/sharecomponent/columncomponent/index.jsx b/src/templates/sharecomponent/columncomponent/index.jsx
index a43d19b..a37af66 100644
--- a/src/templates/sharecomponent/columncomponent/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/index.jsx
@@ -24,7 +24,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     columnlist: null,    // 鏄剧ず鍒�
     showField: false,    // 鏄剧ず鍒楀瓧娈�
     modaltype: '',       // 妯℃�佹鎺у埗
@@ -73,6 +73,7 @@
    */
   handleColumn = (card) => {
     const { menu } = this.props
+    const { columnlist } = this.state
 
     if (card.type !== 'colspan') {
       let menulist = []
@@ -99,11 +100,20 @@
           return fst
         })
       }
+
+      let fields = []
+      columnlist.forEach(col => {
+        if (!col.field) return
+        fields.push({
+          value: col.field,
+          text: col.label
+        })
+      })
       
       this.setState({
         modaltype: 'columns',
         card: card,
-        formlist: getColumnForm(card, menulist)
+        formlist: getColumnForm(card, menulist, fields)
       })
     } else {
       this.setState({
@@ -176,7 +186,7 @@
         _columnlist = _columnlist.filter(item => !item.origin || item.uuid === res.uuid) // 鍘婚櫎鍒濆鍒�
         _columnlist = _columnlist.map(item => {
           if (item.uuid !== res.uuid && res.field && item.field) {
-            if (item.field === res.field) {
+            if (item.field.toLowerCase() === res.field.toLowerCase()) {
               fieldrepet = true
             }
           }
@@ -549,7 +559,6 @@
           visible={modaltype === 'mark'}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.markSubmit}
           onCancel={() => { this.setState({ modaltype: '' }) }}
diff --git a/src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx b/src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx
index de77471..4fbbbac 100644
--- a/src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx
+++ b/src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx
@@ -235,7 +235,7 @@
                 <Select>
                   <Select.Option value="="> = </Select.Option>
                   <Select.Option value="!="> != </Select.Option>
-                  <Select.Option value=">"> > </Select.Option>
+                  <Select.Option value=">"> &gt; </Select.Option>
                   <Select.Option value="<"> &lt; </Select.Option>
                   <Select.Option value="like"> like </Select.Option>
                 </Select>
diff --git a/src/templates/sharecomponent/fieldscomponent/index.jsx b/src/templates/sharecomponent/fieldscomponent/index.jsx
index 0baed4a..8447dd0 100644
--- a/src/templates/sharecomponent/fieldscomponent/index.jsx
+++ b/src/templates/sharecomponent/fieldscomponent/index.jsx
@@ -14,20 +14,19 @@
   static propTpyes = {
     type: PropTypes.string,          // 鎼滅储鏉′欢娣诲姞銆佹樉绀哄垪娣诲姞
     config: PropTypes.object,        // 瀹瑰櫒Id
-    tableFields: PropTypes.string,   // 宸查�夎〃瀛楁闆�
     updatefield: PropTypes.func
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     fields: [],          // 瀛楁闆�
     tableVisible: false,    // 妯℃�佹鎺у埗
   }
 
   queryField = () => {
-    const { type, config, tableFields } = this.props
+    const { type, config } = this.props
     // 鍒ゆ柇鏄惁宸查�夋嫨琛ㄥ悕
-    if (!tableFields || tableFields.length === 0) {
+    if (!window.GLOB.tableFields || window.GLOB.tableFields.length === 0) {
       notification.warning({
         top: 92,
         message: '璇烽�夋嫨琛ㄥ悕锛�',
@@ -38,7 +37,7 @@
 
     // 琛ㄥ瓧娈甸泦杞负map鏁版嵁
     let columns = new Map()
-    tableFields.forEach(table => {
+    window.GLOB.tableFields.forEach(table => {
       table.columns.forEach(column => {
         columns.set(column.field.toLowerCase(), column)
       })
@@ -68,23 +67,12 @@
         }
       })
     } else if (type === 'form') {
-      if (config.groups.length > 1) {
-        config.groups.forEach(group => {
-          group.sublist.forEach(item => {
-            if (columns.has(item.field.toLowerCase())) {
-              let _datatype = columns.get(item.field.toLowerCase()).datatype
-              columns.set(item.field.toLowerCase(), {...item, selected: true, datatype: _datatype})
-            }
-          })
-        })
-      } else {
-        config.fields.forEach(item => {
-          if (columns.has(item.field.toLowerCase())) {
-            let _datatype = columns.get(item.field.toLowerCase()).datatype
-            columns.set(item.field.toLowerCase(), {...item, selected: true, datatype: _datatype})
-          }
-        })
-      }
+      config.fields.forEach(item => {
+        if (item.field && columns.has(item.field.toLowerCase())) {
+          let _datatype = columns.get(item.field.toLowerCase()).datatype
+          columns.set(item.field.toLowerCase(), {...item, selected: true, datatype: _datatype})
+        }
+      })
     }
 
     // 鏄剧ず瀛楁闆嗗脊绐�
@@ -240,32 +228,30 @@
         }
       })
     } else if (type === 'form') {
-      if (config.groups.length > 1) {
-        config.groups.forEach(group => {
-          let _items = []
-          group.sublist.forEach(item => {
-            if (columnsMap.has(item.field.toLowerCase())) {
-              let cell = columnsMap.get(item.field.toLowerCase())
-      
-              if (cell.selected && cell.type === item.type) { // 鏁版嵁閫夋嫨鐘舵�佸強绫诲瀷鏈慨鏀规椂锛岀洿鎺ユ坊鍔�
-                _items.push(item)
-              } else if (cell.selected) {                     // 鏁版嵁绫诲瀷淇敼鏃讹紝閲嶇疆绫诲瀷鍙婂垵濮嬪��
-                item.type = cell.type
-                item.initval = ''
-                _items.push(item)
-              }
-              columnsMap.delete(item.field.toLowerCase())
-            } else if (!item.origin) {                        // 杩囨护绀轰緥椤�
-              _items.push(item)
-            }
-          })
-          group.sublist = _items
-        })
-        
-        let _columns = [...columnsMap.values()]
-    
-        let _additems = _columns.map(item => { // 寰幆娣诲姞鏂板瀛楁
-          return {
+      config.fields.forEach(item => {
+        if (item.field && columnsMap.has(item.field.toLowerCase())) {
+          let cell = columnsMap.get(item.field.toLowerCase())
+  
+          if (cell.selected && cell.type === item.type) { // 鏁版嵁閫夋嫨鐘舵�佸強绫诲瀷鏈慨鏀规椂锛岀洿鎺ユ坊鍔�
+            items.push(item)
+          } else if (cell.selected) {                     // 鏁版嵁绫诲瀷淇敼鏃讹紝閲嶇疆绫诲瀷鍙婂垵濮嬪��
+            item.type = cell.type
+            item.initval = ''
+            items.push(item)
+          }
+          columnsMap.delete(item.field.toLowerCase())
+        } else if (!item.origin) {                        // 杩囨护绀轰緥椤�
+          items.push(item)
+        }
+      })
+  
+      let _columns = [...columnsMap.values()]
+      let lastItem = config.fields[config.fields.length - 1]
+      let span = lastItem ? lastItem.span || 12 : 12
+
+      _columns.forEach(item => { // 寰幆娣诲姞鏂板瀛楁
+        if (item.selected) {
+          let newcard = {
             uuid: Utils.getuuid(),
             label: item.label,
             field: item.field,
@@ -273,59 +259,21 @@
             type: item.type,
             resourceType: '0',
             setAll: 'false',
+            span: span,
+            labelwidth: 33.3,
             options: [],
             dataSource: '',
-            orderType: 'asc',
             decimal: item.decimal,
+            orderType: 'asc',
             readonly: 'false',
             required: 'true'
           }
-        })
-        config.groups[config.groups.length - 1].sublist = [...config.groups.slice(-1)[0].sublist, ..._additems]
-      } else {
-        config.fields.forEach(item => {
-          if (columnsMap.has(item.field.toLowerCase())) {
-            let cell = columnsMap.get(item.field.toLowerCase())
-    
-            if (cell.selected && cell.type === item.type) { // 鏁版嵁閫夋嫨鐘舵�佸強绫诲瀷鏈慨鏀规椂锛岀洿鎺ユ坊鍔�
-              items.push(item)
-            } else if (cell.selected) {                     // 鏁版嵁绫诲瀷淇敼鏃讹紝閲嶇疆绫诲瀷鍙婂垵濮嬪��
-              item.type = cell.type
-              item.initval = ''
-              items.push(item)
-            }
-            columnsMap.delete(item.field.toLowerCase())
-          } else if (!item.origin) {                        // 杩囨护绀轰緥椤�
-            items.push(item)
-          }
-        })
-    
-        let _columns = [...columnsMap.values()]
-    
-        _columns.forEach(item => { // 寰幆娣诲姞鏂板瀛楁
-          if (item.selected) {
-            let newcard = {
-              uuid: Utils.getuuid(),
-              label: item.label,
-              field: item.field,
-              initval: '',
-              type: item.type,
-              resourceType: '0',
-              setAll: 'false',
-              options: [],
-              dataSource: '',
-              decimal: item.decimal,
-              orderType: 'asc',
-              readonly: 'false',
-              required: 'true'
-            }
-    
-            items.push(newcard)
-          }
-        })
-    
-        config.fields = items
-      }
+  
+          items.push(newcard)
+        }
+      })
+  
+      config.fields = items
     }
 
     if (type === 'search' || type === 'columns' || type === 'form') {
@@ -374,7 +322,6 @@
           visible={this.state.tableVisible}
           width={'65vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           cancelText={dict['model.close']}
           onOk={this.addFieldSubmit}
           onCancel={() => { // 鍙栨秷娣诲姞
diff --git a/src/templates/sharecomponent/searchcomponent/index.jsx b/src/templates/sharecomponent/searchcomponent/index.jsx
index ae43ac0..c17f639 100644
--- a/src/templates/sharecomponent/searchcomponent/index.jsx
+++ b/src/templates/sharecomponent/searchcomponent/index.jsx
@@ -23,7 +23,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     searchlist: null,    // 鎼滅储鏉′欢闆�
     sqlVerifing: false,  // sql楠岃瘉涓�
     visible: false,      // 妯℃�佹鎺у埗
diff --git a/src/templates/sharecomponent/settingcalcomponent/index.jsx b/src/templates/sharecomponent/settingcalcomponent/index.jsx
index 2f641c6..416e27d 100644
--- a/src/templates/sharecomponent/settingcalcomponent/index.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/index.jsx
@@ -12,12 +12,11 @@
   static propTpyes = {
     config: PropTypes.any,
     MenuID: PropTypes.string,
-    tableFields: PropTypes.any,
     updateConfig: PropTypes.func
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     sourcelist: [],
     visible: false,
     loading: false,
@@ -66,7 +65,6 @@
           visible={visible}
           width={'75vw'}
           maskClosable={false}
-          style={{minWidth: '900px', maxWidth: '1200px'}}
           okText={dict['model.submit']}
           onOk={this.verifySubmit}
           confirmLoading={loading}
@@ -76,7 +74,6 @@
           <VerifyCard
             dict={dict}
             config={config}
-            tableFields={this.props.tableFields}
             menuId={this.props.config.uuid}
             searches={config.search}
             wrappedComponentRef={(inst) => this.verifyRef = inst}
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx b/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
index 38a6001..065406a 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx
@@ -21,7 +21,6 @@
 class VerifyCard extends Component {
   static propTpyes = {
     dict: PropTypes.object,          // 瀛楀吀椤�
-    tableFields: PropTypes.any,      // 鏁版嵁婧愪俊鎭�
     config: PropTypes.object,        // 鏁版嵁婧愪俊鎭�
     menuId: PropTypes.string,        // 鑿滃崟Id
     searches: PropTypes.array,       // 鎼滅储鏉′欢
@@ -176,7 +175,7 @@
       let _loading = false
       if (this.scriptsForm && this.scriptsForm.state.editItem) {
         _loading = true
-      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
         _loading = true
       }
 
@@ -224,7 +223,7 @@
         let _loading = false
         if (this.scriptsForm && this.scriptsForm.state.editItem) {
           _loading = true
-        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
           _loading = true
         }
 
@@ -279,7 +278,7 @@
       let param = {
         func: 's_debug_sql',
         exec_type: 'y',
-        LText: SettingUtils.getDebugSql(setting, _scripts, columns, Utils.getRegOptions(searches), config.calendar)
+        LText: SettingUtils.getDebugSql(setting, _scripts, columns, Utils.getRegOptions(searches), config.calendar, config.urlFields)
       }
       param.LText = Utils.formatOptions(param.LText)
       param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
@@ -353,7 +352,6 @@
             <FieldsComponent
               config={{...this.props.config, columns}}
               type="fields"
-              tableFields={this.props.tableFields}
               updatefield={this.updatefields}
             />
             <Table
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss b/src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss
index fcd8fff..68adfba 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss
@@ -18,5 +18,7 @@
       margin-right: 3px;
     }
   }
-
+  .ant-radio-group {
+    white-space: nowrap;
+  }
 }
\ No newline at end of file
diff --git a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
index d690943..da2b30d 100644
--- a/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
+++ b/src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx
@@ -7,7 +7,7 @@
    * @return {Object}  setting       椤甸潰璁剧疆
    * @return {Array}   columns       鏄剧ず瀛楁
    */
-  static getDebugSql (setting, scripts, columns, searches, calendar) {
+  static getDebugSql (setting, scripts, columns, searches, calendar, urlFields = []) {
     let sql = ''
     let _dataresource = ''
     let _customScript = ''
@@ -22,27 +22,28 @@
     }
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
 
     if (setting.execute !== 'false') {
-      _dataresource = setting.dataresource
+      _dataresource = setting.dataresource || ''
     }
     
-    if (_dataresource) {
-      _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
-    }
-    if (_customScript) {
-      _customScript = _customScript.replace(/@\$|\$@/ig, '')
-    }
+    _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
+    _customScript = _customScript.replace(/@\$|\$@/ig, '')
     
     // 澶栬仈鏁版嵁搴撴浛鎹�
     if (window.GLOB.externalDatabase !== null) {
       _dataresource = _dataresource.replace(/@db@/ig, window.GLOB.externalDatabase)
       _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
     }
+
+    urlFields.forEach(field => {
+      _dataresource = _dataresource.replace(new RegExp('@' + field + '@', 'ig'), '0')
+      _customScript = _customScript.replace(new RegExp('@' + field + '@', 'ig'), '0')
+    })
     
     // 姝e垯鏇挎崲
     let _regoptions = []
@@ -62,13 +63,23 @@
       _regoptions.push({
         reg: new RegExp('@calendarDate@', 'ig'),
         value: `'1970-01-01 00:00:00.000'`
-      })
-      _regoptions.push({
+      }, {
         reg: new RegExp('@calendarDate1@', 'ig'),
         value: `'2030-12-31 23:59:59.999'`
       })
     }
 
+    _regoptions.push({
+      reg: new RegExp('@login_city@', 'ig'),
+      value: `''`
+    }, {
+      reg: new RegExp('@userName@', 'ig'),
+      value: `''`
+    }, {
+      reg: new RegExp('@fullName@', 'ig'),
+      value: `''`
+    })
+
     if ((setting.queryType === 'statistics' || calendar.refresh === 'true') && _dataresource) {
       _regoptions.forEach(item => {
         _dataresource = _dataresource.replace(item.reg, item.value)
diff --git a/src/templates/sharecomponent/settingcomponent/index.jsx b/src/templates/sharecomponent/settingcomponent/index.jsx
index c96f5c3..bad5664 100644
--- a/src/templates/sharecomponent/settingcomponent/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/index.jsx
@@ -3,8 +3,7 @@
 import { is, fromJS } from 'immutable'
 import { Icon, Modal, Button } from 'antd'
 
-import Utils from '@/utils/utils.js'
-import DevUtils from '@/utils/devutils.js'
+import Utils, { FuncUtils } from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
@@ -23,7 +22,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     menu: null,          // 鑿滃崟淇℃伅
     search: null,        // 鎼滅储鏉′欢锛屽寘鎷富琛ㄦ悳绱�
     formlist: null,      // 琛ㄥ崟淇℃伅
@@ -85,8 +84,8 @@
 
     this.settingRef.handleConfirm('func').then(setting => {
       let _config = {...config, setting: setting}
-      let newLText = Utils.formatOptions(DevUtils.getTableFunc(setting, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
-      let DelText = Utils.formatOptions(DevUtils.dropfunc(setting.innerFunc))          // 鍒犻櫎瀛樺偍杩囩▼sql
+      let newLText = Utils.formatOptions(FuncUtils.getTableFunc(setting, menu, _config)) // 鍒涘缓瀛樺偍杩囩▼sql
+      let DelText = Utils.formatOptions(FuncUtils.dropfunc(setting.innerFunc))          // 鍒犻櫎瀛樺偍杩囩▼sql
 
       this.refs.funcCreatComponent.exec(setting.innerFunc, newLText, DelText).then(result => {
         if (result === 'success') {
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
index e303c63..27b3f4d 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx
@@ -465,7 +465,7 @@
             <Col span={12}>
               <Form.Item label="榛樿鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || '',
+                  initialValue: setting.order || 'ID desc',
                   rules: [
                     {
                       required: true,
@@ -524,6 +524,22 @@
               </Form.Item>
             </Col> : null}
             {interType === 'custom' ? <Col span={12}>
+              <Form.Item label={
+                <Tooltip placement="topLeft" title={'濡傛灉鑷畾涔夋帴鍙d笉鏀寔璺ㄥ煙璇锋眰锛屼細閫氳繃褰撳墠绯荤粺杞彂銆�'}>
+                  <Icon type="question-circle" />
+                  璺ㄥ煙璇锋眰
+                </Tooltip>
+              }>
+                {getFieldDecorator('cross', {
+                  initialValue: setting.cross || 'true'
+                })(
+                <Radio.Group>
+                  <Radio value="true">鏀寔</Radio>
+                  <Radio value="false">涓嶆敮鎸�</Radio>
+                </Radio.Group>)}
+              </Form.Item>
+            </Col> : null}
+            {interType === 'custom' ? <Col span={12}>
               <Form.Item label="鎵ц娆℃暟">
                 {getFieldDecorator('execTime', {
                   initialValue: setting.execTime || 'once'
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
index f6474c0..a66ce20 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/index.jsx
@@ -85,12 +85,23 @@
     status.requestMode = status.requestMode || 'system'
     status.procMode = status.procMode || 'script'
     status.callbackType = status.callbackType || 'script'
+    let regoptions = Utils.getRegOptions(search)
+
+    if (config.urlFields && config.urlFields.length > 0) {
+      config.urlFields.forEach(field => {
+        regoptions.push({
+          key: field,
+          value: '0',
+          type: 'url'
+        })
+      })
+    }
 
     this.setState({
       setting: _setting,
       search: _search,
       arr_field: arr_field.join(','),
-      regoptions: Utils.getRegOptions(search), // 鎼滅储鏉′欢锛屾鍒欐浛鎹�
+      regoptions: regoptions, // 鎼滅储鏉′欢锛屾鍒欐浛鎹�
       columns: columns,
       scripts: _scripts,
       preScripts: _preScripts,
@@ -157,11 +168,11 @@
     const { activeKey, setting, scripts, preScripts, cbScripts } = this.state
 
     let _loading = false
-    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.preScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
     }
 
@@ -293,11 +304,11 @@
     const { activeKey, search, arr_field, setting } = this.state
 
     let _loading = false
-    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+    if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.preScriptsForm && this.preScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.preScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
-    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql')) {
+    } else if (this.cbScriptsForm && this.cbScriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.cbScriptsForm.props.form.getFieldValue('sql'))) {
       _loading = true
     }
 
diff --git a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
index 3a5c5d5..1096b9c 100644
--- a/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
+++ b/src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx
@@ -415,24 +415,24 @@
       <div className="modal-menu-setting-script">
         <Form {...formItemLayout}>
           <Row gutter={24}>
-            <Col span={8}>
-              <Form.Item label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
+            <Col span={4}>
+              <Form.Item labelCol={{span: 17}} wrapperCol={{span: 7}} label={'鍥炶皟琛ㄥ悕'} style={{whiteSpace: 'nowrap', margin: 0}}>
                 {setting.cbTable}
               </Form.Item>
             </Col>
-            <Col span={16}>
-              <Form.Item label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
-                ErrorCode, retmsg
+            <Col span={20}>
+              <Form.Item labelCol={{span: 4}} wrapperCol={{span: 20}} label={'鎶ラ敊瀛楁'} style={{margin: 0}}>
+                ErrorCode锛堝鍔犲悗缂�NT琛ㄧず鏁版嵁涓嶅洖婊氾紝濡侲NT銆丯NT銆丗NT銆丯MNT锛�, retmsg
               </Form.Item>
             </Col>
             {usefulFields ? <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                id, bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id{usefulFields ? ', ' + usefulFields : ''}
+                id, bid, loginuid, sessionuid, userid, username, fullname, 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, appkey, time_id
+                id, bid, loginuid, sessionuid, userid, username, fullname, 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 09f0df4..2801901 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) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
@@ -43,11 +43,21 @@
     let _regoptions = regoptions.map(item => {
       return {
         reg: new RegExp('@' + item.key + '@', 'ig'),
-        value: `'0'`
+        value: `'0'`,
+        type: item.type || ''
       }
     })
 
     _regoptions.push({
+      reg: new RegExp('@login_city@', 'ig'),
+      value: `''`
+    }, {
+      reg: new RegExp('@userName@', 'ig'),
+      value: `''`
+    }, {
+      reg: new RegExp('@fullName@', 'ig'),
+      value: `''`
+    }, {
       reg: new RegExp('@orderBy@', 'ig'),
       value: setting.order
     }, {
@@ -65,6 +75,11 @@
         _dataresource = _dataresource.replace(item.reg, item.value)
       })
       _search = ''
+    } else if (_dataresource) {
+      _regoptions.forEach(item => {
+        if (item.type !== 'url') return
+        _dataresource = _dataresource.replace(item.reg, item.value)
+      })
     }
 
     if (_customScript) {
@@ -118,13 +133,13 @@
     })
 
     if (_customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
 
     _customScript = _customScript.replace(/@\$|\$@/ig, '')
-    _customScript = _customScript.replace(/@userName@|@fullName@/ig, `''`)
+    _customScript = _customScript.replace(/@userName@|@fullName@|@login_city@/ig, `''`)
     // 澶栬仈鏁版嵁搴撴浛鎹�
     if (window.GLOB.externalDatabase !== null) {
       _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
diff --git a/src/templates/sharecomponent/tablecomponent/index.jsx b/src/templates/sharecomponent/tablecomponent/index.jsx
index 04a1a64..e52ef08 100644
--- a/src/templates/sharecomponent/tablecomponent/index.jsx
+++ b/src/templates/sharecomponent/tablecomponent/index.jsx
@@ -23,7 +23,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     tables: [],          // 绯荤粺琛�
     tableFields: [],     // 宸查�夎〃瀛楁闆�
     selectedTables: [],  // 宸查�夎〃
@@ -153,8 +153,7 @@
       this.setState({
         tableFields: _columns
       })
-
-      this.props.updatetable(this.props.config, _columns)
+      window.GLOB.tableFields = _columns
     })
   }
 
@@ -183,7 +182,11 @@
               let _decimal = 0
               let _length = 50
               if (/^nvarchar/.test(_type)) {
-                _length = +_type.match(/\d+/)[0] || 50
+                try { // 瀛樺湪max
+                  _length = +_type.match(/\d+/)[0] || 50
+                } catch {
+                  _length = 2048
+                }
                 _type = 'text'
               } else if (/^int/.test(_type)) {
                 _type = 'number'
@@ -209,11 +212,16 @@
               }
             })
           }
+
+          let _columns = [...tableFields, tabmsg]
+
           this.setState({
-            tableFields: [...tableFields, tabmsg]
+            tableFields: _columns
           })
 
-          this.props.updatetable(_config, [...tableFields, tabmsg])
+          window.GLOB.tableFields = _columns
+
+          this.props.updatetable(_config)
         } else {
           notification.warning({
             top: 92,
@@ -240,7 +248,9 @@
       tableFields: _fields
     })
 
-    this.props.updatetable({...config, tables: _tables}, _fields)
+    window.GLOB.tableFields = _fields
+
+    this.props.updatetable({...config, tables: _tables})
   }
 
   /**
diff --git a/src/templates/sharecomponent/tabscomponent/index.jsx b/src/templates/sharecomponent/tabscomponent/index.jsx
index 901b1b0..b3dff40 100644
--- a/src/templates/sharecomponent/tabscomponent/index.jsx
+++ b/src/templates/sharecomponent/tabscomponent/index.jsx
@@ -23,7 +23,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     tabgroups: [],     // 鏍囩缁�
     card: [],          // 缂栬緫鏍囩
     group: [],         // 缂栬緫缁�
diff --git a/src/templates/sharecomponent/treesettingcomponent/index.jsx b/src/templates/sharecomponent/treesettingcomponent/index.jsx
index 9f99261..359d52f 100644
--- a/src/templates/sharecomponent/treesettingcomponent/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/index.jsx
@@ -16,7 +16,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     menu: null,          // 鑿滃崟淇℃伅
     visible: false,      // 妯℃�佹鎺у埗
     loading: false       // 璁剧疆淇℃伅楠岃瘉淇濆瓨涓�
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
index 0e2884c..4d1c6f3 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx
@@ -402,7 +402,7 @@
             <Col span={12}>
               <Form.Item label="鎺掑簭">
                 {getFieldDecorator('order', {
-                  initialValue: setting.order || '',
+                  initialValue: setting.order || 'ID desc',
                   rules: [
                     {
                       required: true,
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
index 38552f7..056813e 100644
--- a/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
+++ b/src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx
@@ -74,7 +74,7 @@
         let _loading = false
         if (this.scriptsForm && this.scriptsForm.state.editItem) {
           _loading = true
-        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+        } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
           _loading = true
         }
   
@@ -192,7 +192,7 @@
       let _loading = false
       if (this.scriptsForm && this.scriptsForm.state.editItem) {
         _loading = true
-      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql')) {
+      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
         _loading = true
       }
 
diff --git a/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx b/src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx
index 3ff821a..721c7c9 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) select @ErrorCode='',@retmsg =''
+      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
         ${_customScript}
       `
     }
diff --git a/src/templates/subtableconfig/index.jsx b/src/templates/subtableconfig/index.jsx
index 7676116..d3bda26 100644
--- a/src/templates/subtableconfig/index.jsx
+++ b/src/templates/subtableconfig/index.jsx
@@ -47,10 +47,9 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,        // 瀛楀吀
     config: null,            // 椤甸潰閰嶇疆
     visible: false,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪锛屾ā鎬佹鏄剧ず鎺у埗
-    tableFields: [],         // 宸查�夎〃瀛楁闆�
     menuloading: false,      // 鑿滃崟淇濆瓨涓�
     menucloseloading: false, // 鑿滃崟鍏抽棴鏃讹紝閫夋嫨淇濆瓨
     loading: false,          // 鍔犺浇涓紝椤甸潰spin
@@ -90,6 +89,11 @@
     if (_config.type === 'user') {
       _config.action = _config.action.map(item => {
         let uuid = Utils.getuuid()
+
+        if (item.linkTab) {
+          item.linkTab = ''
+        }
+
         if (item.OpenType === 'pop') { // 鍚湁瀛愰厤缃」鐨勬寜閽�
           _oriActions.push({
             prebtn: fromJS(item).toJS(),
@@ -550,6 +554,7 @@
         let curBtn = config.action.filter(cell => item.curuuid === cell.uuid)[0] // 鏌ョ湅鍒濆鍖栨寜閽槸鍚﹀瓨鍦�
         if (!curBtn) return
         if (curBtn.OpenType !== item.prebtn.OpenType) return
+        if (curBtn.OpenType === 'funcbutton' && curBtn.execMode !== 'pop') return
 
         oriActions.push({
           prebtn: item.prebtn,
@@ -666,10 +671,10 @@
         let _view = ''
         let _subtab = editSubTab
 
-        if (btn.OpenType === 'pop') {
-          _view = 'Modal'             // 琛ㄥ崟椤甸潰
+        if (btn.OpenType === 'pop' || btn.execMode === 'pop') {
+          _view = 'Modal'      // 琛ㄥ崟椤甸潰
         } else if (btn.OpenType === 'popview') {
-          _view = btn.tabType || 'SubTable' // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
+          _view = 'SubTable'   // 鏂板脊绐楁爣绛炬ā鏉� tabType 灞炴�у凡鍘婚櫎
           _subtab = btn
 
           if (editSubTab) {
@@ -910,32 +915,11 @@
       chartview: _chartview
     })
   }
-  
-  /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
-
-    this.setState({
-      config: config,
-      tableFields: fields ? fields : tableFields
-    })
-  }
-
-  /**
-   * @description 鎵归噺娣诲姞锛屾洿鏂伴厤缃俊鎭�
-   */
-  updatefield = (config) => {
-    this.setState({
-      config: config
-    })
-  }
 
   render () {
     const { activeKey, config, chartview } = this.state
 
-    const confActions = config.action.filter(_action => !_action.origin && ['pop', 'popview', 'blank'].includes(_action.OpenType))
+    const confActions = config.action.filter(_action => !_action.origin && (['pop', 'popview'].includes(_action.OpenType) || (_action.OpenType === 'funcbutton' && _action.execMode === 'pop')))
 
     return (
       <div className="model-subtable-board">
@@ -955,7 +939,7 @@
                 <TableComponent
                   config={config}
                   containerId="subtable-basedata"
-                  updatetable={this.updatetable}
+                  updatetable={this.updateconfig}
                 />
               </Panel>
               {/* 鎼滅储鏉′欢娣诲姞 */}
@@ -968,8 +952,7 @@
                 <FieldsComponent
                   config={config}
                   type="search"
-                  tableFields={this.state.tableFields}
-                  updatefield={this.updatefield}
+                  updatefield={this.updateconfig}
                 />
               </Panel>
               {/* 鎸夐挳娣诲姞 */}
@@ -1012,8 +995,7 @@
                 <FieldsComponent
                   config={config}
                   type="columns"
-                  tableFields={this.state.tableFields}
-                  updatefield={this.updatefield}
+                  updatefield={this.updateconfig}
                 />
               </Panel>
             </Collapse>
diff --git a/src/templates/subtableconfig/source.jsx b/src/templates/subtableconfig/source.jsx
index d58938e..4c69050 100644
--- a/src/templates/subtableconfig/source.jsx
+++ b/src/templates/subtableconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class SubTableBaseData {
   baseConfig = {
diff --git a/src/templates/treepageconfig/index.jsx b/src/templates/treepageconfig/index.jsx
index a01bcd6..8a4b4dd 100644
--- a/src/templates/treepageconfig/index.jsx
+++ b/src/templates/treepageconfig/index.jsx
@@ -33,7 +33,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     config: null,            // 椤甸潰閰嶇疆
     formlist: null,          // 鎼滅储鏉′欢銆佹寜閽�佹樉绀哄垪琛ㄥ崟瀛楁
     menuloading: false,      // 鑿滃崟淇濆瓨涓�
diff --git a/src/templates/treepageconfig/source.jsx b/src/templates/treepageconfig/source.jsx
index b51a979..9cd261f 100644
--- a/src/templates/treepageconfig/source.jsx
+++ b/src/templates/treepageconfig/source.jsx
@@ -2,7 +2,7 @@
 import zhCN from '@/locales/zh-CN/model.js'
 import enUS from '@/locales/en-US/model.js'
 
-const CommonDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class CommonTableBaseData {
   baseConfig = {
diff --git a/src/templates/zshare/createinterface/index.jsx b/src/templates/zshare/createinterface/index.jsx
index 1583cd2..7f40704 100644
--- a/src/templates/zshare/createinterface/index.jsx
+++ b/src/templates/zshare/createinterface/index.jsx
@@ -7,6 +7,7 @@
 import MutilForm from './mutilform'
 import Utils from '@/utils/utils.js'
 import options from '@/store/options.js'
+import { updateForm } from '@/utils/utils-update.js'
 import Api from '@/api'
 import './index.scss'
 
@@ -422,20 +423,14 @@
                 _LongParam = ''
               }
             }
-            let fields = []
 
             if (_LongParam && _LongParam.type === 'Modal') {
-              if (_LongParam.groups.length > 0) {
-                _LongParam.groups.forEach(group => {
-                  fields = [...fields, ...group.sublist]
-                })
-              } else {
-                fields = _LongParam.fields
-              }
+              _LongParam = updateForm(_LongParam)
             }
 
-            if (fields && fields.length > 0) {
-              formlist = fields.map(cell => {
+            if (_LongParam && _LongParam.fields.length > 0) {
+              _LongParam.fields.forEach(cell => {
+                if (!cell.field) return 
                 let _fieldlen = cell.fieldlength || 50
 
                 if (cell.type === 'textarea' || cell.type === 'fileupload' || cell.type === 'multiselect') {
@@ -471,7 +466,7 @@
                   _field.value = ''
                 }
 
-                return _field
+                formlist.push(_field)
               })
             }
             resolve(true)
diff --git a/src/templates/zshare/customscript/index.jsx b/src/templates/zshare/customscript/index.jsx
index b7c3ecb..a69270f 100644
--- a/src/templates/zshare/customscript/index.jsx
+++ b/src/templates/zshare/customscript/index.jsx
@@ -374,7 +374,7 @@
             </Col>
             <Col span={24} className="sqlfield">
               <Form.Item label={'鍙敤瀛楁'}>
-                id, bid, loginuid, sessionuid, userid, username, fullname, appkey, time_id, orderBy{setting.laypage === 'true' ? ', pageSize, pageIndex': ''}{usefulFields ? ', ' + usefulFields : ''}
+                id, bid, loginuid, sessionuid, userid, username, fullname, 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 f78cdb1..9f96d5f 100644
--- a/src/templates/zshare/editTable/index.jsx
+++ b/src/templates/zshare/editTable/index.jsx
@@ -12,7 +12,7 @@
 import enUS from '@/locales/en-US/model.js'
 import './index.scss'
 
-let eTDict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+let eTDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 const EditableContext = React.createContext()
 let dragingIndex = -1
 
diff --git a/src/templates/zshare/editcomponent/index.jsx b/src/templates/zshare/editcomponent/index.jsx
index 769e66a..80f2599 100644
--- a/src/templates/zshare/editcomponent/index.jsx
+++ b/src/templates/zshare/editcomponent/index.jsx
@@ -21,7 +21,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     thawVisible: false,
     thawbtnlist: null,
     pasteVisible: false,
@@ -221,25 +221,13 @@
         }
         res.copyType = 'form'
 
-        if (_config.groups.length > 0) {
-          _config.groups.forEach(group => {
-            group.sublist.forEach(item => {
-              fields.push(item.field.toLowerCase())
-              labels.push(item.label)
-            })
-            if (group.default) {
-              group.sublist.push(res)
-            }
-          })
-        } else {
-          _config.fields.forEach(item => {
-            fields.push(item.field.toLowerCase())
-            labels.push(item.label)
-          })
-          _config.fields.push(res)
-        }
+        _config.fields.forEach(item => {
+          item.field && fields.push(item.field.toLowerCase())
+          labels.push(item.label)
+        })
+        _config.fields.push(res)
 
-        if (fields.includes(res.field.toLowerCase())) {
+        if (res.field && fields.includes(res.field.toLowerCase())) {
           notification.warning({
             top: 92,
             message: '瀛楁宸插瓨鍦紒',
diff --git a/src/templates/zshare/formconfig.jsx b/src/templates/zshare/formconfig.jsx
index 828532c..759648c 100644
--- a/src/templates/zshare/formconfig.jsx
+++ b/src/templates/zshare/formconfig.jsx
@@ -2,7 +2,7 @@
 import enUS from '@/locales/en-US/model.js'
 import { formRule } from '@/utils/option.js'
 
-const Formdict = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 /**
  * @description 鑾峰彇鏍戝舰椤甸潰璁剧疆琛ㄥ崟閰嶇疆淇℃伅
@@ -667,7 +667,11 @@
   }
 
   let refresh = []
+
   if (type === 'subtable') { // 瀛愯〃椤甸潰锛屽彲璁剧疆鍒锋柊涓昏〃鍙婂悓绾ф爣绛�
+    if (card.focus) {
+      card.popClose = 'maingrid'
+    }
     refresh.push({
       value: 'maingrid',
       text: Formdict['header.form.refresh.maingrid']
@@ -826,7 +830,7 @@
     {
       type: 'text',
       key: 'url',
-      label: Formdict['model.form.newpage.url'],
+      label: Formdict['model.pageUrl'],
       initVal: card.url || '',
       required: true
     },
@@ -884,10 +888,25 @@
     },
     {
       type: 'radio',
+      key: 'cross',
+      label: '璺ㄥ煙璇锋眰',
+      initVal: card.cross || 'true',
+      tooltip: '濡傛灉鑷畾涔夋帴鍙d笉鏀寔璺ㄥ煙璇锋眰锛屼細閫氳繃褰撳墠绯荤粺杞彂銆�',
+      required: false,
+      options: [{
+        value: 'true',
+        text: '鏀寔'
+      }, {
+        value: 'false',
+        text: '涓嶆敮鎸�'
+      }]
+    },
+    {
+      type: 'radio',
       key: 'callbackType',
       label: '鍥炶皟鏂瑰紡',
       initVal: card.callbackType || 'script',
-      tooltip: '浣跨敤榛樿鏂瑰紡鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
+      tooltip: '浣跨敤鍚庡彴鑴氭湰鎵ц鏃讹紝闇�瑕侀厤鍚堣鍒掍换鍔°��',
       required: true,
       options: [{
         value: 'script',
@@ -1116,7 +1135,7 @@
  * @param {object} card       // 鎼滅储鏉′欢瀵硅薄
  * @param {Array}  menulist   // 鑿滃崟鍒楄〃-鐢ㄤ簬瀛楁閫忚
  */
-export function getColumnForm (card, menulist = []) {
+export function getColumnForm (card, menulist = [], fields = []) {
   let roleList = sessionStorage.getItem('sysRoles')
   if (roleList) {
     try {
@@ -1126,6 +1145,10 @@
     }
   } else {
     roleList = []
+  }
+
+  if (!card.linkurl && (!card.linkmenu || card.linkmenu.length === 0)) {
+    card.perspective = ''
   }
 
   return [
@@ -1375,8 +1398,11 @@
       type: 'radio',
       key: 'perspective',
       label: '瀛楁閫忚',
-      initVal: card.perspective || 'linkmenu',
+      initVal: card.perspective || '',
       options: [{
+        value: '',
+        text: '鏃�'
+      }, {
         value: 'linkmenu',
         text: '鑿滃崟'
       }, {
@@ -1389,15 +1415,23 @@
       key: 'linkmenu',
       label: Formdict['model.menu'],
       initVal: card.linkmenu || [],
-      required: false,
+      required: true,
       options: menulist
     },
     {
-      type: 'text',
+      type: 'textarea',
       key: 'linkurl',
       label: '閾炬帴鍦板潃',
       initVal: card.linkurl || '',
-      required: false
+      required: true
+    },
+    {
+      type: 'multiselect',
+      key: 'linkfields',
+      label: '鍏宠仈瀛楁',
+      initVal: card.linkfields || [],
+      required: false,
+      options: fields
     },
     {
       type: 'multiselect',
@@ -1900,8 +1934,10 @@
     })
   }
 
-  if (['textarea', 'fileupload', 'multiselect', 'checkbox', 'brafteditor'].includes(card.type)) {
+  if (['fileupload', 'multiselect', 'checkbox'].includes(card.type)) {
     _fieldlength = 512
+  } else if (['textarea', 'brafteditor'].includes(card.type)) {
+    _fieldlength = 8000
   }
 
   return [
@@ -1970,9 +2006,6 @@
         value: 'textarea',
         text: Formdict['model.form.textarea']
       }, {
-        value: 'hint',
-        text: '鎻愮ず'
-      }, {
         value: 'color',
         text: Formdict['model.form.color']
       }, {
@@ -1981,6 +2014,12 @@
       }, {
         value: 'funcvar',
         text: Formdict['header.form.funcvar']
+      }, {
+        value: 'hint',
+        text: '鎻愮ず'
+      }, {
+        value: 'split',
+        text: '鍒嗛殧绾�'
       },
       ..._openType]
     },
@@ -2059,6 +2098,9 @@
     {
       type: 'number',
       key: 'width',
+      min: 1,
+      max: 24,
+      precision: 0,
       label: '鍗$墖瀹藉害',
       initVal: card.width || 4,
       tooltip: '鏍呮牸甯冨眬锛屾瘡琛岀瓑鍒嗕负24鍒椼��',
@@ -2200,6 +2242,9 @@
     {
       type: 'number',
       key: 'decimal',
+      min: 0,
+      max: 18,
+      precision: 0,
       label: Formdict['header.form.decimal'],
       initVal: card.decimal || 0,
       required: true
@@ -2221,6 +2266,9 @@
     {
       type: 'number',
       key: 'fieldlength',
+      min: 1,
+      max: 1000000,
+      precision: 0,
       label: Formdict['model.form.field'] + Formdict['model.length'],
       tooltip: '鏂囨湰銆佷笅鎷夋銆佹棩鏈熺瓑瀛楁榛樿闀垮害涓�50锛屽琛屾枃鏈笌鏂囦欢涓婁紶瀛楁榛樿闀垮害涓�512',
       initVal: card.fieldlength || _fieldlength,
@@ -2229,6 +2277,9 @@
     {
       type: 'number',
       key: 'maxRows',
+      min: 2,
+      max: 100,
+      precision: 0,
       label: Formdict['header.form.maxRows'],
       initVal: card.maxRows || 6,
       required: false
@@ -2271,6 +2322,9 @@
     {
       type: 'number',
       key: 'maxfile',
+      min: 1,
+      max: 1000000,
+      precision: 0,
       label: '鏈�澶ф枃浠舵暟',
       initVal: card.maxfile || '',
       required: false
@@ -2369,18 +2423,26 @@
       }]
     },
     {
-      type: 'radio',
-      key: 'entireLine',
-      label: '鍗犳嵁鏁磋',
-      initVal: card.entireLine || 'false',
-      required: false,
-      options: [{
-        value: 'true',
-        text: '鏄�'
-      }, {
-        value: 'false',
-        text: '鍚�'
-      }]
+      type: 'number',
+      key: 'span',
+      min: 1,
+      max: 24,
+      precision: 0,
+      label: '琛ㄥ崟瀹藉害',
+      initVal: card.span || (['textarea', 'hint', 'checkcard', 'brafteditor'].includes(card.type) ? 24 : 12),
+      tooltip: '鏍呮牸甯冨眬鏁磋24绛夊垎銆�',
+      required: true
+    },
+    {
+      type: 'number',
+      key: 'labelwidth',
+      min: 1,
+      max: 100,
+      precision: 1,
+      label: '鍚嶇О瀹藉害',
+      initVal: card.labelwidth || 33.3,
+      tooltip: '鍚嶇О鍗犳嵁琛ㄥ崟瀹藉害鐨勭櫨鍒嗘瘮銆傛敞锛氬瓨鍦ㄥ鍒楄〃鍗曟椂锛屽綋鍓嶈〃鍗曞鏋滄兂瑕佸崰鎹暣琛屽彲鍙傜収浠ヤ笅姣斾緥锛屼袱鍒楋紙16.2锛夈�佷笁鍒楋紙10.5锛夈�佸洓鍒楋紙7.7锛�',
+      required: true
     },
     {
       type: 'text',
@@ -2407,7 +2469,7 @@
     {
       type: 'radio',
       key: 'interception',
-      label: '鎴彇',
+      label: '鎴彇绌烘牸',
       initVal: card.interception || 'false',
       tooltip: '鎻愪氦鏃讹紝鏄惁鎴彇棣栧熬鐨勭┖鐧藉瓧绗︺��',
       options: [{
@@ -2447,6 +2509,14 @@
     },
     {
       type: 'text',
+      key: 'extra',
+      label: '搴曢儴鎻愮ず',
+      tooltip: '鏄剧ず浜庤〃鍗曞簳閮ㄣ��',
+      initVal: card.extra || '',
+      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 b871d33..dd53a73 100644
--- a/src/templates/zshare/modalform/index.jsx
+++ b/src/templates/zshare/modalform/index.jsx
@@ -14,31 +14,33 @@
 const { TextArea } = Input
 
 const modalTypeOptions = {
-  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'entireLine', 'tooltip', 'enter'],
-  number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'entireLine', 'tooltip', 'enter'],
-  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkSubField', 'entireLine', 'tooltip', 'emptyText', 'enter'],
-  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'entireLine', 'tooltip'],
-  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'entireLine', 'tooltip', 'setAll', 'emptyText'],
-  checkcard: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'fieldlength', 'display', 'tooltip', 'width', 'multiple'],
-  multiselect: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'entireLine', 'tooltip'],
-  link: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkField', 'entireLine', 'tooltip', 'emptyText', 'enter'],
-  fileupload: ['readonly', 'required', 'readin', 'fieldlength', 'maxfile', 'fileType', 'entireLine', 'tooltip', 'suffix'],
-  switch: ['initval', 'openVal', 'closeVal', 'openText', 'closeText', 'readonly', 'hidden', 'readin', 'entireLine', 'tooltip'],
-  date: ['initval', 'readonly', 'required', 'hidden', 'readin', 'entireLine', 'tooltip'],
-  datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'entireLine', 'tooltip'],
-  datetime: ['initval', 'readonly', 'required', 'hidden', 'readin', 'entireLine', 'tooltip'],
-  textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'maxRows', 'encryption', 'interception', 'tooltip'],
-  color: ['initval', 'readonly', 'required', 'hidden', 'readin', 'entireLine', 'tooltip'],
-  hint: ['label', 'type', 'blacklist', 'message'],
-  brafteditor: ['required', 'hidelabel', 'hidden', 'readin', 'fieldlength', 'readonly', 'tooltip', 'encryption'],
-  funcvar: [],
-  linkMain: ['readonly', 'required', 'hidden', 'fieldlength', 'entireLine', 'tooltip']
+  text: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'regular', 'interception', 'span', 'labelwidth', 'tooltip', 'extra', 'enter'],
+  number: ['initval', 'readonly', 'hidden', 'decimal', 'min', 'max', 'readin', 'span', 'labelwidth', 'tooltip', 'extra', 'enter'],
+  select: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter'],
+  checkbox: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'span', 'labelwidth', 'tooltip', 'extra'],
+  radio: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'span', 'labelwidth', 'tooltip', 'extra', 'setAll', 'emptyText'],
+  checkcard: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'linkSubField', 'fieldlength', 'span', 'labelwidth', 'display', 'tooltip', 'extra', 'width', 'multiple'],
+  multiselect: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra'],
+  link: ['initval', 'readonly', 'required', 'hidden', 'readin', 'resourceType', 'setAll', 'linkField', 'span', 'labelwidth', 'tooltip', 'extra', 'emptyText', 'enter'],
+  fileupload: ['readonly', 'required', 'readin', 'fieldlength', 'maxfile', 'fileType', 'span', 'labelwidth', 'tooltip', 'extra', 'suffix'],
+  switch: ['initval', 'openVal', 'closeVal', 'openText', 'closeText', 'readonly', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
+  date: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
+  datemonth: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
+  datetime: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
+  textarea: ['initval', 'readonly', 'required', 'hidden', 'readin', 'fieldlength', 'span', 'labelwidth', 'maxRows', 'encryption', 'interception', 'tooltip', 'extra'],
+  color: ['initval', 'readonly', 'required', 'hidden', 'readin', 'span', 'labelwidth', 'tooltip', 'extra'],
+  hint: ['label', 'type', 'blacklist', 'message', 'span', 'labelwidth'],
+  split: ['label', 'type'],
+  brafteditor: ['required', 'hidelabel', 'hidden', 'readin', 'fieldlength', 'readonly', 'span', 'labelwidth', 'tooltip', 'extra', 'encryption'],
+  funcvar: ['span', 'labelwidth'],
+  linkMain: ['readonly', 'required', 'hidden', 'fieldlength', 'span', 'labelwidth', 'tooltip', 'extra']
 }
 
 class MainSearch extends Component {
   static propTpyes = {
     dict: PropTypes.object,    // 瀛楀吀椤�
     formlist: PropTypes.any,
+    standardform: PropTypes.any,
     card: PropTypes.object,
     inputSubmit: PropTypes.any
   }
@@ -56,7 +58,6 @@
 
   UNSAFE_componentWillMount () {
     let formlist = fromJS(this.props.formlist).toJS()
-
     let type = ''
     let resourceType = ''
     let supField = ''
@@ -147,6 +148,8 @@
 
     if (type === 'hint') {
       _options = fromJS(modalTypeOptions[type]).toJS()
+    } else if (type === 'split') {
+      return fromJS(modalTypeOptions[type]).toJS()
     } else if (['multiselect', 'select', 'link', 'radio', 'checkbox'].includes(type)) {
       if (resourceType === '0') {        // 鑷畾涔夎祫婧�
         _options.push('options')
@@ -232,7 +235,7 @@
             }
           } else if (form.key === 'fieldlength') {
             form.initVal = 50
-            if (value === 'textarea') {
+            if (value === 'textarea' || value === 'brafteditor') {
               form.initVal = 8000
             } else if (value === 'fileupload' || value === 'multiselect' || value === 'checkbox') {
               form.initVal = 512
@@ -361,6 +364,50 @@
     })
   }
 
+  changeVal = (val, type) => {
+    if (type !== 'span' || ![24, 12, 8, 6].includes(val)) return
+
+    const { standardform } = this.props
+
+    if (!standardform || ![24, 12, 8, 6].includes(standardform.span) || !standardform.labelwidth) return
+
+    let labelwidth = null
+
+    if (standardform.span === val) {
+      labelwidth = standardform.labelwidth
+    } else if (standardform.span > val) {
+      labelwidth = 33.3
+    } else {
+      switch(standardform.span) {
+        case 12:
+          labelwidth = 16.2
+          break;
+        case 8:
+          if (val === 12) {
+            labelwidth = 21.6
+          } else {
+            labelwidth = 10.5
+          }
+          break;
+        case 6:
+          if (val === 8) {
+            labelwidth = 24.3
+          } else if (val === 12) {
+            labelwidth = 15.8
+          } else {
+            labelwidth = 7.7
+          }
+          break;
+        default:
+      }
+    } 
+
+
+    if (labelwidth) {
+      this.props.form.setFieldsValue({labelwidth})
+    }
+  }
+
   handleSubmit = (e) => {
     e.preventDefault()
 
@@ -417,10 +464,15 @@
           </Col>
         )
       } else if (item.type === 'number') {
-        if (item.key === 'decimal') {
+        if (item.max) {
           fields.push(
             <Col span={12} key={index}>
-              <Form.Item label={item.label}>
+              <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 || 0,
                   rules: [
@@ -429,50 +481,18 @@
                       message: this.props.dict['form.required.input'] + item.label + '!'
                     }
                   ]
-                })(<InputNumber min={0} max={18} precision={0} />)}
+                })(<InputNumber onChange={(value) => this.changeVal(value, item.key)} min={item.min} max={item.max} precision={item.precision} onPressEnter={this.handleSubmit}/>)}
               </Form.Item>
             </Col>
           )
-        } else if (item.key === 'fieldlength' || item.key === 'maxfile') {
+        } else {
           fields.push(
             <Col span={12} key={index}>
-              <Form.Item label={item.label}>
-                {getFieldDecorator(item.key, {
-                  initialValue: item.initVal,
-                  rules: [
-                    {
-                      required: !!item.required,
-                      message: this.props.dict['form.required.input'] + item.label + '!'
-                    }
-                  ]
-                })(<InputNumber min={1} precision={0} />)}
-              </Form.Item>
-            </Col>
-          )
-        } else if (item.key === 'maxRows') {
-          fields.push(
-            <Col span={12} key={index}>
-              <Form.Item label={item.label}>
-                {getFieldDecorator(item.key, {
-                  initialValue: item.initVal,
-                  rules: [
-                    {
-                      required: !!item.required,
-                      message: this.props.dict['form.required.input'] + item.label + '!'
-                    }
-                  ]
-                })(<InputNumber min={2} max={100} precision={0} />)}
-              </Form.Item>
-            </Col>
-          )
-        } else if (item.key === 'width') {
-          fields.push(
-            <Col span={12} key={index}>
-              <Form.Item label={
+              <Form.Item label={item.tooltip ?
                 <Tooltip placement="topLeft" title={item.tooltip}>
                   <Icon type="question-circle" />
                   {item.label}
-                </Tooltip>
+                </Tooltip> : item.label
               }>
                 {getFieldDecorator(item.key, {
                   initialValue: item.initVal,
@@ -482,23 +502,7 @@
                       message: this.props.dict['form.required.input'] + item.label + '!'
                     }
                   ]
-                })(<InputNumber min={1} max={24} precision={0} />)}
-              </Form.Item>
-            </Col>
-          )
-        } else {
-          fields.push(
-            <Col span={12} key={index}>
-              <Form.Item label={item.label}>
-                {getFieldDecorator(item.key, {
-                  initialValue: item.initVal,
-                  rules: [
-                    {
-                      required: !!item.required,
-                      message: this.props.dict['form.required.input'] + item.label + '!'
-                    }
-                  ]
-                })(<InputNumber />)}
+                })(<InputNumber onPressEnter={this.handleSubmit}/>)}
               </Form.Item>
             </Col>
           )
@@ -710,6 +714,8 @@
             }
           } else if (values.type === 'linkMain') {
             values.initval = ''
+          } else if (values.type === 'split') {
+            values.span = 24
           }
 
           ['linkField', 'valueField', 'valueText', 'orderBy'].forEach(item => {
diff --git a/src/templates/zshare/verifycard/callbackcustomscript/index.jsx b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
index aca9271..5d9f5e7 100644
--- a/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
+++ b/src/templates/zshare/verifycard/callbackcustomscript/index.jsx
@@ -107,6 +107,20 @@
           }
         })
 
+        if (!values.uuid) {
+          if (values.position === 'front') {
+            _prevCustomScript += `
+            /* 榛樿sql鍓嶈剼鏈� */
+            ${values.sql}
+            `
+          } else {
+            _backCustomScript += `
+            /* 榛樿sql鍚庤剼鏈� */
+            ${values.sql}
+            `
+          }
+        }
+
         let param = {
           func: 's_debug_sql',
           exec_type: 'y',
@@ -258,12 +272,7 @@
             </Button>
           </Col>
           <Col span={24} className="sql">
-            <Form.Item label={
-              <Tooltip placement="topLeft" title={'鏁版嵁妫�鏌ユ浛鎹㈢ $check@ -> /* 鎴� \'\'銆� @check$ -> */ 鎴� \'\''}>
-                <Icon type="question-circle" />
-                sql
-              </Tooltip>
-            }>
+            <Form.Item label="sql">
               {getFieldDecorator('sql', {
                 initialValue: '',
                 rules: [
diff --git a/src/templates/zshare/verifycard/customscript/index.jsx b/src/templates/zshare/verifycard/customscript/index.jsx
index 7cd554e..f7f1d2e 100644
--- a/src/templates/zshare/verifycard/customscript/index.jsx
+++ b/src/templates/zshare/verifycard/customscript/index.jsx
@@ -114,6 +114,25 @@
           }
         })
 
+        if (!values.uuid) {
+          if (values.position === 'init') {
+            _initCustomScript += `
+            /* 鍒濆鍖栬剼鏈� */
+            ${values.sql}
+            `
+          } else if (values.position === 'front') {
+            _prevCustomScript += `
+            /* 榛樿sql鍓嶈剼鏈� */
+            ${values.sql}
+            `
+          } else {
+            _backCustomScript += `
+            /* 榛樿sql鍚庤剼鏈� */
+            ${values.sql}
+            `
+          }
+        }
+
         let param = {
           func: 's_debug_sql',
           exec_type: 'y',
@@ -267,7 +286,7 @@
           </Col>
           <Col span={24} className="sql">
             <Form.Item label={
-              <Tooltip placement="topLeft" title={'鏁版嵁妫�鏌ユ浛鎹㈢ $check@ -> /* 鎴� \'\'銆� @check$ -> */ 鎴� \'\''}>
+              <Tooltip placement="topLeft" title={'鏁版嵁妫�鏌ユ浛鎹㈢ $check@ -> \'\'銆� @check$ -> \'\'锛孍rrorCode绛変簬C鏃� $check@ -> /*銆� @check$ -> */'}>
                 <Icon type="question-circle" />
                 sql
               </Tooltip>
diff --git a/src/templates/zshare/verifycard/index.jsx b/src/templates/zshare/verifycard/index.jsx
index 09a4338..a3a388e 100644
--- a/src/templates/zshare/verifycard/index.jsx
+++ b/src/templates/zshare/verifycard/index.jsx
@@ -6,7 +6,7 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
-
+import options from '@/store/options.js'
 import UniqueForm from './uniqueform'
 import ContrastForm from './contrastform'
 import CustomForm from './customform'
@@ -15,6 +15,7 @@
 import BillcodeForm from './billcodeform'
 import VoucherForm from './voucherform'
 import asyncComponent from '@/utils/asyncComponent'
+import { updateForm } from '@/utils/utils-update.js'
 import './index.scss'
 
 const { TabPane } = Tabs
@@ -473,11 +474,11 @@
           if (record.TypeCharOne === 'n') {
             _text = record.ModularDetailCode + Array(_type).join('0') + '1'
           } else if (record.TypeCharOne === 'Y') {
-            _text = record.ModularDetailCode + moment().format('YYYYMMDD') + Array(_type).join('0') + '1'
+            _text = record.ModularDetailCode + moment().format('YYMMDD') + Array(_type).join('0') + '1'
           } else if (record.TypeCharOne === 'Lp') {
             _text = Array(_type).join('0') + '10'
           } else if (record.TypeCharOne === 'BN') {
-            _text = moment().format('YYYYMMDD') + Array(_type).join('0') + '1'
+            _text = moment().format('YYMMDD') + Array(_type).join('0') + '1'
           }
           return _text
         }
@@ -571,13 +572,7 @@
         })
         resolve(_fields)
       } else if (card.modal) {
-        if (card.modal.groups && card.modal.groups.length > 0) {
-          card.modal.groups.forEach(group => {
-            _fields.push(...group.sublist)
-          })
-        } else {
-          _fields = card.modal.fields || []
-        }
+        _fields = card.modal.fields || []
         resolve(_fields)
       } else if (card.OpenType === 'pop') {
         Api.getSystemConfig({
@@ -598,13 +593,8 @@
             if (!_LongParam) {
               message.warning('鏈幏鍙栧埌琛ㄥ崟淇℃伅锛岄儴鍒嗛獙璇佸皢鏃犳硶璁剧疆锛�')
             } else {
-              if (_LongParam.groups.length > 0) {
-                _LongParam.groups.forEach(group => {
-                  _fields.push(...group.sublist)
-                })
-              } else {
-                _fields = _LongParam.fields || []
-              }
+              _LongParam = updateForm(_LongParam)
+              _fields = _LongParam.fields || []
             }
           } else {
             notification.warning({
@@ -619,9 +609,9 @@
         resolve(_fields)
       }
     }).then(_fields => {
-      let _usefulfields = ['BID', 'ID', 'LoginUID', 'SessionUid', 'UserID', 'Appkey', 'UserName', 'FullName', 'BillCode', 'BVoucher', 'FIBVoucherDate', 'FiYear', 'ModularDetailCode']
-      let _declare = ['@UserName nvarchar(50)', '@FullName 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=\'\'', '@ErrorCode=\'\'', '@retmsg=\'\'', '@BillCode=\'\'', '@BVoucher=\'\'', '@FIBVoucherDate=\'\'', '@FiYear=\'\'', '@ModularDetailCode=\'\'']
+      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 fieldArr = _usefulfields.map(_f => _f.toLowerCase())
       let hasBid = false
 
@@ -916,7 +906,7 @@
       {
         obj_name: 'modularDetail',
         arr_field: 'ModularDetailCode,CodeName,BID,Type',
-        LText: window.btoa(window.encodeURIComponent(`select distinct ModularDetailCode,ModularDetailCode+ModularDetailName as CodeName,ModularCode as BID,Type from sModularDetail where Appkey= @Appkey@ and deleted=0 order by ModularDetailCode`))
+        LText: window.btoa(window.encodeURIComponent(`select distinct ModularDetailCode,ModularDetailCode+ModularDetailName as CodeName,ModularCode as BID,Type from sModularDetail where Appkey= @Appkey@ and deleted=0 order by ModularDetailCode desc`))
       },
       {
         obj_name: 'voucher',
@@ -931,7 +921,7 @@
           select distinct a.ModularDetailCode,a.ModularDetailCode+ModularDetailName as CodeName,ModularCode as BID, VoucherTypeTwo, IDefine1 
           from (select * from sModularDetail where Deleted=0 and VoucherTypeTwo!='' and Appkey='') a
           left join (select distinct ModularDetailCode from sModularDetail where Deleted=0 and VoucherTypeTwo!='' 
-          and Appkey=@Appkey@) m on a.ModularDetailCode=m.ModularDetailCode where m.ModularDetailCode is null order by ModularDetailCode`
+          and Appkey=@Appkey@) m on a.ModularDetailCode=m.ModularDetailCode where m.ModularDetailCode is null order by ModularDetailCode desc`
         ))
       },
       {
@@ -961,7 +951,13 @@
     mutilparam.secretkey = Utils.encrypt(mutilparam.LText, mutilparam.timestamp)
     mutilparam.open_key = Utils.encryptOpenKey(mutilparam.secretkey, mutilparam.timestamp)
 
-    Api.getCloudCacheConfig(mutilparam).then(res => {
+    if (options.cloudServiceApi) { // 浜戠璇锋眰
+      mutilparam.rduri = options.cloudServiceApi
+      mutilparam.userid = sessionStorage.getItem('CloudUserID') || ''
+      mutilparam.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
+    }
+
+    Api.getSystemCacheConfig(mutilparam).then(res => {
       if (res.status) {
         this.setState({
           orderModular: res.modular,
@@ -1321,19 +1317,17 @@
         return
       }
 
-      let _loading = false
+      let msg = ''
       if (this.customForm && this.customForm.state.editItem) {
-        _loading = true
+        msg = '鑷畾涔夐獙璇�'
+      } else if (this.customForm && this.customForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.customForm.props.form.getFieldValue('sql'))) {
+        msg = '鑷畾涔夐獙璇�'
       } else if (this.orderForm && this.orderForm.state.editItem) {
-        _loading = true
+        msg = '鍗曞彿鐢熸垚'
       } else if (this.scriptsForm && this.scriptsForm.state.editItem) {
-        _loading = true
-      }
-
-      if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && this.scriptsForm.props.form.getFieldValue('sql') !== ' ') {
-        _loading = true
-      } else if (this.customForm && this.customForm.props.form.getFieldValue('sql') && this.customForm.props.form.getFieldValue('sql') !== ' ') {
-        _loading = true
+        msg = '鑷畾涔夎剼鏈�'
+      } else if (this.scriptsForm && this.scriptsForm.props.form.getFieldValue('sql') && !/^\s+$/.test(this.scriptsForm.props.form.getFieldValue('sql'))) {
+        msg = '鑷畾涔夎剼鏈�'
       }
 
       if (verify.noteEnable === 'true' && !verify.noteCode) { // 寮�鍚煭淇℃椂锛岄渶瑕佹ā鏉跨紪鐮�
@@ -1342,9 +1336,9 @@
         verify.noteCode = ''
       }
 
-      if (_loading) {
+      if (msg) {
         confirm({
-          content: `瀛樺湪鏈繚瀛橀」锛岀‘瀹氭彁浜ゅ悧锛焋,
+          content: msg + '鏈繚瀛橈紝纭畾鎻愪氦鍚楋紵',
           onOk() {
             resolve(verify)
           },
diff --git a/src/utils/devutils.js b/src/utils/devutils.js
deleted file mode 100644
index b314e29..0000000
--- a/src/utils/devutils.js
+++ /dev/null
@@ -1,459 +0,0 @@
-export default class Utils {
-  /**
-   * @description 鍒犻櫎瀛樺偍杩囩▼sql
-   * @return {String} name 瀛樺偍杩囩▼鍚嶇О
-   */
-  static dropfunc (name) {
-    return `IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('${name}') AND type in (N'P', N'PC'))  mdrpk PROCEDURE ${name}`
-  }
-
-  /**
-   * @description 鍒涘缓椤甸潰瀛樺偍杩囩▼
-   * @return {String}
-   */
-  static getTableFunc (param, menu, config) {
-    let form = ''
-    let formParam = ''
-    let _vars = ['bid', 'pageindex', 'pagesize', 'ordercol', 'ordertype', 'exceltype', 'septmenuno', 'lang', 'debug', 'loginuid', 'sessionuid', 'userid', 'errorcode', 'retmsg']
-    let _columns = []
-    let primaryKey = config.setting.primaryKey || 'ID'
-
-    if (!_vars.includes(primaryKey.toLowerCase())) {
-      _vars.push(primaryKey.toLowerCase())
-      formParam = `mchr13k@${primaryKey} nvarchar(50)='',`
-    }
-
-    if (config.search && config.search.length > 0) {
-      let _fields = new Map()
-      config.search.forEach(item => {
-        if (item.field) {
-          let type = ''
-
-          if (item.type.match(/date/ig)) {
-            type = 'datetime=null'
-          } else {
-            type = 'nvarchar(50)=\'\''
-          }
-
-          item.field.split(',').forEach(cell => {
-            let _f = cell
-            if (_fields.has(cell)) {
-              _f = _f + '1'
-            }
-  
-            _fields.set(cell, true)
-
-            if (!_vars.includes(_f.toLowerCase())) {
-              _vars.push(_f.toLowerCase())
-              formParam = formParam + `mchr13k@${_f} ${type},`
-            }
-          })
-        }
-      })
-    }
-
-    if (config.columns && config.columns.length > 0) {
-      config.columns.forEach(item => {
-        if (item.field) {
-          _columns.push(`${item.field} as ${item.label}`)
-        }
-      })
-
-      form = `
-        declare @dc table (${_columns.join(',')})
-        
-        @tableid ='${menu.MenuID}'
-      `
-    }
-
-    let Ltext = `create proc ${param.innerFunc}
-    ( /*${menu.MenuName}*/
-    @appkey nvarchar(50)='',
-    @BID nvarchar(50)='',${formParam}
-    @PageIndex nvarchar(50)='',
-    @PageSize nvarchar(50)='',
-    @OrderCol nvarchar(50)='',
-    @OrderType nvarchar(50)='',
-    @exceltype nvarchar(50)='',
-    @sEPTMenuNo nvarchar(50)='${menu.MenuNo}',
-    @lang nvarchar(50)='',
-    @debug nvarchar(50)='',
-    @LoginUID nvarchar(50)='',
-    @SessionUid nvarchar(50)='',
-    @UserID nvarchar(50),
-    @ErrorCode nvarchar(50) out,
-    @retmsg nvarchar(4000) out
-    )
-    as
-    begin
-    declare  @BegindateTest datetime,@EnddateTest datetime
-    select  @BegindateTest=getdate()
-    set @ErrorCode=''
-    set @retmsg=''
-    BEGIN TRY
-      /*浜嬪姟鎿嶄綔*/
-      BEGIN TRAN
-        /*鍏蜂綋涓氬姟鎿嶄綔*/
-        
-        /* 
-        select top 10 * from sProcExcep order by id desc
-        
-        declare @UserName  nvarchar(50),@FullName nvarchar(50)
-        
-        select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
-        ${form}
-        if 1=2
-        begin
-          set @ErrorCode='E'
-          set @retmsg='鍦ㄦ鍐欐姤閿�'
-          goto GOTO_RETURN
-        end
-        
-        insert into sNote (remark,createuserid,CreateUser,CreateStaff)
-        select '鍦ㄦ鍐欐棩蹇�',@UserID,@UserName,@FullName
-        */
-        
-      COMMIT TRAN
-      SET NOCOUNT ON
-      RETURN
-    END TRY
-    BEGIN CATCH
-      /*閿欒澶勭悊*/
-      ROLLBACK TRAN
-      DECLARE @ErrorMessage NVARCHAR(4000);
-      DECLARE @ErrorSeverity INT;
-      DECLARE @ErrorState INT;
-      
-      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
-      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
-      SET @retmsg=ERROR_MESSAGE();
-      SELECT @ErrorMessage=ERROR_MESSAGE(),
-        @ErrorSeverity=ERROR_SEVERITY(),
-        @ErrorState=ERROR_STATE();
-        
-      RAISERROR(@ErrorMessage, /*-- Message text.*/
-        @ErrorSeverity, /*-- Severity.*/
-        @ErrorState  /*-- State.*/
-      );
-    END CATCH
-    
-    GOTO_RETURN:
-      ROLLBACK TRAN
-      
-    END`
-
-    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
-
-    return Ltext
-  }
-
-  /**
-   * @description 鍒涘缓瀛樺偍杩囩▼
-   * @return {String}
-   */
-  static getfunc (param, btn, menu, config) {
-    let form = ''
-    let formParam = ''
-    let _vars = ['bid', 'septmenuno', 'lang', 'debug', 'loginuid', 'sessionuid', 'userid', 'errorcode', 'retmsg']
-    let columns = config.columns
-    let primaryKey = config.setting.primaryKey || 'ID'
-
-    if (!_vars.includes(primaryKey.toLowerCase())) {
-      _vars.push(primaryKey.toLowerCase())
-      let _type = '50'
-      if (btn.Ot === 'requiredOnce') { // 澶氳鎷兼帴鏃讹紝涓婚敭璁句负max
-        _type = 'max'
-      }
-      formParam = `mchr13k@${primaryKey} nvarchar(${_type})='',`
-    }
-
-    if (param.fields && param.fields.length > 0) {
-      let _fields = []
-      param.fields.forEach(item => {
-        if (item.field) {
-          let type = ''
-          if (item.type.match(/date/ig)) {
-            type = 'datetime=null'
-          } else if (item.type === 'number') {
-            type = `decimal(18,${item.decimal})=0`
-          } else {
-            type = 'nvarchar(50)=\'\''
-          }
-
-          if (!_vars.includes(item.field.toLowerCase())) {
-            _vars.push(item.field.toLowerCase())
-            formParam = formParam + `mchr13k@${item.field} ${type},`
-          }
-
-          _fields.push(item.field)
-        }
-      })
-
-      let field1 = _fields.join(',')
-      let field2 = _fields.join(',@')
-      let field3 = _fields.map(cell => {
-        return cell + '=@' + cell
-      })
-
-      field2 = field2 ? '@' + field2 : ''
-      field3 = field3.join(',')
-
-      form = `
-        insert into ${param.name} (${field1},createuserid) select ${field2},@UserID
-        
-        update ${param.name} set ${field3},modifydate=getdate(),modifyuserid=@UserID
-      `
-    } else if (btn.OpenType === 'prompt' || btn.OpenType === 'exec') {
-      form = `
-        update ${param.name} set ModifyDate=getdate(),ModifyUserID=@UserID where ${primaryKey}=@${primaryKey}
-      `
-    }
-
-    if (columns) {
-      let _col = []
-      let _field = []
-      columns.forEach(col => {
-        if (col.field) {
-          if (col.type === 'number') {
-            _col.push(col.field + ' decimal(18,2)')
-          } else {
-            _col.push(col.field + ' nvarchar(50)')
-          }
-          _field.push(col.field)
-        }
-      })
-      _col = _col.join(',')
-      _field = _field.join(',')
-
-      form = form + `
-        declare @dc table (${_col})
-        
-        insert into @dc (${_field})
-
-        @tableid ='${menu.MenuID}'
-      `
-    }
-
-    // 鎵撳嵃鑷畾涔夋ā鏉垮瓧娈垫彁绀�
-    let _printRemark = ''
-    if (btn.funcType === 'print') {
-      _printRemark = '/* 鑷畾涔夋暟鎹墦鍗版ā鏉挎椂锛岃浣跨敤TemplateID瀛楁 */'
-    }
-
-    let Ltext = `create proc ${param.funcName}
-    ( /*${menu.MenuName}  ${btn.label}*/
-    @appkey nvarchar(50)='',
-    @BID nvarchar(50)='',${formParam}
-    @sEPTMenuNo nvarchar(50)='${param.menuNo}',
-    @lang nvarchar(50)='',
-    @debug nvarchar(50)='',
-    @LoginUID nvarchar(50)='',
-    @SessionUid nvarchar(50)='',
-    @UserID nvarchar(50),
-    @ErrorCode nvarchar(50) out,
-    @retmsg nvarchar(4000) out
-    )
-    as
-    begin
-    declare  @BegindateTest datetime,@EnddateTest datetime
-    select  @BegindateTest=getdate()
-    set @ErrorCode=''
-    set @retmsg=''
-    BEGIN TRY
-      /*浜嬪姟鎿嶄綔*/
-      BEGIN TRAN
-        /*鍏蜂綋涓氬姟鎿嶄綔*/
-        ${_printRemark}
-        /* 
-        select top 10 * from sProcExcep order by id desc
-        
-        declare @UserName  nvarchar(50),@FullName nvarchar(50)
-        
-        select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
-        ${form}
-        if 1=2
-        begin
-          set @ErrorCode='E'
-          set @retmsg='鍦ㄦ鍐欐姤閿�'
-          goto GOTO_RETURN
-        end
-        
-        insert into sNote (remark,createuserid,CreateUser,CreateStaff)
-        select '鍦ㄦ鍐欐棩蹇�',@UserID,@UserName,@FullName
-        */
-        
-      COMMIT TRAN
-      SET NOCOUNT ON
-      RETURN
-    END TRY
-    BEGIN CATCH
-      /*閿欒澶勭悊*/
-      ROLLBACK TRAN
-      DECLARE @ErrorMessage NVARCHAR(4000);
-      DECLARE @ErrorSeverity INT;
-      DECLARE @ErrorState INT;
-      
-      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
-      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
-      SET @retmsg=ERROR_MESSAGE();
-      SELECT @ErrorMessage=ERROR_MESSAGE(),
-        @ErrorSeverity=ERROR_SEVERITY(),
-        @ErrorState=ERROR_STATE();
-        
-      RAISERROR(@ErrorMessage, /*-- Message text.*/
-        @ErrorSeverity, /*-- Severity.*/
-        @ErrorState  /*-- State.*/
-      );
-    END CATCH
-    
-    GOTO_RETURN:
-      ROLLBACK TRAN
-      
-    END`
-
-    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
-
-    return Ltext
-  }
-
-  /**
-   * @description 鍒涘缓瀵煎叆瀛樺偍杩囩▼
-   * @return {String}
-   */
-  static getexcelInfunc (param, btn, menu) {
-    let _verify = btn.verify
-
-    let _uniquesql = ''
-    if (_verify.uniques && _verify.uniques.length > 0) {
-      _verify.uniques.forEach(unique => {
-        if (unique.status === 'false') return
-
-        let _fields = unique.field.split(',')
-        let _fields_ = _fields.map(_field => {
-          return `a.${_field}=b.${_field}`
-        })
-        _fields_ = _fields_.join(' and ')
-
-        if (unique.verifyType !== 'physical') {
-          _fields_ += ' and b.deleted=0'
-        }
-
-        _uniquesql += `
-        Set @tbid=''
-        Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from @${btn.sheet} ) a group by ${unique.field} having sum(n)>1
-
-        If @tbid!=''
-        Begin
-          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 閲嶅'
-          goto aaa
-        end
-
-        Set @tbid=''
-        Select top 1 @tbid=${_fields.join('+\' \'+')} from  @${btn.sheet} a
-        Inner join ${btn.sheet} b on ${_fields_}
-
-        If @tbid!=''
-        Begin
-          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 涓庡凡鏈夋暟鎹噸澶�'
-          goto aaa
-        end
-        `
-      })
-
-      if (_uniquesql) {
-        _uniquesql = `
-        Declare @tbid Nvarchar(512)
-        ${_uniquesql}`
-      }
-    }
-
-    let declarefields = []
-    let fields = []
-
-    _verify.columns.forEach(col => {
-      declarefields.push(`${col.Column} ${col.type}`)
-      fields.push(col.Column)
-    })
-
-    fields = fields.join(',')
-
-    let _sql = `declare @${btn.sheet} table (${declarefields.join(',')},jskey nvarchar(50))
-      Declare @UserName nvarchar(50),@FullName nvarchar(50)
-      
-      select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
-      
-      Insert into @${btn.sheet} (${fields},jskey)
-
-      exec s_KeyWords_Replace
-      @LText=@LText, @BID=@BID,@LoginUID=@LoginUID,@SessionUid=@SessionUid,@UserID=@UserID,@ID=@ID
-      ${_uniquesql}
-      Insert into ${btn.sheet} (${fields},createuserid,createuser,createstaff,bid) 
-      Select ${fields},@userid,@username,@fullname,@BID From @${btn.sheet}
-
-      Delete @${btn.sheet}`
-
-    let Ltext = `create proc ${param.funcName}
-    ( /*${menu.MenuName}  ${btn.label}*/
-      @appkey nvarchar(50)='',
-      @ID nvarchar(50)='',
-      @BID nvarchar(50)='',
-      @Ltext nvarchar(max)='',
-      @sEPTMenuNo nvarchar(50)='${param.menuNo}', 
-      @secretkey nvarchar(50)='',
-      @timestamp nvarchar(50)='',
-      @lang nvarchar(50)='',
-      @LoginUID nvarchar(50)='',
-      @SessionUid nvarchar(50)='',
-      @UserID nvarchar(50), 
-      @ErrorCode nvarchar(50) out,
-      @retmsg nvarchar(4000) out
-    )
-    as
-    begin
-    declare @BegindateTest datetime,@EnddateTest datetime 
-    select @BegindateTest=getdate() 
-    set @ErrorCode=''
-    set @retmsg=''
-    BEGIN TRY
-      /*浜嬪姟鎿嶄綔*/
-      BEGIN TRAN
-        /*鍏蜂綋涓氬姟鎿嶄綔*/
-
-        /* 
-        ${_sql}
-        */
-        
-      COMMIT TRAN
-      SET NOCOUNT ON
-      RETURN
-    END TRY
-    BEGIN CATCH
-      /*閿欒澶勭悊*/
-      ROLLBACK TRAN
-      DECLARE @ErrorMessage NVARCHAR(4000);
-      DECLARE @ErrorSeverity INT;
-      DECLARE @ErrorState INT;
-      
-      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
-      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
-      SET @retmsg=ERROR_MESSAGE();
-      SELECT @ErrorMessage=ERROR_MESSAGE(),
-        @ErrorSeverity=ERROR_SEVERITY(),
-        @ErrorState=ERROR_STATE();
-        
-      RAISERROR(@ErrorMessage, /*-- Message text.*/
-        @ErrorSeverity, /*-- Severity.*/
-        @ErrorState  /*-- State.*/
-      );
-    END CATCH
-    
-    GOTO_RETURN:
-      ROLLBACK TRAN
-      
-    END`
-
-    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
-
-    return Ltext
-  }
-}
\ No newline at end of file
diff --git a/src/menu/utils/menuUtils.js b/src/utils/utils-custom.js
similarity index 94%
rename from src/menu/utils/menuUtils.js
rename to src/utils/utils-custom.js
index 0a705c7..e991da4 100644
--- a/src/menu/utils/menuUtils.js
+++ b/src/utils/utils-custom.js
@@ -183,7 +183,7 @@
           getUuids(c)
         })
       } else {
-        if (item.action && item.action.length) {
+        if (item.action && item.action.length > 0) {
           item.action.forEach(act => {
             if (!act.origin) {
               uuids.push(act.uuid)
@@ -198,6 +198,14 @@
               }
             })
             _card.backElements && _card.backElements.forEach(cell => {
+              if (cell.eleType === 'button') {
+                uuids.push(cell.uuid)
+              }
+            })
+          })
+        } else if (item.type === 'carousel') {
+          item.subcards.forEach(_card => {
+            _card.elements && _card.elements.forEach(cell => {
               if (cell.eleType === 'button') {
                 uuids.push(cell.uuid)
               }
@@ -248,6 +256,10 @@
    */
   static resetConfig = (components) => {
     return components.map(item => {
+      if (item.type === 'navbar') {
+        return item
+      }
+
       item.uuid = this.getuuid()
 
       if (item.type === 'tabs') {
@@ -268,7 +280,7 @@
           return cell
         })
         item.components = this.resetConfig(item.components)
-      } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+      } else if (item.type === 'card' || item.type === 'carousel' || (item.type === 'table' && item.subtype === 'tablecard')) {
         item.subcards.forEach(card => {
           card.uuid = this.getuuid()
           if (card.elements) {
diff --git a/src/utils/utils-datamanage.js b/src/utils/utils-datamanage.js
index 95707ed..f684846 100644
--- a/src/utils/utils-datamanage.js
+++ b/src/utils/utils-datamanage.js
@@ -91,6 +91,7 @@
 
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -101,8 +102,8 @@
     let _customScript = ''
     
     if (setting.customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+      _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}'
         ${setting.customScript}
       `
     }
@@ -110,14 +111,6 @@
     let regoptions = null
     if (setting.queryType === 'statistics' || _customScript) {
       let allSearch = Utils.getAllSearchOptions(search)
-      let userName = sessionStorage.getItem('User_Name') || ''
-      let fullName = sessionStorage.getItem('Full_Name') || ''
-
-      if (sessionStorage.getItem('isEditState') === 'true') {
-        userName = sessionStorage.getItem('CloudUserName') || ''
-        fullName = sessionStorage.getItem('CloudFullName') || ''
-      }
-
       regoptions = allSearch.map(item => {
         return {
           reg: new RegExp('@' + item.key + '@', 'ig'),
@@ -125,11 +118,14 @@
         }
       })
       regoptions.push({
+        reg: new RegExp('@login_city@', 'ig'),
+        value: `'${city}'`
+      }, {
         reg: new RegExp('@userName@', 'ig'),
-        value: userName
+        value: `'${userName}'`
       }, {
         reg: new RegExp('@fullName@', 'ig'),
-        value: fullName
+        value: `'${fullName}'`
       }, {
         reg: new RegExp('@orderBy@', 'ig'),
         value: orderBy
@@ -202,6 +198,8 @@
     param.LText = Utils.formatOptions(LText)
     param.DateCount = Utils.formatOptions(DateCount)
 
+    // exec_type: 'y' 瑙g爜瀛楁锛歀Text銆丩Text1銆丩Text2銆乧ustom_script銆丏ateCount
+
     param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
     param.secretkey = Utils.encrypt('', param.timestamp)
 
@@ -226,9 +224,18 @@
     
     let _dataresource = setting.dataresource
     let _customScript = ''
+    let userName = sessionStorage.getItem('User_Name') || ''
+    let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
+
+    if (sessionStorage.getItem('isEditState') === 'true') {
+      userName = sessionStorage.getItem('CloudUserName') || ''
+      fullName = sessionStorage.getItem('CloudFullName') || ''
+    }
     
     if (setting.customScript) {
-      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000) select @ErrorCode='',@retmsg =''
+      _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}'
         ${setting.customScript}
       `
     }
@@ -236,13 +243,6 @@
     let regoptions = null
     if (setting.queryType === 'statistics' || _customScript) {
       let allSearch = Utils.getAllSearchOptions(search)
-      let userName = sessionStorage.getItem('User_Name') || ''
-      let fullName = sessionStorage.getItem('Full_Name') || ''
-
-      if (sessionStorage.getItem('isEditState') === 'true') {
-        userName = sessionStorage.getItem('CloudUserName') || ''
-        fullName = sessionStorage.getItem('CloudFullName') || ''
-      }
 
       regoptions = allSearch.map(item => {
         return {
@@ -251,11 +251,14 @@
         }
       })
       regoptions.push({
+        reg: new RegExp('@login_city@', 'ig'),
+        value: `'${city}'`
+      }, {
         reg: new RegExp('@userName@', 'ig'),
-        value: userName
+        value: `'${userName}'`
       }, {
         reg: new RegExp('@fullName@', 'ig'),
-        value: fullName
+        value: `'${fullName}'`
       }, {
         reg: new RegExp('@orderBy@', 'ig'),
         value: orderBy
@@ -366,6 +369,7 @@
     let sql = ''
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
@@ -380,8 +384,8 @@
 
     if (sql) {
       sql = `/*鍓嶇疆鑴氭湰*/
-        declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-        Select @ErrorCode='',@retmsg ='',@UserName='${userName}', @FullName='${fullName}'
+        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}'
         ${sql}
         aaa:
           if @ErrorCode!=''
@@ -396,11 +400,14 @@
         }
       })
       regoptions.push({
+        reg: new RegExp('@login_city@', 'ig'),
+        value: `'${city}'`
+      }, {
         reg: new RegExp('@userName@', 'ig'),
-        value: userName
+        value: `'${userName}'`
       }, {
         reg: new RegExp('@fullName@', 'ig'),
-        value: fullName
+        value: `'${fullName}'`
       })
 
       regoptions.forEach(item => {
@@ -444,14 +451,15 @@
 
     let userName = sessionStorage.getItem('User_Name') || ''
     let fullName = sessionStorage.getItem('Full_Name') || ''
+    let city = sessionStorage.getItem('city') || ''
 
     if (sessionStorage.getItem('isEditState') === 'true') {
       userName = sessionStorage.getItem('CloudUserName') || ''
       fullName = sessionStorage.getItem('CloudFullName') || ''
     }
 
-    let _prevCustomScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50)
-        Select @ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}'
+    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}'
         ${errSql}
     `
     let _backCustomScript = `
diff --git a/src/utils/utils-update.js b/src/utils/utils-update.js
index 8c06998..2d851a6 100644
--- a/src/utils/utils-update.js
+++ b/src/utils/utils-update.js
@@ -1,4 +1,59 @@
 /**
+ * @description 鍗囩骇琛ㄥ崟淇℃伅
+ * @param {Object}   config      琛ㄥ崟閰嶇疆淇℃伅
+ * @return {Object}  config
+ */
+export function updateForm (config) {
+  if (!config.version && config.groups) {
+    config.version = '1.0'
+    if (config.groups && config.groups.length > 0) {
+      let fields = []
+      config.groups.forEach(group => {
+        if (group.sublist.length === 0) return
+
+        if (!group.default) {
+          fields.push({
+            type: 'split',
+            label: group.label,
+            uuid: group.uuid,
+            span: 24
+          })
+        }
+
+        fields.push(...group.sublist)
+      })
+      config.fields = fields
+    }
+    delete config.groups
+
+    let _col = config.setting.cols || '2'
+
+    config.fields = config.fields.map(item => {
+      item.labelwidth = 33.3
+      if (_col === '1' || item.entireLine === 'true' || ['textarea','hint','checkcard','brafteditor'].includes(item.type)) {
+        item.span = 24
+        if (_col === '2') {
+          item.labelwidth = 16.3
+        } else if (_col === '3') {
+          item.labelwidth = 10.5
+        } else if (_col === '4') {
+          item.labelwidth = 8.3
+        }
+      } else if (_col === '2') {
+        item.span = 12
+      } else if (_col === '3') {
+        item.span = 8
+      } else if (_col === '4') {
+        item.span = 6
+      }
+      return item
+    })
+  }
+
+  return config
+}
+
+/**
  * @description 鍗囩骇涓昏〃淇℃伅
  * @param {Object}   config      椤甸潰閰嶇疆淇℃伅
  * @return {Object}  config
@@ -73,6 +128,8 @@
     config.action = config.action.map(item => {
       if (item.execMode) {
         item.OpenType = 'funcbutton'
+      } else if (item.OpenType === 'blank') {
+        item.OpenType = 'tab'
       }
       return item
     })
@@ -153,6 +210,8 @@
     config.action = config.action.map(item => {
       if (item.execMode) {
         item.OpenType = 'funcbutton'
+      } else if (item.OpenType === 'blank') {
+        item.OpenType = 'tab'
       }
       return item
     })
diff --git a/src/utils/utils.js b/src/utils/utils.js
index ce58c46..d0a9b07 100644
--- a/src/utils/utils.js
+++ b/src/utils/utils.js
@@ -598,6 +598,10 @@
           key: search.datefield,
           value: '0'
         })
+        options.push({
+          key: search.datefield + '1',
+          value: '0'
+        })
         if (search.transfer === 'true') {
           options.push(item)
         }
@@ -718,6 +722,22 @@
       sql = `select ${item.type === 'checkcard' ? 'top 20' : ''} ${arrfield} from (select distinct ${arrfield} from ${_datasource}) a`
     }
 
+    if (sessionStorage.getItem('dataM') === 'true') { // 鏁版嵁鏉冮檺
+      sql = sql.replace(/\$@/ig, '/*')
+      sql = sql.replace(/@\$/ig, '*/')
+    } else {
+      sql = sql.replace(/@\$|\$@/ig, '')
+    }
+
+    // 澶栬仈鏁版嵁搴撴浛鎹�
+    if (window.GLOB.externalDatabase !== null) {
+      sql = sql.replace(/@db@/ig, window.GLOB.externalDatabase)
+    }
+
+    if ((window.GLOB.systemType !== 'production' && options.sysType !== 'cloud') || window.debugger === true) {
+      console.info(sql)
+    }
+
     return {
       sql: sql,
       field: arrfield
@@ -727,14 +747,17 @@
 
 /**
  * @description 鑾峰彇excel瀵煎叆鍙傛暟
- * @return {String} btn   鎸夐挳
- * @return {String} data  excel鏁版嵁
+ * @return {Object} item   鎸夐挳淇℃伅
+ * @return {Array}  data   excel鏁版嵁
+ * @return {Object} dict   瀛楀吀椤�
+ * @return {String} BID    涓婄骇Id
  */
 export function getExcelInSql (item, data, dict, BID) {
   let btn = item.verify
   let keys = ['delete', 'drop', 'insert', 'truncate', 'update']
   let userName = sessionStorage.getItem('User_Name') || ''
   let fullName = sessionStorage.getItem('Full_Name') || ''
+  let city = sessionStorage.getItem('city') || ''
 
   if (sessionStorage.getItem('isEditState') === 'true') {
     userName = sessionStorage.getItem('CloudUserName') || ''
@@ -968,9 +991,9 @@
     _sql = `
       /* 绯荤粺鐢熸垚 */
       declare @${sheet} table (${declarefields.join(',')},jskey nvarchar(50),BID nvarchar(50) )
-      Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
       
-      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
       ${_initCustomScript}
       `
     _sqlInsert = `Insert into @${sheet} (${fields},jskey,BID)`
@@ -1003,9 +1026,9 @@
     _sql = `
       /* 绯荤粺鐢熸垚 */
       declare @${sheet} table (jskey nvarchar(50))
-      Declare @UserName nvarchar(50),@FullName nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
+      Declare @UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50),@ErrorCode nvarchar(50),@retmsg nvarchar(4000),@tbid Nvarchar(512)
       
-      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}'
+      Select  @ErrorCode='', @retmsg='', @UserName='${userName}', @FullName='${fullName}', @login_city='${city}'
       `
   }
 
@@ -1025,8 +1048,14 @@
 
 /**
  * @description 浣跨敤绯荤粺鍑芥暟鏃讹紙sPC_TableData_InUpDe 锛夛紝鐢熸垚sql璇彞
- * @return {String} type   鎵ц绫诲瀷
- * @return {String} table  琛ㄥ悕
+ * @return {Object}  btn       鎸夐挳淇℃伅
+ * @return {Object}  setting   鑿滃崟鎴栫粍浠舵暟鎹簮璁剧疆
+ * @return {Array}   formdata  琛ㄥ崟
+ * @return {Object}  param     璇锋眰鍙傛暟
+ * @return {Array}   data      鍒楄〃琛屾暟鎹�
+ * @return {Array}   columns   鏄剧ず鍒�
+ * @return {Object}  tab       鏍囩淇℃伅
+ * @return {Boolean} retmsg    鏄惁闇�瑕佹暟鎹繑鍥�
  */
 export function getSysDefaultSql (btn, setting, formdata, param, data, columns, tab, retmsg = false) {
   let primaryId = param.ID
@@ -1184,7 +1213,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),@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),@login_city nvarchar(50),@ModularDetailCode nvarchar(50)${_declarefields}
     `
 
   // 琛ㄥ崟鍙橀噺璧嬪��
@@ -1218,6 +1247,7 @@
 
   let userName = sessionStorage.getItem('User_Name') || ''
   let fullName = sessionStorage.getItem('Full_Name') || ''
+  let city = sessionStorage.getItem('city') || ''
 
   if (sessionStorage.getItem('isEditState') === 'true') {
     userName = sessionStorage.getItem('CloudUserName') || ''
@@ -1227,7 +1257,7 @@
   // 鍒濆鍖栧嚟璇佸強鐢ㄦ埛淇℃伅瀛楁
   _sql += `
       /* 鍑瘉鍙婄敤鎴蜂俊鎭垵濮嬪寲璧嬪�� */
-      select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @BillCode='', @ModularDetailCode=''
+      select @BVoucher='',@FIBVoucherDate='',@FiYear='',@ErrorCode='',@retmsg='',@UserName='${userName}', @FullName='${fullName}', @login_city='${city}', @BillCode='', @ModularDetailCode=''
       `
 
   if (retmsg) {
@@ -1258,7 +1288,7 @@
   }
 
   // 澶辨晥楠岃瘉锛屾坊鍔犳暟鎹椂涓嶇敤
-  if (btn.sqlType !== 'insert' && verify.invalid === 'true' && setting.dataresource) {
+  if (btn.sqlType !== 'insert' && btn.Ot !== 'notRequired' && verify.invalid === 'true' && setting.dataresource) {
     let datasource = setting.dataresource
     if (/\s/.test(datasource) && !/tb$/.test(datasource)) { // 鎷兼帴鍒悕
       datasource = '(' + datasource + ') tb'
@@ -1681,4 +1711,467 @@
   } else {
     return _sql
   }
+}
+
+/**
+ * @description 鍒涘缓瀛樺偍杩囩▼绫�
+ */
+export class FuncUtils {
+  /**
+   * @description 鍒犻櫎瀛樺偍杩囩▼sql
+   * @return {String} name 瀛樺偍杩囩▼鍚嶇О
+   */
+  static dropfunc (name) {
+    return `IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID('${name}') AND type in (N'P', N'PC'))  mdrpk PROCEDURE ${name}`
+  }
+
+  /**
+   * @description 鍒涘缓椤甸潰瀛樺偍杩囩▼
+   * @return {String}
+   */
+  static getTableFunc (param, menu, config) {
+    let form = ''
+    let formParam = ''
+    let _vars = ['bid', 'pageindex', 'pagesize', 'ordercol', 'ordertype', 'exceltype', 'septmenuno', 'lang', 'debug', 'loginuid', 'sessionuid', 'userid', 'errorcode', 'retmsg']
+    let _columns = []
+    let primaryKey = config.setting.primaryKey || 'ID'
+
+    if (!_vars.includes(primaryKey.toLowerCase())) {
+      _vars.push(primaryKey.toLowerCase())
+      formParam = `mchr13k@${primaryKey} nvarchar(50)='',`
+    }
+
+    if (config.search && config.search.length > 0) {
+      let _fields = new Map()
+      config.search.forEach(item => {
+        if (item.field) {
+          let type = ''
+
+          if (item.type.match(/date/ig)) {
+            type = 'datetime=null'
+          } else {
+            type = 'nvarchar(50)=\'\''
+          }
+
+          item.field.split(',').forEach(cell => {
+            let _f = cell
+            if (_fields.has(cell)) {
+              _f = _f + '1'
+            }
+  
+            _fields.set(cell, true)
+
+            if (!_vars.includes(_f.toLowerCase())) {
+              _vars.push(_f.toLowerCase())
+              formParam = formParam + `mchr13k@${_f} ${type},`
+            }
+          })
+        }
+      })
+    }
+
+    if (config.columns && config.columns.length > 0) {
+      config.columns.forEach(item => {
+        if (item.field) {
+          _columns.push(`${item.field} as ${item.label}`)
+        }
+      })
+
+      form = `
+        declare @dc table (${_columns.join(',')})
+        
+        @tableid ='${menu.MenuID}'
+      `
+    }
+
+    let Ltext = `create proc ${param.innerFunc}
+    ( /*${menu.MenuName}*/
+    @appkey nvarchar(50)='',
+    @BID nvarchar(50)='',${formParam}
+    @PageIndex nvarchar(50)='',
+    @PageSize nvarchar(50)='',
+    @OrderCol nvarchar(50)='',
+    @OrderType nvarchar(50)='',
+    @exceltype nvarchar(50)='',
+    @sEPTMenuNo nvarchar(50)='${menu.MenuNo}',
+    @lang nvarchar(50)='',
+    @debug nvarchar(50)='',
+    @LoginUID nvarchar(50)='',
+    @SessionUid nvarchar(50)='',
+    @UserID nvarchar(50),
+    @ErrorCode nvarchar(50) out,
+    @retmsg nvarchar(4000) out
+    )
+    as
+    begin
+    declare  @BegindateTest datetime,@EnddateTest datetime
+    select  @BegindateTest=getdate()
+    set @ErrorCode=''
+    set @retmsg=''
+    BEGIN TRY
+      /*浜嬪姟鎿嶄綔*/
+      BEGIN TRAN
+        /*鍏蜂綋涓氬姟鎿嶄綔*/
+        
+        /* 
+        select top 10 * from sProcExcep order by id desc
+        
+        declare @UserName  nvarchar(50),@FullName nvarchar(50)
+        
+        select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
+        ${form}
+        if 1=2
+        begin
+          set @ErrorCode='E'
+          set @retmsg='鍦ㄦ鍐欐姤閿�'
+          goto GOTO_RETURN
+        end
+        
+        insert into sNote (remark,createuserid,CreateUser,CreateStaff)
+        select '鍦ㄦ鍐欐棩蹇�',@UserID,@UserName,@FullName
+        */
+        
+      COMMIT TRAN
+      SET NOCOUNT ON
+      RETURN
+    END TRY
+    BEGIN CATCH
+      /*閿欒澶勭悊*/
+      ROLLBACK TRAN
+      DECLARE @ErrorMessage NVARCHAR(4000);
+      DECLARE @ErrorSeverity INT;
+      DECLARE @ErrorState INT;
+      
+      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
+      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
+      SET @retmsg=ERROR_MESSAGE();
+      SELECT @ErrorMessage=ERROR_MESSAGE(),
+        @ErrorSeverity=ERROR_SEVERITY(),
+        @ErrorState=ERROR_STATE();
+        
+      RAISERROR(@ErrorMessage, /*-- Message text.*/
+        @ErrorSeverity, /*-- Severity.*/
+        @ErrorState  /*-- State.*/
+      );
+    END CATCH
+    
+    GOTO_RETURN:
+      ROLLBACK TRAN
+      
+    END`
+
+    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
+
+    return Ltext
+  }
+
+  /**
+   * @description 鍒涘缓瀛樺偍杩囩▼
+   * @return {String}
+   */
+  static getfunc (param, btn, menu, config) {
+    let form = ''
+    let formParam = ''
+    let _vars = ['bid', 'septmenuno', 'lang', 'debug', 'loginuid', 'sessionuid', 'userid', 'errorcode', 'retmsg']
+    let columns = config.columns
+    let primaryKey = config.setting.primaryKey || 'ID'
+
+    if (!_vars.includes(primaryKey.toLowerCase())) {
+      _vars.push(primaryKey.toLowerCase())
+      let _type = '50'
+      if (btn.Ot === 'requiredOnce') { // 澶氳鎷兼帴鏃讹紝涓婚敭璁句负max
+        _type = 'max'
+      }
+      formParam = `mchr13k@${primaryKey} nvarchar(${_type})='',`
+    }
+
+    if (param.fields && param.fields.length > 0) {
+      let _fields = []
+      param.fields.forEach(item => {
+        if (item.field) {
+          let type = ''
+          if (item.type.match(/date/ig)) {
+            type = 'datetime=null'
+          } else if (item.type === 'number') {
+            type = `decimal(18,${item.decimal})=0`
+          } else {
+            type = 'nvarchar(50)=\'\''
+          }
+
+          if (!_vars.includes(item.field.toLowerCase())) {
+            _vars.push(item.field.toLowerCase())
+            formParam = formParam + `mchr13k@${item.field} ${type},`
+          }
+
+          _fields.push(item.field)
+        }
+      })
+
+      let field1 = _fields.join(',')
+      let field2 = _fields.join(',@')
+      let field3 = _fields.map(cell => {
+        return cell + '=@' + cell
+      })
+
+      field2 = field2 ? '@' + field2 : ''
+      field3 = field3.join(',')
+
+      form = `
+        insert into ${param.name} (${field1},createuserid) select ${field2},@UserID
+        
+        update ${param.name} set ${field3},modifydate=getdate(),modifyuserid=@UserID
+      `
+    } else if (btn.OpenType === 'prompt' || btn.OpenType === 'exec') {
+      form = `
+        update ${param.name} set ModifyDate=getdate(),ModifyUserID=@UserID where ${primaryKey}=@${primaryKey}
+      `
+    }
+
+    if (columns) {
+      let _col = []
+      let _field = []
+      columns.forEach(col => {
+        if (col.field) {
+          if (col.type === 'number') {
+            _col.push(col.field + ' decimal(18,2)')
+          } else {
+            _col.push(col.field + ' nvarchar(50)')
+          }
+          _field.push(col.field)
+        }
+      })
+      _col = _col.join(',')
+      _field = _field.join(',')
+
+      form = form + `
+        declare @dc table (${_col})
+        
+        insert into @dc (${_field})
+
+        @tableid ='${menu.MenuID}'
+      `
+    }
+
+    // 鎵撳嵃鑷畾涔夋ā鏉垮瓧娈垫彁绀�
+    let _printRemark = ''
+    if (btn.funcType === 'print') {
+      _printRemark = '/* 鑷畾涔夋暟鎹墦鍗版ā鏉挎椂锛岃浣跨敤TemplateID瀛楁 */'
+    }
+
+    let Ltext = `create proc ${param.funcName}
+    ( /*${menu.MenuName}  ${btn.label}*/
+    @appkey nvarchar(50)='',
+    @BID nvarchar(50)='',${formParam}
+    @sEPTMenuNo nvarchar(50)='${param.menuNo}',
+    @lang nvarchar(50)='',
+    @debug nvarchar(50)='',
+    @LoginUID nvarchar(50)='',
+    @SessionUid nvarchar(50)='',
+    @UserID nvarchar(50),
+    @ErrorCode nvarchar(50) out,
+    @retmsg nvarchar(4000) out
+    )
+    as
+    begin
+    declare  @BegindateTest datetime,@EnddateTest datetime
+    select  @BegindateTest=getdate()
+    set @ErrorCode=''
+    set @retmsg=''
+    BEGIN TRY
+      /*浜嬪姟鎿嶄綔*/
+      BEGIN TRAN
+        /*鍏蜂綋涓氬姟鎿嶄綔*/
+        ${_printRemark}
+        /* 
+        select top 10 * from sProcExcep order by id desc
+        
+        declare @UserName  nvarchar(50),@FullName nvarchar(50)
+        
+        select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
+        ${form}
+        if 1=2
+        begin
+          set @ErrorCode='E'
+          set @retmsg='鍦ㄦ鍐欐姤閿�'
+          goto GOTO_RETURN
+        end
+        
+        insert into sNote (remark,createuserid,CreateUser,CreateStaff)
+        select '鍦ㄦ鍐欐棩蹇�',@UserID,@UserName,@FullName
+        */
+        
+      COMMIT TRAN
+      SET NOCOUNT ON
+      RETURN
+    END TRY
+    BEGIN CATCH
+      /*閿欒澶勭悊*/
+      ROLLBACK TRAN
+      DECLARE @ErrorMessage NVARCHAR(4000);
+      DECLARE @ErrorSeverity INT;
+      DECLARE @ErrorState INT;
+      
+      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
+      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
+      SET @retmsg=ERROR_MESSAGE();
+      SELECT @ErrorMessage=ERROR_MESSAGE(),
+        @ErrorSeverity=ERROR_SEVERITY(),
+        @ErrorState=ERROR_STATE();
+        
+      RAISERROR(@ErrorMessage, /*-- Message text.*/
+        @ErrorSeverity, /*-- Severity.*/
+        @ErrorState  /*-- State.*/
+      );
+    END CATCH
+    
+    GOTO_RETURN:
+      ROLLBACK TRAN
+      
+    END`
+
+    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
+
+    return Ltext
+  }
+
+  /**
+   * @description 鍒涘缓瀵煎叆瀛樺偍杩囩▼
+   * @return {String}
+   */
+  static getexcelInfunc (param, btn, menu) {
+    let _verify = btn.verify
+
+    let _uniquesql = ''
+    if (_verify.uniques && _verify.uniques.length > 0) {
+      _verify.uniques.forEach(unique => {
+        if (unique.status === 'false') return
+
+        let _fields = unique.field.split(',')
+        let _fields_ = _fields.map(_field => {
+          return `a.${_field}=b.${_field}`
+        })
+        _fields_ = _fields_.join(' and ')
+
+        if (unique.verifyType !== 'physical') {
+          _fields_ += ' and b.deleted=0'
+        }
+
+        _uniquesql += `
+        Set @tbid=''
+        Select top 1 @tbid=${_fields.join('+\' \'+')} from (select 1 as n,${unique.field} from @${btn.sheet} ) a group by ${unique.field} having sum(n)>1
+
+        If @tbid!=''
+        Begin
+          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 閲嶅'
+          goto aaa
+        end
+
+        Set @tbid=''
+        Select top 1 @tbid=${_fields.join('+\' \'+')} from  @${btn.sheet} a
+        Inner join ${btn.sheet} b on ${_fields_}
+
+        If @tbid!=''
+        Begin
+          select @ErrorCode='${unique.errorCode}',@retmsg=@tbid+' 涓庡凡鏈夋暟鎹噸澶�'
+          goto aaa
+        end
+        `
+      })
+
+      if (_uniquesql) {
+        _uniquesql = `
+        Declare @tbid Nvarchar(512)
+        ${_uniquesql}`
+      }
+    }
+
+    let declarefields = []
+    let fields = []
+
+    _verify.columns.forEach(col => {
+      declarefields.push(`${col.Column} ${col.type}`)
+      fields.push(col.Column)
+    })
+
+    fields = fields.join(',')
+
+    let _sql = `declare @${btn.sheet} table (${declarefields.join(',')},jskey nvarchar(50))
+      Declare @UserName nvarchar(50),@FullName nvarchar(50)
+      
+      select @UserName=UserName,@FullName=FullName from SUsers where UID=@UserID
+      
+      Insert into @${btn.sheet} (${fields},jskey)
+
+      exec s_KeyWords_Replace
+      @LText=@LText, @BID=@BID,@LoginUID=@LoginUID,@SessionUid=@SessionUid,@UserID=@UserID,@ID=@ID
+      ${_uniquesql}
+      Insert into ${btn.sheet} (${fields},createuserid,createuser,createstaff,bid) 
+      Select ${fields},@userid,@username,@fullname,@BID From @${btn.sheet}
+
+      Delete @${btn.sheet}`
+
+    let Ltext = `create proc ${param.funcName}
+    ( /*${menu.MenuName}  ${btn.label}*/
+      @appkey nvarchar(50)='',
+      @ID nvarchar(50)='',
+      @BID nvarchar(50)='',
+      @Ltext nvarchar(max)='',
+      @sEPTMenuNo nvarchar(50)='${param.menuNo}', 
+      @secretkey nvarchar(50)='',
+      @timestamp nvarchar(50)='',
+      @lang nvarchar(50)='',
+      @LoginUID nvarchar(50)='',
+      @SessionUid nvarchar(50)='',
+      @UserID nvarchar(50), 
+      @ErrorCode nvarchar(50) out,
+      @retmsg nvarchar(4000) out
+    )
+    as
+    begin
+    declare @BegindateTest datetime,@EnddateTest datetime 
+    select @BegindateTest=getdate() 
+    set @ErrorCode=''
+    set @retmsg=''
+    BEGIN TRY
+      /*浜嬪姟鎿嶄綔*/
+      BEGIN TRAN
+        /*鍏蜂綋涓氬姟鎿嶄綔*/
+
+        /* 
+        ${_sql}
+        */
+        
+      COMMIT TRAN
+      SET NOCOUNT ON
+      RETURN
+    END TRY
+    BEGIN CATCH
+      /*閿欒澶勭悊*/
+      ROLLBACK TRAN
+      DECLARE @ErrorMessage NVARCHAR(4000);
+      DECLARE @ErrorSeverity INT;
+      DECLARE @ErrorState INT;
+      
+      /*鎶婅嚜瀹氫箟鐨勫弸濂界殑閿欒淇℃伅鎻愮ず鍔犱笂*/
+      set @ErrorCode=cast(ERROR_NUMBER() as nvarchar(50))
+      SET @retmsg=ERROR_MESSAGE();
+      SELECT @ErrorMessage=ERROR_MESSAGE(),
+        @ErrorSeverity=ERROR_SEVERITY(),
+        @ErrorState=ERROR_STATE();
+        
+      RAISERROR(@ErrorMessage, /*-- Message text.*/
+        @ErrorSeverity, /*-- Severity.*/
+        @ErrorState  /*-- State.*/
+      );
+    END CATCH
+    
+    GOTO_RETURN:
+      ROLLBACK TRAN
+      
+    END`
+
+    Ltext = Ltext.replace(/\n\s{4}/ig, 'mchr13k')
+
+    return Ltext
+  }
 }
\ No newline at end of file
diff --git a/src/views/appmanage/index.jsx b/src/views/appmanage/index.jsx
new file mode 100644
index 0000000..3efc3ba
--- /dev/null
+++ b/src/views/appmanage/index.jsx
@@ -0,0 +1,587 @@
+import React, {Component} from 'react'
+import { fromJS } from 'immutable'
+import { Spin, notification, Button, Table, Modal, ConfigProvider } from 'antd'
+import moment from 'moment'
+import md5 from 'md5'
+import enUS from 'antd/es/locale/en_US'
+import zhCN from 'antd/es/locale/zh_CN'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const { confirm } = Modal
+
+const _locale = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Header = asyncComponent(() => import('@/mob/header'))
+const MutilForm = asyncComponent(() => import('./mutilform'))
+const SubMutilForm = asyncComponent(() => import('./submutilform'))
+
+class AppManage extends Component {
+  state = {
+    loading: false,
+    applist: [],
+    columns: [
+      { title: '搴旂敤鍚嶇О', dataIndex: 'remark', key: 'remark', align: 'center' },
+      { title: '搴旂敤缂栫爜', dataIndex: 'kei_no', key: 'kei_no', align: 'center' },
+      {
+        title: '鎿嶄綔',
+        key: 'action',
+        align: 'center',
+        render: (text, record) => (<Button type="link" onClick={() => this.deleteApp(record)} style={{color: '#ff4d4f'}}>鍒犻櫎</Button>),
+      },
+    ],
+    subcolumns: [
+      {
+        title: '搴旂敤绫诲瀷', dataIndex: 'typename', key: 'typename', align: 'center'
+      },
+      {
+        title: '璇█', dataIndex: 'lang', key: 'lang', align: 'center',
+        render: (text, record) => text === 'en-US' ? '鑻辨枃' : '涓枃'
+      },
+      {
+        title: '鐧诲綍', dataIndex: 'login_types', key: 'login_types', align: 'center',
+        render: (text, record) => text === 'false' ? '涓嶉渶瑕�' : '闇�瑕�'
+      },
+      {
+        title: '鏉冮檺绠$悊', dataIndex: 'role_type', key: 'role_type', align: 'center',
+        render: (text, record) => text === 'false' ? '涓嶅惎鐢�' : '鍚敤'
+      },
+      {
+        title: '鐭繛鎺�', dataIndex: 'link_type', key: 'link_type', align: 'center',
+        render: (text, record) => text === 'false' ? '涓嶅惎鐢�' : '鍚敤'
+      },
+      {
+        title: '鐨偆', dataIndex: 'css', key: 'css', align: 'center',
+        render: (text, record) => {
+          const style = {
+            bg_black_style_blue: '钃濋粦鑹茬郴',
+            bg_white_style_blue: '钃濈櫧鑹茬郴',
+            bg_black_style_red: '绾㈤粦鑹茬郴',
+            bg_white_style_red: '绾㈢櫧鑹茬郴',
+            bg_black_style_orange_red: '姗欑孩榛戣壊绯�',
+            bg_white_style_orange_red: '姗欑孩鐧借壊绯�',
+            bg_black_style_orange: '姗欓粦鑹茬郴',
+            bg_white_style_orange: '姗欑櫧鑹茬郴',
+            bg_black_style_orange_yellow: '姗欓粍榛戣壊绯�',
+            bg_white_style_orange_yellow: '姗欓粍鐧借壊绯�',
+            bg_black_style_yellow: '榛勯粦鑹茬郴',
+            bg_white_style_yellow: '榛勭櫧鑹茬郴',
+            bg_black_style_yellow_green: '榛勭豢榛戣壊绯�',
+            bg_white_style_yellow_green: '榛勭豢鐧借壊绯�',
+            bg_black_style_green: '缁块粦鑹茬郴',
+            bg_white_style_green: '缁跨櫧鑹茬郴',
+            bg_black_style_cyan: '闈掗粦鑹茬郴',
+            bg_white_style_cyan: '闈掔櫧鑹茬郴',
+            bg_black_style_blue_purple: '钃濈传榛戣壊绯�',
+            bg_white_style_blue_purple: '钃濈传鐧借壊绯�',
+            bg_black_style_purple: '绱粦鑹茬郴',
+            bg_white_style_purple: '绱櫧鑹茬郴',
+            bg_black_style_magenta: '娲嬬孩榛戣壊绯�',
+            bg_white_style_magenta: '娲嬬孩鐧借壊绯�',
+            bg_black_style_grass_green: '鑽夌豢榛戣壊绯�',
+            bg_white_style_grass_green: '鑽夌豢鐧借壊绯�',
+            bg_black_style_deep_red: '娣辩孩榛戣壊绯�',
+            bg_white_style_deep_red: '娣辩孩鐧借壊绯�'
+          }
+
+          return style[text] || '钃濋粦鑹茬郴'
+        }
+      },
+      {
+        title: '鏍囬', dataIndex: 'title', key: 'title', align: 'center', width: '170px'
+      },
+      {
+        title: '鍥炬爣', dataIndex: 'favicon', key: 'favicon', align: 'center', width: '120px',
+        render: (text, record) => (text ? <img style={{width: '32px', height: '32px'}} src={text} alt="" /> : null)
+      },
+      {
+        title: '鎿嶄綔',
+        key: 'action',
+        align: 'center',
+        width: '190px',
+        render: (text, record) => (
+          <div>
+            <Button type="link" onClick={() => this.deleteSubApp(record)} style={{color: '#ff4d4f'}}>鍒犻櫎</Button>
+            <Button type="link" onClick={() => this.jumpApp(record)}>缂栬緫搴旂敤</Button>
+          </div>
+        )
+      },
+    ],
+    selectApp: null,
+    selectSubApp: null,
+    selectedRowKeys: [],
+    selectedSubRowKeys: [],
+    visible: false,
+    subVisible: false
+  }
+
+  UNSAFE_componentWillMount() {
+    document.body.className = ''
+    this.getAppList()
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  getAppList = () => {
+    let param = {
+      func: 's_get_kei'
+    }
+
+    Api.getCloudConfig(param).then(result => {
+      if (result.status) {
+        let selectApp = null
+        let applist = result.data.map(item => {
+          item.sublist = item.data_detail || []
+          item.sublist = item.sublist.map(cell => {
+            cell.ID = cell.d_id
+            return cell
+          })
+
+          if (this.state.selectApp && this.state.selectApp.ID === item.ID) {
+            selectApp = item
+          }
+
+          return item
+        })
+
+        this.setState({
+          loading: false,
+          applist: applist,
+          selectApp
+        })
+
+      } else {
+        this.setState({
+          loading: false
+        })
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+      }
+    })
+  }
+
+  deleteApp = (record) => {
+    const _this = this
+
+    confirm({
+      content: '纭畾鍒犻櫎銆�' + record.remark + '銆嬪悧锛�',
+      onOk() {
+        return new Promise(resolve => {
+          let param = {
+            func: 's_kei_del',
+            ID: record.ID,
+            kei_no: record.kei_no
+          }
+    
+          Api.getCloudConfig(param).then(result => {
+            if (result.status) {
+              notification.success({
+                top: 92,
+                message: '鍒犻櫎鎴愬姛锛�',
+                duration: 5
+              })
+
+              _this.setState({
+                selectedRowKeys: [],
+                selectedSubRowKeys: [],
+                selectApp: null,
+                selectSubApp: null,
+                loading: true
+              })
+              _this.getAppList()
+            } else {
+              notification.warning({
+                top: 92,
+                message: result.message,
+                duration: 5
+              })
+            }
+            resolve()
+          }, () => {
+            resolve()
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+  
+  deleteSubApp = (record) => {
+    const { selectApp } = this.state
+    const _this = this
+
+    let param = {
+      func: 's_kei_addupt',
+      ID: selectApp.ID,
+      exec_type: 'y',
+      remark: selectApp.remark,
+      kei_no: selectApp.kei_no,
+      LText: ''
+    }
+
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+
+    let sublist = fromJS(selectApp.sublist).toJS()
+    sublist = sublist.filter(item => item.ID !== record.ID)
+
+    param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}'`)
+    param.LText = param.LText.join(' union all ')
+    param.LText = Utils.formatOptions(param.LText)
+    
+    confirm({
+      content: '纭畾鍒犻櫎璇ュ瓙搴旂敤鍚楋紵',
+      onOk() {
+        return new Promise(resolve => {
+          Api.getCloudConfig(param).then(result => {
+            if (result.status) {
+              notification.success({
+                top: 92,
+                message: '鎿嶄綔鎴愬姛锛�',
+                duration: 5
+              })
+      
+              _this.setState({
+                selectedSubRowKeys: [],
+                selectSubApp: null,
+                loading: true
+              })
+              _this.getAppList()
+            } else {
+              notification.warning({
+                top: 92,
+                message: result.message,
+                duration: 5
+              })
+            }
+            resolve()
+          }, () => {
+            resolve()
+          })
+        })
+      },
+      onCancel() {}
+    })
+  }
+  
+  jumpApp = (item) => {
+    const { selectApp } = this.state
+
+    let route = 'mobdesign'
+    if (item.typename === 'pc') {
+      route = 'pcdesign'
+    }
+
+    window.open(window.location.href.replace(/#.+/ig, `#/${route}/${window.btoa(window.encodeURIComponent(JSON.stringify({...item, kei_no: selectApp.kei_no, remark: selectApp.remark, type: 'app'})))}`))
+  }
+
+  /**
+   * 
+   */
+  onSelectChange = selectedRowKeys => {
+    const { applist } = this.state
+    let selectApp = applist.filter(item => item.ID === selectedRowKeys[0])[0]
+
+    this.setState({ selectedRowKeys, selectApp })
+  }
+
+  /**
+   * 
+   */
+  onSubChange = selectedSubRowKeys => {
+    this.setState({ selectedSubRowKeys })
+  }
+
+  /**
+   * @description 鐐瑰嚮鏁磋锛岃Е鍙戝垏鎹紝 鍒ゆ柇鏄惁鍙�夛紝鍗曢�夋垨澶氶�夛紝杩涜瀵瑰簲鎿嶄綔
+   */
+  changeRow = (record) => {
+    this.setState({ selectedRowKeys: [record.ID], selectApp: record })
+  }
+
+  /**
+   * @description 鐐瑰嚮鏁磋锛岃Е鍙戝垏鎹紝 鍒ゆ柇鏄惁鍙�夛紝鍗曢�夋垨澶氶�夛紝杩涜瀵瑰簲鎿嶄綔
+   */
+  changeSubRow = (record) => {
+    this.setState({ selectedSubRowKeys: [record.ID], selectSubApp: record })
+  }
+
+  trigerApp = (type) => {
+    if (type === 'edit' && !this.state.selectApp) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨闇�瑕佺紪杈戠殑搴旂敤锛�',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({
+      visible: type
+    })
+  }
+
+  trigerSubApp = (type) => {
+    if (type === 'edit' && !this.state.selectSubApp) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨闇�瑕佺紪杈戠殑瀛愬簲鐢紒',
+        duration: 5
+      })
+      return
+    } else if (!this.state.selectApp) {
+      notification.warning({
+        top: 92,
+        message: '璇烽�夋嫨搴旂敤锛�',
+        duration: 5
+      })
+      return
+    }
+
+    this.setState({
+      subVisible: type
+    })
+  }
+
+  
+  submitCard = () => {
+    const { selectApp, visible } = this.state
+
+    this.mobcardRef.handleConfirm().then(res => {
+      this.setState({
+        confirmloading: true
+      })
+
+      let ID = ''
+      if (visible === 'edit') {
+        ID = selectApp.ID
+      } else {
+        ID = md5(window.GLOB.appkey + res.kei_no)
+      }
+
+      let param = {
+        func: 's_kei_addupt',
+        ID: ID,
+        exec_type: 'y',
+        remark: res.remark,
+        kei_no: res.kei_no,
+        LText: ''
+      }
+
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+
+      if (visible === 'edit') {
+        param.LText = selectApp.sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}'`)
+        param.LText = param.LText.join(' union all ')
+        param.LText = Utils.formatOptions(param.LText)
+      }
+
+      Api.getCloudConfig(param).then(result => {
+        if (result.status) {
+          notification.success({
+            top: 92,
+            message: '鎿嶄綔鎴愬姛锛�',
+            duration: 5
+          })
+
+          this.setState({
+            selectedRowKeys: [],
+            selectedSubRowKeys: [],
+            selectApp: null,
+            selectSubApp: null,
+            confirmloading: false,
+            visible: false,
+            loading: true
+          })
+          this.getAppList()
+        } else {
+          this.setState({
+            confirmloading: false
+          })
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+        }
+      }, () => {
+        this.setState({
+          confirmloading: false
+        })
+      })
+    })
+  }
+
+  submitSubCard = () => {
+    const { selectApp, subVisible, selectSubApp } = this.state
+
+    this.submobcardRef.handleConfirm().then(res => {
+      if (subVisible === 'plus' && selectApp.sublist.filter(item => item.typename === res.typename && item.lang === res.lang).length > 0) {
+        notification.warning({
+          top: 92,
+          message: '搴旂敤绫诲瀷宸插瓨鍦紒',
+          duration: 5
+        })
+        return
+      }
+      this.setState({
+        confirmloading: true
+      })
+
+      let param = {
+        func: 's_kei_addupt',
+        ID: selectApp.ID,
+        exec_type: 'y',
+        remark: selectApp.remark,
+        kei_no: selectApp.kei_no,
+        LText: ''
+      }
+
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+
+      let sublist = fromJS(selectApp.sublist).toJS()
+      if (subVisible === 'plus') {
+        sublist.unshift({
+          ID: md5(window.GLOB.appkey + selectApp.kei_no + res.typename + res.lang),
+          ...res
+        })
+      } else {
+        res.ID = selectSubApp.ID
+        sublist = sublist.map(item => {
+          if (item.ID !== res.ID) {
+            return item
+          } else {
+            return res
+          }
+        })
+      }
+
+      param.LText = sublist.map(item => `select '${item.ID}','${item.typename}','${selectApp.ID}','${sessionStorage.getItem('CloudUserID') || ''}','${window.GLOB.appkey || ''}','${item.login_types || 'true'}','${item.link_type || 'true'}','${item.role_type || 'true'}','${item.lang || 'zh-CN'}','${item.css || ''}','${item.title || ''}','${item.favicon || ''}'`)
+      param.LText = param.LText.join(' union all ')
+      param.LText = Utils.formatOptions(param.LText)
+
+      Api.getCloudConfig(param).then(result => {
+        if (result.status) {
+          notification.success({
+            top: 92,
+            message: '鎿嶄綔鎴愬姛锛�',
+            duration: 5
+          })
+
+          this.setState({
+            selectedSubRowKeys: [],
+            selectSubApp: null,
+            confirmloading: false,
+            subVisible: false,
+            loading: true
+          })
+          this.getAppList()
+        } else {
+          this.setState({
+            confirmloading: false
+          })
+          notification.warning({
+            top: 92,
+            message: result.message,
+            duration: 5
+          })
+        }
+      }, () => {
+        this.setState({
+          confirmloading: false
+        })
+      })
+    })
+  }
+
+  render () {
+    const { loading, visible, subVisible, columns, applist, selectedRowKeys, selectedSubRowKeys, subcolumns, selectApp, selectSubApp } = this.state
+
+    return (
+      <div className="mk-app-manage">
+        <ConfigProvider locale={_locale}>
+          <Header view="manage" />
+          {loading ?
+            <div className="loading-mask">
+              <div className="ant-spin-blur"></div>
+              <Spin />
+            </div> : null
+          }
+          <div className="app-table">
+            <div className="app-action">
+              <Button className="mk-green" onClick={() => this.trigerApp('plus')}>娣诲姞</Button>
+              <Button className="mk-purple" onClick={() => this.trigerApp('edit')}>淇敼</Button>
+            </div>
+            <Table
+              rowKey="ID"
+              columns={columns}
+              dataSource={applist}
+              pagination={false}
+              rowSelection={{ type: 'radio', selectedRowKeys, onChange: this.onSelectChange }}
+              onRow={(record) => ({ onClick: () => {this.changeRow(record)} })}
+            />
+          </div>
+          <div className="app-table">
+            <div className="sub-app-title"><span>瀛愬簲鐢�</span></div>
+            <div className="app-action">
+              <Button className="mk-green" onClick={() => this.trigerSubApp('plus')}>娣诲姞</Button>
+              <Button className="mk-purple" onClick={() => this.trigerSubApp('edit')}>淇敼</Button>
+            </div>
+            <Table
+              rowKey="ID"
+              columns={subcolumns}
+              dataSource={selectApp ? selectApp.sublist : []}
+              pagination={false}
+              rowSelection={{ type: 'radio', selectedRowKeys: selectedSubRowKeys, onChange: this.onSubChange }}
+              onRow={(record) => ({ onClick: () => {this.changeSubRow(record)} })}
+            />
+          </div>
+          <Modal
+            title={'缂栬緫搴旂敤'}
+            width={'600px'}
+            maskClosable={false}
+            visible={!!visible}
+            onCancel={() => this.setState({visible: false})}
+            confirmLoading={this.state.confirmloading}
+            onOk={this.submitCard}
+            cancelText="鍙栨秷"
+            okText="纭畾"
+            destroyOnClose
+          >
+            <MutilForm type={visible} card={visible === 'edit' ? selectApp : ''} wrappedComponentRef={(inst) => this.mobcardRef = inst} inputSubmit={this.submitCard} />
+          </Modal>
+          <Modal
+            title={'缂栬緫瀛愬簲鐢�'}
+            width={'750px'}
+            maskClosable={false}
+            visible={!!subVisible}
+            onCancel={() => this.setState({subVisible: false})}
+            confirmLoading={this.state.confirmloading}
+            onOk={this.submitSubCard}
+            cancelText="鍙栨秷"
+            okText="纭畾"
+            destroyOnClose
+          >
+            <SubMutilForm type={subVisible} card={subVisible === 'edit' ? selectSubApp : ''} wrappedComponentRef={(inst) => this.submobcardRef = inst} inputSubmit={this.submitSubCard} />
+          </Modal>
+        </ConfigProvider>
+      </div>
+    )
+  }
+}
+
+export default AppManage
\ No newline at end of file
diff --git a/src/views/appmanage/index.scss b/src/views/appmanage/index.scss
new file mode 100644
index 0000000..b1aa6a4
--- /dev/null
+++ b/src/views/appmanage/index.scss
@@ -0,0 +1,41 @@
+.mk-app-manage {
+  background: #fff;
+  min-height: 100vh;
+  padding-bottom: 70px;
+
+  .mob-header-container {
+    padding-right: 0px;
+    position: relative;
+    z-index: 10;
+  }
+  .app-table {
+    max-width: 1440px;
+    margin: 0 auto;
+    padding: 40px 30px 20px;
+    .sub-app-title {
+      margin-bottom: 15px;
+      border-bottom: 1px solid #e8e8e8;
+      span {
+        position: relative;
+        top: 1px;
+        display: inline-block;
+        color: #1890ff;
+        padding: 10px 15px 5px;
+        border-bottom: 2px solid #1890ff;
+      }
+    }
+  }
+  .ant-table-wrapper {
+    border: 1px solid #e8e8e8;
+    border-bottom: 0;
+    border-radius: 4px;
+    .ant-table-tbody > tr.ant-table-row-selected td {
+      background: #bae7ff;
+    }
+  }
+  .app-action {
+    button {
+      margin: 0px 15px 15px 0px;
+    }
+  }
+}
diff --git a/src/views/appmanage/mutilform/index.jsx b/src/views/appmanage/mutilform/index.jsx
new file mode 100644
index 0000000..f27151f
--- /dev/null
+++ b/src/views/appmanage/mutilform/index.jsx
@@ -0,0 +1,89 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input } from 'antd'
+import './index.scss'
+
+class MainSearch extends Component {
+  static propTpyes = {
+    type: PropTypes.any,         // 缂栬緫绫诲瀷
+    card: PropTypes.any,         // 缂栬緫搴旂敤
+    inputSubmit: PropTypes.func  // input鍥炶溅鎻愪氦
+  }
+
+  state = {}
+
+  /**
+   * @description 鑾峰彇琛ㄥ崟鍊�
+   */
+  handleConfirm = () => {
+    return new Promise(resolve => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          resolve(values)
+        }
+      })
+    })
+  }
+
+  /**
+   * @description 鍥炶溅鎻愪氦
+   */
+  handleSubmit = (e) => {
+    e.preventDefault()
+    this.props.inputSubmit()
+  }
+
+  render() {
+    const { card, type } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 12 }
+      }
+    }
+    return (
+      <Form {...formItemLayout} className="mob-card-edit-form">
+        <Row gutter={24}>
+          <Col span={24}>
+            <Form.Item label="搴旂敤鍚�">
+              {getFieldDecorator('remark', {
+                initialValue: card ? card.remark : '',
+                rules: [{
+                  required: true,
+                  message: '璇疯緭鍏ュ簲鐢ㄥ悕!'
+                }, {
+                  max: 20,
+                  message: '搴旂敤鍚嶄笉鍙秴杩�20涓瓧绗�!'
+                }]
+              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label="搴旂敤缂栫爜">
+              {getFieldDecorator('kei_no', {
+                initialValue: card ? card.kei_no : '',
+                rules: [{
+                  required: true,
+                  message: '璇疯緭鍏ュ簲鐢ㄧ紪鐮�!'
+                }, {
+                  pattern: /^[a-zA-Z_]*$/ig,
+                  message: '搴旂敤缂栫爜鍙厑璁稿寘鍚ぇ灏忓啓瀛楁瘝鍙奯!'
+                }, {
+                  max: 10,
+                  message: '搴旂敤缂栫爜涓嶅彲瓒呰繃10涓瓧绗�!'
+                }]
+              })(<Input placeholder="" disabled={type === 'edit'} autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(MainSearch)
\ No newline at end of file
diff --git a/src/mob/mobcard/mutilform/index.scss b/src/views/appmanage/mutilform/index.scss
similarity index 100%
rename from src/mob/mobcard/mutilform/index.scss
rename to src/views/appmanage/mutilform/index.scss
diff --git a/src/views/appmanage/submutilform/index.jsx b/src/views/appmanage/submutilform/index.jsx
new file mode 100644
index 0000000..712464f
--- /dev/null
+++ b/src/views/appmanage/submutilform/index.jsx
@@ -0,0 +1,192 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Select, Radio, Input } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import './index.scss'
+
+const FileUpload = asyncComponent(() => import('@/tabviews/zshare/fileupload'))
+
+class MainSearch extends Component {
+  static propTpyes = {
+    type: PropTypes.any,         // 缂栬緫绫诲瀷
+    card: PropTypes.any,         // 缂栬緫搴旂敤
+    inputSubmit: PropTypes.func  // input鍥炶溅鎻愪氦
+  }
+
+  state = {}
+
+  /**
+   * @description 鑾峰彇琛ㄥ崟鍊�
+   */
+  handleConfirm = () => {
+    return new Promise(resolve => {
+      this.props.form.validateFieldsAndScroll((err, values) => {
+        if (!err) {
+          if (values.favicon && values.favicon.length > 0 && values.favicon[0].status === 'done') {
+            values.favicon = values.favicon[0].response || values.favicon[0].url || ''
+          } else {
+            values.favicon = ''
+          }
+          resolve(values)
+        }
+      })
+    })
+  }
+
+  /**
+   * @description 鍥炶溅鎻愪氦
+   */
+  handleSubmit = (e) => {
+    e.preventDefault()
+    this.props.inputSubmit()
+  }
+
+  render() {
+    const { card, type } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    let filelist = []
+
+    if (type === 'edit' && card && card.favicon) {
+      filelist = [{
+        uid: `favicon`,
+        name: card.favicon.slice(card.favicon.lastIndexOf('/') + 1),
+        status: 'done',
+        url: card.favicon,
+        origin: true
+      }]
+    }
+
+    return (
+      <Form {...formItemLayout} className="mob-card-edit-form">
+        <Row gutter={24}>
+          <Col span={12}>
+            <Form.Item label="搴旂敤绫诲瀷">
+              {getFieldDecorator('typename', {
+                initialValue: card ? card.typename : 'mob'
+              })(
+                <Select disabled={type === 'edit'}>
+                  <Select.Option value="mob">绉诲姩绔�(鍖呮嫭android銆乮os)</Select.Option>
+                  <Select.Option value="pc">PC绔�</Select.Option>
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="璇█">
+              {getFieldDecorator('lang', {
+                initialValue: card ? card.lang || 'zh-CN' : 'zh-CN'
+              })(
+                <Radio.Group disabled={type === 'edit'}>
+                  <Radio value="zh-CN">涓枃</Radio>
+                  <Radio value="en-US">鑻辨枃</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鐧诲綍">
+              {getFieldDecorator('login_types', {
+                initialValue: card ? card.login_types || 'true' : 'true'
+              })(
+                <Radio.Group>
+                  <Radio value="true">闇�瑕�</Radio>
+                  <Radio value="false">涓嶉渶瑕�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鏉冮檺绠$悊">
+              {getFieldDecorator('role_type', {
+                initialValue: card ? card.role_type || 'true' : 'true'
+              })(
+                <Radio.Group>
+                  <Radio value="true">鍚敤</Radio>
+                  <Radio value="false">涓嶅惎鐢�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鐭繛鎺�">
+              {getFieldDecorator('link_type', {
+                initialValue: card ? card.link_type || 'true' : 'true'
+              })(
+                <Radio.Group>
+                  <Radio value="true">鍚敤</Radio>
+                  <Radio value="false">涓嶅惎鐢�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鐨偆">
+              {getFieldDecorator('css', {
+                initialValue: card ? card.css : 'bg_black_style_blue'
+              })(
+                <Select>
+                  <Select.Option value="bg_black_style_blue">钃濋粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_blue">钃濈櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_red">绾㈤粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_red">绾㈢櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_orange_red">姗欑孩榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_orange_red">姗欑孩鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_orange">姗欓粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_orange">姗欑櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_orange_yellow">姗欓粍榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_orange_yellow">姗欓粍鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_yellow">榛勯粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_yellow">榛勭櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_yellow_green">榛勭豢榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_yellow_green">榛勭豢鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_green">缁块粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_green">缁跨櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_cyan">闈掗粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_cyan">闈掔櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_blue_purple">钃濈传榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_blue_purple">钃濈传鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_purple">绱粦鑹茬郴</Select.Option>
+                  <Select.Option value="bg_white_style_purple">绱櫧鑹茬郴</Select.Option>
+                  <Select.Option value="bg_black_style_magenta">娲嬬孩榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_magenta">娲嬬孩鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_grass_green">鑽夌豢榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_grass_green">鑽夌豢鐧借壊绯�</Select.Option>
+                  <Select.Option value="bg_black_style_deep_red">娣辩孩榛戣壊绯�</Select.Option>
+                  <Select.Option value="bg_white_style_deep_red">娣辩孩鐧借壊绯�</Select.Option>
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鏍囬">
+              {getFieldDecorator('title', {
+                initialValue: card ? card.title || '' : ''
+              })(<Input placeholder="" autoComplete="off" onPressEnter={this.handleSubmit} />)}
+            </Form.Item>
+          </Col>
+          <Col span={12}>
+            <Form.Item label="鍥炬爣">
+              {getFieldDecorator('favicon', {
+                initialValue: filelist
+              })(<FileUpload accept=".jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp,.ico" maxFile={1} fileType={'text'} />)}
+            </Form.Item>
+          </Col>
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(MainSearch)
\ No newline at end of file
diff --git a/src/mob/mobcard/mutilform/index.scss b/src/views/appmanage/submutilform/index.scss
similarity index 100%
copy from src/mob/mobcard/mutilform/index.scss
copy to src/views/appmanage/submutilform/index.scss
diff --git a/src/views/billprint/index.jsx b/src/views/billprint/index.jsx
index 97a31d7..66e09e1 100644
--- a/src/views/billprint/index.jsx
+++ b/src/views/billprint/index.jsx
@@ -26,7 +26,7 @@
 
 class BillPrint extends Component {
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     loadingview: true,
     printing: false,
     pages: null,
@@ -197,6 +197,12 @@
             component.setting.dataresource = component.setting.dataresource.replace(/@\$|\$@/ig, '')
             _customScript = _customScript.replace(/@\$|\$@/ig, '')
           }
+
+          // 澶栬仈鏁版嵁搴撴浛鎹�
+          if (window.GLOB.externalDatabase !== null) {
+            component.setting.dataresource = component.setting.dataresource.replace(/@db@/ig, window.GLOB.externalDatabase)
+            _customScript = _customScript.replace(/@db@/ig, window.GLOB.externalDatabase)
+          }
     
           component.setting.customScript = _customScript // 鏁寸悊鍚庤嚜瀹氫箟鑴氭湰
     
diff --git a/src/views/design/header/index.jsx b/src/views/design/header/index.jsx
new file mode 100644
index 0000000..5b8c73d
--- /dev/null
+++ b/src/views/design/header/index.jsx
@@ -0,0 +1,382 @@
+import React, {Component} from 'react'
+import { withRouter } from 'react-router-dom'
+import {connect} from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { Dropdown, Menu, Icon, Modal, Form, notification, Switch, Button } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import {
+  modifyMenuTree,
+  modifyMainMenu,
+  modifyTabview,
+  resetEditLevel,
+  logout
+} from '@/store/action'
+import Api from '@/api'
+import options from '@/store/options.js'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import Utils from '@/utils/utils.js'
+import avatar from '@/assets/img/avatar.jpg'
+import MainLogo from '@/assets/img/main-logo.png'
+import './index.scss'
+
+const EditMenu = asyncComponent(() => import('@/templates/menuconfig/editfirstmenu'))
+const { confirm } = Modal
+
+class Header extends Component {
+  state = {
+    menulist: null, // 涓�绾ц彍鍗�
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    userName: sessionStorage.getItem('CloudUserName'),
+    avatar: Utils.getrealurl(sessionStorage.getItem('CloudAvatar')),
+  }
+
+  logout = () => {
+    // 閫�鍑虹櫥褰�
+    let _this = this
+    confirm({
+      title: this.state.dict['main.logout.hint'],
+      content: '',
+      onOk() {
+        sessionStorage.clear()
+        _this.props.logout()
+        _this.props.history.replace('/login')
+      },
+      onCancel() {}
+    })
+  }
+
+  changeMenu (value) {
+    // 涓昏彍鍗曞垏鎹�
+    if (this.props.editLevel) {
+      // 缂栬緫鐘舵�佷笅锛屼笉鍙垏鎹㈣彍鍗�
+      return
+    }
+    if (value.PageParam.OpenType === 'menu') {
+      this.props.modifyMainMenu(value)
+    } else if (value.PageParam.OpenType === 'outpage') {
+      window.open(value.PageParam.linkUrl)
+    }
+  }
+
+  async loadmenu () {
+    // 鑾峰彇涓昏彍鍗�
+    let _param = {func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}
+    _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
+
+    let result = await Api.getSystemConfig(_param)
+
+    // 鐧诲綍瓒呮椂
+    if (!result) return
+
+    if (result.status) {
+      const { menulist } = this.getMenulist(result)
+
+      this.setState({ menulist })
+
+      this.props.modifyMenuTree(menulist)
+      if (window.GLOB.systemType !== 'production') { // 闈炴寮忕郴缁熼�夋嫨绗竴椤�
+        this.props.modifyMainMenu(menulist[0] || null)
+      }
+    } else {
+      notification.error({
+        top: 92,
+        message: result.message,
+        duration: 10
+      })
+    }
+  }
+
+  getMenulist = (result) => {
+    let iframes = ['Main/Index', 'bda/rdt', 'Home/rdt']
+    let menulist = []
+    result.fst_menu && result.fst_menu.forEach(fst => {
+      let fstItem = {
+        MenuID: fst.MenuID,
+        MenuName: fst.MenuName,
+        PageParam: {OpenType: 'menu', linkUrl: ''},
+        children: []
+      }
+      if (fst.PageParam) {
+        try {
+          fstItem.PageParam = JSON.parse(fst.PageParam)
+        } catch (e) {
+          fstItem.PageParam = {OpenType: 'menu', linkUrl: ''}
+        }
+      }
+
+      if (fst.snd_menu) {
+        fstItem.children = fst.snd_menu.map(snd => {
+          let sndItem = {
+            ParentId: fst.MenuID,
+            MenuID: snd.MenuID,
+            MenuName: snd.MenuName,
+            PageParam: {Icon: 'folder'},
+            children: []
+          }
+
+          if (snd.PageParam) {
+            try {
+              sndItem.PageParam = JSON.parse(snd.PageParam)
+            } catch (e) {
+              sndItem.PageParam = {Icon: 'folder'}
+            }
+          }
+
+          if (snd.trd_menu) {
+            sndItem.children = snd.trd_menu.map(trd => {
+              let trdItem = {
+                FstId: fst.MenuID,
+                ParentId: snd.MenuID,
+                MenuID: trd.MenuID,
+                MenuName: trd.MenuName,
+                MenuNo: trd.MenuNo,
+                EasyCode: trd.EasyCode,
+                type: 'CommonTable',            // 榛樿鍊间负甯哥敤琛�
+                OpenType: 'newtab'              // 鎵撳紑鏂瑰紡
+              }
+  
+              if (trd.LinkUrl && iframes.includes(trd.LinkUrl.split('?')[0])) {
+                trdItem.type = 'iframe'
+                trdItem.LinkUrl = trd.LinkUrl.replace('&amp;', '&')
+                trdItem.forbidden = true
+              } else {
+                try {
+                  trdItem.PageParam = trd.PageParam ? JSON.parse(trd.PageParam) : {OpenType: 'newtab'}
+                } catch (e) {
+                  trdItem.PageParam = {OpenType: 'newtab'}
+                }
+
+                trdItem.type = trdItem.PageParam.Template || trdItem.type
+                trdItem.OpenType = trdItem.PageParam.OpenType || trdItem.OpenType
+
+                if (trdItem.type === 'CustomPage' && this.props.memberLevel < 20) { // 浼氬憳绛夌骇澶т簬绛変簬20鏃讹紝鏈夌紪杈戞潈闄�
+                  trdItem.forbidden = true
+                }
+              }
+
+              return trdItem
+            })
+          }
+
+          return sndItem
+        })
+      }
+
+      menulist.push(fstItem)
+    })
+
+    return { menulist }
+  }
+
+  reload = () => {
+    this.loadmenu()
+  }
+
+  changeEditState = () => {
+    this.props.history.replace('/main')
+    window.location.reload()
+  }
+
+  enterEdit = () => {
+    // 杩涘叆缂栬緫鐘舵��
+    this.props.resetEditLevel('level1')
+  }
+
+  enterEditManage = () => {
+    const { editLevel } = this.props
+
+    if (editLevel === 'HS')  return
+
+    this.props.resetEditLevel('HS')
+    this.props.modifyMainMenu({
+      MenuID: 'systemManageView'
+    })
+  }
+
+  /**
+   * @description 閫�鍑虹鐞嗙晫闈㈣彍鍗�
+   */
+  exitManage = () => {
+    const { menulist } = this.state
+
+    if (window.GLOB.systemType === 'production') { // 姝e紡绯荤粺鐗堟湰鍗囩骇鍚庯紝椤甸潰鍒锋柊
+      this.props.history.replace('/main')
+      window.location.reload()
+      return
+    }
+
+    this.props.modifyMainMenu(menulist[0] || null)
+    this.props.resetEditLevel(false)
+    this.props.modifyTabview([])
+  }
+  
+  exitEdit = () => {
+    // 閫�鍑虹紪杈戠姸鎬�
+    this.props.resetEditLevel(false)
+  }
+  
+  UNSAFE_componentWillMount () {
+    sessionStorage.setItem('isEditState', 'true')
+    document.body.className = ''
+    
+    // 缁勪欢鍔犺浇鏃讹紝鑾峰彇鑿滃崟鏁版嵁
+    this.loadmenu()
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    if (!is(fromJS(this.props.menuTree), fromJS(nextProps.menuTree)) && !is(fromJS(this.state.menulist), fromJS(nextProps.menuTree))) {
+      this.setState({
+        menulist: nextProps.menuTree
+      })
+    }
+  }
+
+  componentDidMount () {
+    if (window.GLOB.systemType !== 'production') {
+      Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
+        if (res.status) {
+          let _permFuncField = []
+          let _sysRoles = []
+
+          if (res.Roles && res.Roles.length > 0) {
+            _sysRoles = res.Roles.map(role => {
+              return {
+                uuid: Utils.getuuid(),
+                value: role.RoleID,
+                text: role.RoleName
+              }
+            })
+          }
+
+          if (res.sModular && res.sModular.length > 0) {
+            res.sModular.forEach(field => {
+              if (field.ModularNo) {
+                _permFuncField.push(field.ModularNo)
+              }
+            })
+            _permFuncField = _permFuncField.sort()
+          }
+
+          sessionStorage.setItem('sysRoles', JSON.stringify(_sysRoles))
+          sessionStorage.setItem('permFuncField', JSON.stringify(_permFuncField))
+        }
+      })
+    } else if (window.GLOB.systemType === 'production') {
+      this.props.resetEditLevel('HS')
+      this.props.modifyMainMenu({
+        MenuID: 'systemManageView'
+      })
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+  }
+
+  gotoDoc = () => {
+    if (options.sysType === 'local' && window.GLOB.mainSystemApi) {
+      let ssodomain = window.GLOB.mainSystemApi.replace('/webapi/dostars', '')
+      let url = `${ssodomain}/doc/index.html#?appkey=${window.GLOB.appkey}&LoginUID=${sessionStorage.getItem('LoginUID')}`
+      window.open(url)
+    } else if (options.sysType === 'SSO' || options.sysType === 'cloud') {
+      window.open(`${window.location.href.replace(/\/index.html(.*)|\/#(.*)/ig, '')}/doc/index.html#?appkey=${window.GLOB.appkey}&LoginUID=${sessionStorage.getItem('LoginUID')}`)
+    }
+  }
+
+  render () {
+    const { mainMenu, editLevel } = this.props
+    const { menulist } = this.state
+
+    return (
+      <header className={'sys-header-container ant-menu-dark ' + (['level2', 'level3', 'HS'].includes(editLevel) ? 'mask' : '')} id="main-header-container">
+        <div className="header-logo"><img src={MainLogo} alt=""/></div>
+        <div className="header-collapse">
+          <Icon type="menu-fold"/>
+        </div>
+        {/* 姝e父鑿滃崟 */}
+        {editLevel !== 'level1' && menulist ?
+          <ul className={'header-menu ' + editLevel}>{
+            menulist.map(item => {
+              return (
+                <li key={item.MenuID} onClick={() => {this.changeMenu(item)}} className={mainMenu && mainMenu.MenuID === item.MenuID ? 'active' : ''}>
+                  <span>{item.MenuName}</span>
+                </li>
+              )
+            })}
+            {!editLevel || editLevel === 'HS' ?
+              <li key="HS" onClick={this.enterEditManage} className={editLevel === 'HS' ? 'active' : ''}>
+                <span>HS</span>
+              </li> : null
+            }
+          </ul> : null
+        }
+        {editLevel === 'HS' ? <Button className="level4-close" type="primary" onClick={this.exitManage}>閫�鍑�</Button> : null}
+        {/* 杩涘叆缂栬緫鎸夐挳 */}
+        {!editLevel ? <Icon onClick={this.enterEdit} className="edit-check" type="edit" /> : null}
+        {!editLevel && options.sysType === 'local' && window.GLOB.systemType !== 'production' && this.props.memberLevel >= 20 ?
+          <span onClick={() => {window.open('#/appmanage')}} className="mobile" type="edit"> 搴旂敤绠$悊 <Icon type="arrow-right" /></span> : null
+        }
+        {/* window.btoa(window.encodeURIComponent(JSON.stringify({ MenuType: 'home', MenuId: 'home_page_id', MenuName: '棣栭〉' }))) */}
+        {!editLevel && window.GLOB.systemType !== 'production' && this.props.memberLevel >= 20 ?
+          <span className="home-edit" onClick={() => {window.open('#/menudesign/JTdCJTIyTWVudVR5cGUlMjIlM0ElMjJob21lJTIyJTJDJTIyTWVudUlkJTIyJTNBJTIyaG9tZV9wYWdlX2lkJTIyJTJDJTIyTWVudU5hbWUlMjIlM0ElMjIlRTklQTYlOTYlRTklQTElQjUlMjIlN0Q=')}}>
+            棣栭〉 <Icon type="arrow-right" />
+          </span> : null
+        }
+        {/* 缂栬緫鑿滃崟 */}
+        {editLevel === 'level1' ? <EditMenu menulist={this.state.menulist} reload={this.reload} exitEdit={this.exitEdit}/> : null}
+        {/* 澶村儚銆佺敤鎴峰悕 */}
+        <Dropdown className="header-setting" overlay={
+          <Menu className="header-dropdown">
+            <Menu.Item key="switch">
+              {this.state.dict['main.edit']}
+              <Switch size="small" style={{marginLeft: '7px'}} disabled={!!editLevel} checked={true} onChange={this.changeEditState} />
+            </Menu.Item>
+            <Menu.Item key="doc" onClick={this.gotoDoc}>{this.state.dict['main.doc']}</Menu.Item>
+            <Menu.Item key="logout" onClick={this.logout}>{this.state.dict['main.logout']}</Menu.Item>
+          </Menu>
+        }>
+          <div>
+            <img src={this.state.avatar || avatar} alt=""/>
+            <span>
+              <span className="username">{this.state.userName}</span> <Icon type="down" />
+            </span>
+          </div>
+        </Dropdown>
+      </header>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    tabviews: state.tabviews,
+    menuTree: state.menuTree,
+    mainMenu: state.mainMenu,
+    editLevel: state.editLevel,
+    permAction: state.permAction,
+    memberLevel: state.memberLevel
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews)),
+    modifyMenuTree: (menuTree) => dispatch(modifyMenuTree(menuTree)),
+    modifyMainMenu: (mainMenu) => dispatch(modifyMainMenu(mainMenu)),
+    resetEditLevel: (level) => dispatch(resetEditLevel(level)),
+    logout: () => dispatch(logout())
+  }
+}
+
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(Form.create()(Header)))
\ No newline at end of file
diff --git a/src/views/design/header/index.scss b/src/views/design/header/index.scss
new file mode 100644
index 0000000..705e4d5
--- /dev/null
+++ b/src/views/design/header/index.scss
@@ -0,0 +1,160 @@
+.sys-header-container {
+  position: fixed;
+  z-index: 20;
+  left: 0;
+  top: 0;
+  font-weight: bold!important;
+  width: 100%;
+  height: 48px;
+
+  .header-logo {
+    float: left;
+    width: 180px;
+    line-height: 48px;
+    text-align: center;
+    padding-left: 5px;
+    box-sizing: border-box;
+    opacity: 1;
+    transition: width 0.2s, opacity 0.15s;
+    img {
+      max-width: 100%;
+      max-height: 40px;
+    }
+  }
+
+  .header-collapse {
+    float: left;
+    width: 35px;
+    min-height: 48px;
+    line-height: 48px;
+    padding-left: 10px;
+    margin: 0 10px;
+    transition: padding-left 0.15s;
+    i {
+      cursor: pointer;
+      position: relative;
+      top: 3px;
+      font-size: 20px;
+      color: #ffffff;
+    }
+  }
+  .header-menu {
+    float: left;
+    margin: 0;
+    line-height: 48px;
+    li {
+      float: left;
+      font-size: 1.3rem;
+      cursor: pointer;
+      span {
+        padding: 0 10px;
+        height: 42px;
+        display: inline-block;
+      }
+
+      &:hover {
+        color: #eeeeee;
+        span {
+          border-bottom: 4px solid #fafcfb;
+        }
+      }
+      &.active {
+        color: #ffffff;
+        span {
+          border-bottom: 4px solid #1890ff;
+        }
+      }
+    }
+  }
+
+  .header-menu.level4 {
+    li {
+      cursor: default;
+      &:hover {
+        span {
+          cursor: default;
+          color: rgba(255, 255, 255, 0.65);
+          border-bottom: none;
+        }
+      }
+      &.active {
+        span {
+          color: #ffffff;
+          border-bottom: 4px solid #1890ff;
+        }
+      }
+    }
+  }
+
+  .header-setting {
+    float: right;
+    line-height: 48px;
+    margin-right: 10px;
+    img {
+      width: 29px;
+      height: 29px;
+      border-radius: 30px;
+      margin-right: 7px;
+    }
+    span {
+      color: #ffffff;
+      font-size: 0.95rem;
+      .username {
+        display: inline-block;
+        height: 30px;
+        max-width: 95px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+      }
+    }
+  }
+  .edit-check {
+    font-size: 18px;
+    margin-top: 14px;
+    margin-left: 10px;
+    cursor: pointer;
+  }
+  .level4-close {
+    position: relative;
+    top: 13px;
+    left: 20px;
+    height: 26px;
+    padding: 0 10px;
+    z-index: 2;
+  }
+  .mobile {
+    position: absolute;
+    top: 135px;
+    right: 50px;
+    color: #1890ff;
+    cursor: pointer;
+  }
+  .home-edit {
+    position: absolute;
+    top: 100px;
+    right: 50px;
+    cursor: pointer;
+    color: #1890ff;
+  }
+}
+.sys-header-container.mask::after {
+  content: ' ';
+  display: block;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+.header-dropdown {
+  >li {
+    padding: 5px 25px;
+  }
+  >li.ant-dropdown-menu-submenu {
+    padding: 0px;
+    .ant-dropdown-menu-submenu-title {
+      padding: 5px 25px;
+    }
+  }
+}
diff --git a/src/views/design/index.jsx b/src/views/design/index.jsx
new file mode 100644
index 0000000..167b576
--- /dev/null
+++ b/src/views/design/index.jsx
@@ -0,0 +1,29 @@
+import React, {Component} from 'react'
+import { ConfigProvider } from 'antd'
+import enUS from 'antd/es/locale/en_US'
+import zhCN from 'antd/es/locale/zh_CN'
+
+import asyncComponent from '@/utils/asyncComponent'
+import Header from './header'
+import Sidemenu from './sidemenu'
+
+import './index.scss'
+
+const Tabview = asyncComponent(() => import('@/components/tabview'))
+const _locale = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+
+class Design extends Component {
+  render () {
+    return (
+      <div className="mk-main-view">
+        <ConfigProvider locale={_locale}>
+          <Header key="header"/>
+          <Sidemenu key="sidemenu"/>
+          <Tabview key="tabview"/>
+        </ConfigProvider>
+      </div>
+    )
+  }
+}
+
+export default Design
\ No newline at end of file
diff --git a/src/views/design/index.scss b/src/views/design/index.scss
new file mode 100644
index 0000000..c14dc8e
--- /dev/null
+++ b/src/views/design/index.scss
@@ -0,0 +1,5 @@
+.mk-main-view {
+  display: flex;
+  flex: auto;
+  min-height: 100%;
+}
\ No newline at end of file
diff --git a/src/components/sidemenu/config.jsx b/src/views/design/sidemenu/config.jsx
similarity index 96%
rename from src/components/sidemenu/config.jsx
rename to src/views/design/sidemenu/config.jsx
index c7b95af..1ec2bae 100644
--- a/src/components/sidemenu/config.jsx
+++ b/src/views/design/sidemenu/config.jsx
@@ -90,13 +90,13 @@
     MenuID: '1602315375262ikd33ii0nii34pt861o',
     MenuNo: 's_worksflow_roleM',
     MenuName: '鍏抽敭瑙掕壊绠$悊',
-  }, {
-    src: '',
-    PageParam: {OpenType: 'newtab', Template: 'ManageTable'},
-    type: 'ManageTable',
-    MenuID: '1606794243739c5ihs58lucpskp3r4s2',
-    MenuNo: 's_custom_componentsM',
-    MenuName: '鑷畾涔夌粍浠�',
+  // }, {
+  //   src: '',
+  //   PageParam: {OpenType: 'newtab', Template: 'ManageTable'},
+  //   type: 'ManageTable',
+  //   MenuID: '1606794243739c5ihs58lucpskp3r4s2',
+  //   MenuNo: 's_custom_componentsM',
+  //   MenuName: '鑷畾涔夌粍浠�',
   }]
 }, {
   MenuID: 'systemManageViewInterface',
diff --git a/src/views/design/sidemenu/index.jsx b/src/views/design/sidemenu/index.jsx
new file mode 100644
index 0000000..99c1da2
--- /dev/null
+++ b/src/views/design/sidemenu/index.jsx
@@ -0,0 +1,323 @@
+import React, {Component} from 'react'
+import { connect } from 'react-redux'
+import { is, fromJS } from 'immutable'
+import { Menu, Icon, notification } from 'antd'
+
+import asyncComponent from '@/utils/asyncComponent'
+import { modifyTabview, resetEditLevel, modifyMenuTree, modifyMainMenu } from '@/store/action'
+import { SySMenuList } from './config'
+import options from '@/store/options.js'
+import zhCN from '@/locales/zh-CN/main.js'
+import enUS from '@/locales/en-US/main.js'
+import Api from '@/api'
+import './index.scss'
+
+const EditSecMenu = asyncComponent(() => import('@/templates/menuconfig/editsecmenu'))
+const EditThdMenu = asyncComponent(() => import('@/templates/menuconfig/editthdmenu'))
+const { SubMenu } = Menu
+
+class Sidemenu extends Component {
+  state = {
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    subMenulist: [],         // 浜岀骇鑿滃崟
+    editMenu: null,          // 缂栬緫涓夌骇鑿滃崟鏃惰缃�
+    rootSubmenuKeys: null,
+    openKeys: null,
+    preview: null
+  }
+
+  async loadsubmenu (menu) {
+    if (!menu || !menu.MenuID) { // 娌℃湁涓昏彍鍗曟椂锛屾竻绌轰笅绾ц彍鍗�
+      this.setState({
+        subMenulist: [],
+        rootSubmenuKeys: [],
+        openKeys: [],
+        editMenu: null
+      })
+      return
+    }
+
+    menu = fromJS(menu).toJS()
+    let openKey = ''
+
+    if (menu.children[0]) {
+      openKey = menu.openId || menu.children[0].MenuID
+    }
+
+    this.setState({
+      subMenulist: menu.children,
+      rootSubmenuKeys: menu.children.map(item => item.MenuID),
+      openKeys: openKey ? [openKey] : [],
+      editMenu: this.props.editLevel === 'level3' ? menu.children.filter(_menu => _menu.MenuID === this.state.editMenu.MenuID)[0] : null
+    })
+  }
+
+  enterManageView = () => {
+    let menulist = SySMenuList
+
+    if (window.GLOB.systemType === 'production') {
+      menulist.forEach(menu => {
+        menu.children = menu.children.filter(item => item.systems && item.systems.includes(window.GLOB.systemType))
+      })
+
+      menulist = menulist.filter(menu => menu.children.length > 0)
+    } else {
+      menulist.forEach(menu => {
+        menu.children = menu.children.filter(item => !item.systems || item.systems.includes(options.sysType))
+      })
+
+      menulist = menulist.filter(menu => menu.children.length > 0)
+    }
+
+    this.setState({
+      subMenulist: menulist,
+      rootSubmenuKeys: menulist.map(item => item.MenuID),
+      openKeys: [menulist[0].MenuID]
+    })
+  }
+
+  changemenu(e, menu) {
+    e.preventDefault()
+    if (this.props.editLevel !== 'HS') {
+      return
+    }
+
+    let tabs = fromJS(this.props.tabviews).toJS()
+    tabs = tabs.filter(tab => {
+      tab.selected = false
+      return tab.MenuID !== menu.MenuID
+    })
+
+    if (this.props.tabviews.length > tabs.length) {
+      this.props.modifyTabview(fromJS(tabs).toJS())
+    }
+
+    this.setState({}, () => {
+      menu.selected = true
+      tabs.push(menu)
+      this.props.modifyTabview(tabs)
+    })
+  }
+
+  UNSAFE_componentWillReceiveProps (nextProps) {
+    if (!is(fromJS(this.props.mainMenu), fromJS(nextProps.mainMenu)) && nextProps.mainMenu && nextProps.mainMenu.MenuID === 'systemManageView') {
+      this.enterManageView()
+    } else if (!is(fromJS(this.props.mainMenu), fromJS(nextProps.mainMenu))) {
+      // 涓昏彍鍗曞垏鎹紝璇锋眰2銆�3绾ц彍鍗曟暟鎹�
+      this.loadsubmenu(nextProps.mainMenu)
+    }
+  }
+
+  shouldComponentUpdate(nextProps, nextState) {
+    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  onOpenChange = openKeys => {
+    const latestOpenKey = openKeys.find(key => this.state.openKeys.indexOf(key) === -1)
+    if (this.state.rootSubmenuKeys.indexOf(latestOpenKey) === -1) {
+      this.setState({ openKeys })
+    } else {
+      this.setState({
+        openKeys: latestOpenKey ? [latestOpenKey] : []
+      })
+    }
+  }
+
+  enterSubEdit = (e) => {
+    // 缂栬緫浜岀骇鑿滃崟
+    e.stopPropagation()
+    this.props.resetEditLevel('level2')
+  }
+
+  enterThrEdit = (e, menu) => {
+    // 缂栬緫涓夌骇鑿滃崟
+    e.stopPropagation()
+    this.props.resetEditLevel('level3')
+    this.setState({editMenu: menu})
+  }
+
+  reload = () => {
+    const { mainMenu } = this.props
+    let _param = {func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}
+    _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
+
+    Api.getSystemConfig(_param).then(result => {
+      // 鐧诲綍瓒呮椂
+      if (!result) return
+  
+      if (result.status) {
+        let res = this.getMenulist(result)
+        let _mainMenu = res.menulist.filter(item => item.MenuID === mainMenu.MenuID)[0]
+  
+        this.props.modifyMenuTree(res.menulist)
+        this.props.modifyMainMenu(_mainMenu || null)
+      } else {
+        notification.error({
+          top: 92,
+          message: result.message,
+          duration: 10
+        })
+      }
+      this.loadsubmenu(this.props.mainMenu)
+    })
+  }
+
+  getMenulist = (result) => {
+    let iframes = ['Main/Index', 'bda/rdt', 'Home/rdt']
+    let menulist = result.fst_menu.map(fst => {
+      let fstItem = {
+        MenuID: fst.MenuID,
+        MenuName: fst.MenuName,
+        PageParam: {OpenType: 'menu', linkUrl: ''},
+        children: []
+      }
+      if (fst.PageParam) {
+        try {
+          fstItem.PageParam = JSON.parse(fst.PageParam)
+        } catch (e) {
+          fstItem.PageParam = {OpenType: 'menu', linkUrl: ''}
+        }
+      }
+
+      if (fst.snd_menu) {
+        fstItem.children = fst.snd_menu.map(snd => {
+          let sndItem = {
+            ParentId: fst.MenuID,
+            MenuID: snd.MenuID,
+            MenuName: snd.MenuName,
+            PageParam: {Icon: 'folder'},
+            children: []
+          }
+
+          if (snd.PageParam) {
+            try {
+              sndItem.PageParam = JSON.parse(snd.PageParam)
+            } catch (e) {
+              sndItem.PageParam = {Icon: 'folder'}
+            }
+          }
+
+          if (snd.trd_menu) {
+            sndItem.children = snd.trd_menu.map(trd => {
+              let trdItem = {
+                FstId: fst.MenuID,
+                ParentId: snd.MenuID,
+                MenuID: trd.MenuID,
+                MenuName: trd.MenuName,
+                MenuNo: trd.MenuNo,
+                EasyCode: trd.EasyCode,
+                type: 'CommonTable',            // 榛樿鍊间负甯哥敤琛�
+                OpenType: 'newtab'              // 鎵撳紑鏂瑰紡
+              }
+  
+              if (trd.LinkUrl && iframes.includes(trd.LinkUrl.split('?')[0])) {
+                trdItem.type = 'iframe'
+                trdItem.LinkUrl = trd.LinkUrl.replace('&amp;', '&')
+                trdItem.forbidden = true
+              } else {
+                try {
+                  trdItem.PageParam = trd.PageParam ? JSON.parse(trd.PageParam) : {OpenType: 'newtab'}
+                } catch (e) {
+                  trdItem.PageParam = {OpenType: 'newtab'}
+                }
+
+                trdItem.type = trdItem.PageParam.Template || trdItem.type
+                trdItem.OpenType = trdItem.PageParam.OpenType || trdItem.OpenType
+
+                if (trdItem.type === 'CustomPage' && this.props.memberLevel < 20) { // 浼氬憳绛夌骇澶т簬绛変簬20鏃讹紝鏈夌紪杈戞潈闄�
+                  trdItem.forbidden = true
+                }
+              }
+              return trdItem
+            })
+          }
+          return sndItem
+        })
+      }
+      return fstItem
+    })
+
+    return { menulist }
+  }
+
+  exitEdit = () => {
+    if (this.props.editLevel === 'level3') {
+      this.setState({editMenu: null})
+    }
+    this.props.resetEditLevel(false)
+  }
+
+  render () {
+    return (
+      <aside id="mk-sidemenu-wrap" className="mk-sys-side-menu ant-menu-dark mk-edit">
+        {!(this.props.editLevel === 'level2' || this.props.editLevel === 'level3') &&
+          <Menu openKeys={this.state.openKeys} onOpenChange={this.onOpenChange} mode="inline" theme="dark">
+          {!this.props.editLevel && <li className="sup-menu"><Icon onClick={this.enterSubEdit} className="edit-check" type="edit" /></li>}
+          {this.state.subMenulist && this.state.subMenulist.map((item, index) => {
+            return (
+              <SubMenu
+                key={item.MenuID}
+                title={
+                  <span className={!this.props.editLevel && index === 0 ? 'edit-control' : ''}>
+                    <Icon type={item.PageParam.Icon} />
+                    <span>{item.MenuName}</span>
+                  </span>
+                }
+              >
+                {!this.props.editLevel ? <li className={'ant-menu-item ' + (item.children.length > 0 ? 'sub-menu' : '')}>
+                  <Icon onClick={(e) => {this.enterThrEdit(e, item)}} className="edit-check" type="edit" />
+                </li> : null}
+                {item.children.map(cell => {
+                  return (
+                    <Menu.Item key={cell.MenuID}>
+                      <a href={cell.src} id={cell.MenuID} onClick={(e) => this.changemenu(e, cell)}>{cell.MenuName}</a>
+                    </Menu.Item>
+                  )
+                })}
+              </SubMenu>
+            )
+          })}
+        </Menu>}
+        {this.props.editLevel === 'level2' ?
+          <EditSecMenu
+            menulist={this.state.subMenulist}
+            menuTree={this.props.menuTree}
+            supMenu={this.props.mainMenu}
+            reload={this.reload}
+            exitEdit={this.exitEdit}
+          /> : null
+        }
+        {this.props.editLevel === 'level3' && this.state.editMenu ?
+          <EditThdMenu
+            menulist={this.state.editMenu.children}
+            supMenuList={this.state.subMenulist}
+            supMenu={this.state.editMenu}
+            reload={this.reload}
+            exitEdit={this.exitEdit}
+          /> : null
+        }
+      </aside>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    tabviews: state.tabviews,
+    mainMenu: state.mainMenu,
+    menuTree: state.menuTree,
+    memberLevel: state.memberLevel,
+    editLevel: state.editLevel
+  }
+}
+
+const mapDispatchToProps = (dispatch) => {
+  return {
+    modifyMenuTree: (menuTree) => dispatch(modifyMenuTree(menuTree)),
+    modifyMainMenu: (mainMenu) => dispatch(modifyMainMenu(mainMenu)),
+    modifyTabview: (tabviews) => dispatch(modifyTabview(tabviews)),
+    resetEditLevel: (level) => dispatch(resetEditLevel(level))
+  }
+}
+
+export default connect(mapStateToProps, mapDispatchToProps)(Sidemenu)
\ No newline at end of file
diff --git a/src/views/design/sidemenu/index.scss b/src/views/design/sidemenu/index.scss
new file mode 100644
index 0000000..c3e8ff7
--- /dev/null
+++ b/src/views/design/sidemenu/index.scss
@@ -0,0 +1,119 @@
+@import '../../../assets/css/iconfont.css';
+
+.mk-sys-side-menu {
+  flex: 0 0 235px;
+  width: 235px;
+  padding: 48px 0 40px;
+  transition: width 0.2s, flex 0.2s;
+  .ant-menu-item {
+    padding-left: 0!important;
+    cursor: default;
+    a {
+      padding-left: 48px;
+    }
+  }
+  .ant-menu-sub.ant-menu-inline {
+    position: relative;
+  }
+  .ant-menu-sub.ant-menu-inline > .ant-menu-item {
+    height: 38px;
+    line-height: 38px;
+    margin: 0px;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    a {
+      display: inline-block;
+    }
+    .edit-check {
+      top: -5px;
+    }
+  }
+  .ant-menu-sub.ant-menu-inline > .ant-menu-item.ant-menu-item-active {
+    background: #06b4f7;
+  }
+  .ant-menu-sub.ant-menu-inline > .ant-menu-item.ant-menu-item-selected {
+    background: #06b4f7;
+  }
+  .ant-menu-inline .ant-menu-item {
+    font-size: 1.1rem;
+  }
+  .ant-menu-dark.ant-menu-inline .ant-menu-submenu-title {
+    margin: 0;
+    height: 48px;
+    line-height: 48px;
+  }
+  .ant-menu-dark.ant-menu-inline .ant-menu-submenu-open .ant-menu-submenu-title {
+    background: #364150;
+  }
+  .edit-check {
+    font-size: 18px;
+    position: absolute;
+    cursor: pointer;
+    padding: 10px 15px;
+    margin-right: 0px;
+    right: 0px;
+    top: 0px;
+    :hover {
+      color: #ffffff;
+    }
+  }
+  .edit-control + .ant-menu-submenu-arrow {
+    display: none;
+  }
+  .menu-add {
+    position: relative;
+    border: 1px dashed gray;
+    margin: 8px 0px;
+    height: 40px;
+    line-height: 40px;
+    width: 98%;
+    clear: both;
+    text-align: center;
+    cursor: pointer;
+    .anticon {
+      font-size: 20px;
+    }
+  }
+  .menu-btn {
+    .ant-btn {
+      padding: 0 10px;
+      margin-left: 5px;
+    }
+  }
+  .sup-menu {
+    position: relative;
+    z-index: 1;
+  }
+  .ant-menu-sub.ant-menu-inline .sub-menu {
+    position: absolute;
+    z-index: 1;
+    width: 48px;
+    left: 187px;
+  }
+}
+.mk-sys-side-menu.mk-edit { // 缂栬緫鏃舵帶鍒惰彍鍗曞簳鑹�
+  .ant-menu-sub.ant-menu-inline {
+    > .ant-menu-item.ant-menu-item-selected {
+      background: unset;
+    }
+    > .ant-menu-item.ant-menu-item-active {
+      background: unset;
+    }
+  }
+}
+.ant-menu-submenu.ant-menu-submenu-popup {
+  max-height: 80vh;
+  overflow-y: scroll;
+  &::-webkit-scrollbar {
+    display: none;
+  }
+}
+.ant-menu-vertical .ant-menu-item {
+  cursor: default;
+  height: 30px;
+  line-height: 30px;
+  a {
+    cursor: pointer;
+  }
+}
\ No newline at end of file
diff --git a/src/views/login/index.jsx b/src/views/login/index.jsx
index c0cef5d..edc38e7 100644
--- a/src/views/login/index.jsx
+++ b/src/views/login/index.jsx
@@ -6,7 +6,7 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js' 
-import options from '@/store/options.js'
+import options, { styles } from '@/store/options.js'
 import zhCN from '@/locales/zh-CN/login.js'
 import enUS from '@/locales/en-US/login.js'
 import asyncLoadComponent from '@/utils/asyncLoadComponent'
@@ -15,7 +15,7 @@
 
 const LoginForm = asyncLoadComponent(() => import('./loginform'))
 const LoginCloudForm = asyncLoadComponent(() => import('./logincloudform'))
-const iszhCN = localStorage.getItem('lang') !== 'en-US'
+const iszhCN = sessionStorage.getItem('lang') !== 'en-US'
 
 const _href = window.location.href.split('#')[0]
 if (localStorage.getItem(_href + 'paramsmain')) {
@@ -40,7 +40,9 @@
     syncApp: false,
     loginWays: null,
     touristLogin: false,
-    syncing: false
+    syncing: false,
+    ipAddress: '',
+    city: ''
   }
 
   UNSAFE_componentWillMount() {
@@ -90,6 +92,7 @@
    * @param {Object} param 鐢ㄦ埛鍚嶅瘑鐮佺瓑淇℃伅
    */
   async loginsubmit (param) {
+    const { ipAddress, city } = this.state
     if (options.sysType === 'local' && !window.GLOB.mainSystemApi) { // 涓氬姟绯荤粺蹇呴』璁剧疆鍗曠偣鍦板潃
       Modal.warning({
         title: '鏈缃崟鐐规湇鍔″櫒鍦板潃锛岃鑱旂郴绠$悊鍛橈紒'
@@ -98,7 +101,7 @@
     }
 
     // 鐧诲綍鎻愪氦
-    let res = await Api.getusermsg(param.username, param.password)
+    let res = await Api.getusermsg(param.username, param.password, false, ipAddress, city)
     if (res.status) {
       localStorage.setItem('UserID', res.UserID)
       localStorage.setItem('LoginUID', res.LoginUID)
@@ -189,6 +192,7 @@
   }
 
   async phoneloginsubmit (param) {
+    const { ipAddress, city } = this.state
     if (options.sysType === 'local' && !window.GLOB.mainSystemApi) { // 涓氬姟绯荤粺蹇呴』璁剧疆鍗曠偣鍦板潃
       Modal.warning({
         title: '鏈缃崟鐐规湇鍔″櫒鍦板潃锛岃鑱旂郴绠$悊鍛橈紒'
@@ -197,7 +201,7 @@
     }
 
     // 鐧诲綍鎻愪氦
-    let res = await Api.getphoneusermsg(param.phone, param.vercode)
+    let res = await Api.getphoneusermsg(param.phone, param.vercode, false, ipAddress, city)
     if (res.status) {
       sessionStorage.setItem('UserID', res.UserID)
       sessionStorage.setItem('LoginUID', res.LoginUID)
@@ -210,7 +214,13 @@
       sessionStorage.setItem('role_id', res.role_id || '')
       sessionStorage.setItem('localRole_id', res.role_id || '')
 
-      localStorage.setItem('lang', param.lang || 'zh-CN')
+      let _url = window.location.href.split('#')[0]
+
+      localStorage.setItem(_url + 'lang', param.lang || 'zh-CN')
+      sessionStorage.setItem('lang', param.lang || 'zh-CN')
+
+      sessionStorage.removeItem('visitorUserID')
+      sessionStorage.removeItem('visitorLoginUID')
 
       let _history = sessionStorage.getItem('history')
       if (_history) {
@@ -236,6 +246,15 @@
   }
 
   componentDidMount () {
+    // 鑾峰彇ip鍙婂煄甯備俊鎭�
+    Api.directRequest('http://epc.mk9h.cn/webapi/iploc', 'get', null, 'true').then(res => {
+      if (!res || !res.location) return
+      sessionStorage.setItem('city', res.location)
+      this.setState({
+        ipAddress: res.ip || '',
+        city: res.location
+      })
+    })
     const timeStamp = new Date().getTime()
     const _authUrl = window.location.href.split('#')[0] + 'AuthCode'
 
@@ -263,16 +282,24 @@
       let _id = window.atob('YmgwYmFwYWJ0ZDQ1ZXBz$mkZ3JhNzlzZWdiY2g2YzFpYms='.replace(/\$mk/ig, ''))
   
       let param = {
-        rduri: _rduri,
         func: _func,
         AppID: _appId,
         TimeStamp: timeStamp,
         appkey: window.GLOB.appkey,
         userid: _id,
-        LoginUID: _id
+        LoginUID: _id,
+        nonc: Utils.getuuid()
       }
+
+      let keys = Object.keys(param).sort()
+      let values = ''
+      keys.forEach(key => {
+        values += key + param[key]
+      })
+      param.sign = md5(values)
+      param.t = new Date().getTime()
   
-      Api.dostarToDostars(param).then(res => {
+      Api.directRequest(_rduri + '/sEmpowerCloud_Get_LinkUrl', 'post', param, 'true').then(res => {
         if (res.status) {
           if (res.EPC === str) {
             let box = []
@@ -303,12 +330,8 @@
 
     Api.getTouristMsg().then(result => {
       if (result.status) {
-        if (result.UserID && !sessionStorage.getItem('UserID')) {
-          sessionStorage.setItem('UserID', result.UserID)
-        }
-        if (result.LoginUID && !sessionStorage.getItem('LoginUID')) {
-          sessionStorage.setItem('LoginUID', result.LoginUID)
-        }
+        sessionStorage.setItem('visitorUserID', result.UserID || '')
+        sessionStorage.setItem('visitorLoginUID', result.LoginUID || '')
 
         if (result.UserID && result.LoginUID) {
           this.setState({touristLogin: true})
@@ -321,11 +344,19 @@
           LText: `select '${window.GLOB.appkey}'`,
         }
 
+        _param.userid = result.UserID
+        _param.LoginUID = result.LoginUID
         _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
         _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
 
         Api.getSystemConfig(_param).then(res => {
           if (res.status) {
+            // positecgroup
+            res.Banner = res.Banner ? res.Banner.replace(/:8080/ig, '').replace(/http:/ig, 'https:') : ''
+            res.doclogo = res.doclogo ? res.doclogo.replace(/:8080/ig, '').replace(/http:/ig, 'https:') : ''
+            res.indexlogo = res.indexlogo ? res.indexlogo.replace(/:8080/ig, '').replace(/http:/ig, 'https:') : ''
+            res.loginlogo = res.loginlogo ? res.loginlogo.replace(/:8080/ig, '').replace(/http:/ig, 'https:') : ''
+
             let _url = window.location.href.split('#')[0] + 'system'
             let systemMsg = {
               favicon: res.titlelogo || '',
@@ -339,7 +370,8 @@
               doclogo: res.doclogo || '',
               style: res.CSS || '',
               showline: res.split_line_show || 'true',
-              webSite: res.WebSite || ''
+              webSite: res.WebSite || '',
+              navBar: res.menu_type
             }
 
             sessionStorage.setItem('home_background', res.index_background_color)
@@ -392,9 +424,10 @@
 
             window.GLOB.mainlogo = systemMsg.mainlogo
             window.GLOB.style = systemMsg.style
+            window.GLOB.navBar = systemMsg.navBar
         
-            if (window.GLOB.style && options.styles[window.GLOB.style]) {
-              document.body.className = options.styles[window.GLOB.style] + ' ' + (res.split_line_show === 'false' ? 'hidden-split-line' : '')
+            if (window.GLOB.style && styles[window.GLOB.style]) {
+              document.body.className = styles[window.GLOB.style] + ' ' + (res.split_line_show === 'false' ? 'hidden-split-line' : '')
             }
 
             if (res.titlelogo && window.GLOB.favicon !== res.titlelogo) {
@@ -413,11 +446,28 @@
             }
 
             if (res.users_upt === 'true' && window.GLOB.systemType === 'production') {
-              Api.getLocalConfig ({func: 's_Get_local_u_deleted', users_upt_date: res.users_upt_date}).then(localres => {
+              Api.getLocalConfig ({
+                func: 's_Get_local_u_deleted',
+                users_upt_date: res.users_upt_date,
+                userid: result.UserID,
+                LoginUID: result.LoginUID
+              }).then(localres => {
                 if (!localres.status) return
-                Api.getSystemConfig({func: 's_get_sso_u_create', user_ids: localres.user_ids, user_ids_local: localres.user_ids_local, users_upt_date: localres.users_upt_date}).then(ssores => {
+                Api.getSystemConfig({
+                  func: 's_get_sso_u_create',
+                  user_ids: localres.user_ids,
+                  user_ids_local: localres.user_ids_local,
+                  users_upt_date: localres.users_upt_date,
+                  userid: result.UserID,
+                  LoginUID: result.LoginUID
+                }).then(ssores => {
                   if (!ssores.status) return
-                  Api.getLocalConfig ({func: 's_get_local_u_create', user_ids_local: ssores.user_ids_local})
+                  Api.getLocalConfig ({
+                    func: 's_get_local_u_create',
+                    user_ids_local: ssores.user_ids_local,
+                    userid: result.UserID,
+                    LoginUID: result.LoginUID
+                  })
                 })
               })
             }
@@ -545,7 +595,7 @@
         </div>
         <div className="login-bottom">
           {webSite && copyRight ?
-            <a target="blank" href={webSite} dangerouslySetInnerHTML={{ __html: copyRight.replace(/\s/ig, '&nbsp;') }}></a> :
+            <a target="_blank" rel="noopener noreferrer" href={webSite} dangerouslySetInnerHTML={{ __html: copyRight.replace(/\s/ig, '&nbsp;') }}></a> :
             <p dangerouslySetInnerHTML={{ __html: copyRight ? copyRight.replace(/\s/ig, '&nbsp;') : '' }}></p>
           }
           {ICP ? <p dangerouslySetInnerHTML={{ __html: ICP.replace(/\s/ig, '&nbsp;') }}></p> : null}
@@ -557,6 +607,7 @@
           cancelText={this.state.dict['login.auth.cancel']}
           visible={this.state.syncApp}
           onOk={this.syncSubmit}
+          maskClosable={false}
           className="sync-cloud-application"
           width={'430px'}
           confirmLoading={this.state.syncing}
diff --git a/src/views/login/logincloudform.jsx b/src/views/login/logincloudform.jsx
index 4a6c448..0e1008c 100644
--- a/src/views/login/logincloudform.jsx
+++ b/src/views/login/logincloudform.jsx
@@ -11,7 +11,7 @@
   }
 
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
   }
 
   handleConfirm = () => {
diff --git a/src/views/login/loginform.jsx b/src/views/login/loginform.jsx
index f92a8b4..0bf62a6 100644
--- a/src/views/login/loginform.jsx
+++ b/src/views/login/loginform.jsx
@@ -210,6 +210,9 @@
     param.LText = md5(`${_phone}mingke${window.GLOB.appkey}${param.timestamp}`)
     param.secretkey = md5(`${param.LText}mingke${param.timestamp}`)
 
+    param.userid = sessionStorage.getItem('visitorUserID') || ''
+    param.LoginUID = sessionStorage.getItem('visitorLoginUID') || ''
+
     this.setState({
       verdisabled: true,
       delay: 60
diff --git a/src/views/main/index.jsx b/src/views/main/index.jsx
index 718f936..0abf7b6 100644
--- a/src/views/main/index.jsx
+++ b/src/views/main/index.jsx
@@ -3,23 +3,33 @@
 import enUS from 'antd/es/locale/en_US'
 import zhCN from 'antd/es/locale/zh_CN'
 
+import asyncComponent from '@/utils/asyncComponent'
 import Header from '@/components/header'
 import Sidemenu from '@/components/sidemenu'
-import Tabview from '@/components/tabview'
 import QueryLog from '@/components/querylog'
 
 import './index.scss'
 
-const _locale = localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+const Tabview = asyncComponent(() => import('@/components/tabview'))
+const Breadview = asyncComponent(() => import('@/components/breadview'))
+const _locale = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
 
 class Main extends Component {
+  state = {
+    navBar: window.GLOB.navBar
+  }
+
   render () {
+    const { navBar } = this.state
+    const isSideMenu = !['linkage_navigation', 'linkage'].includes(navBar)
+
     return (
       <div className="mk-main-view">
         <ConfigProvider locale={_locale}>
           <Header key="header"/>
-          <Sidemenu key="sidemenu"/>
-          <Tabview key="tabview"/>
+          {isSideMenu ? <Sidemenu key="sidemenu"/> : null}
+          {isSideMenu ? <Tabview key="tabview"/> : null}
+          {!isSideMenu ? <Breadview key="breadview"/> : null}
           <QueryLog />
         </ConfigProvider>
       </div>
diff --git a/src/views/menudesign/index.jsx b/src/views/menudesign/index.jsx
index c79828e..84cee4d 100644
--- a/src/views/menudesign/index.jsx
+++ b/src/views/menudesign/index.jsx
@@ -1,5 +1,4 @@
 import React, { Component } from 'react'
-import { connect } from 'react-redux'
 import { DndProvider } from 'react-dnd'
 import { is, fromJS } from 'immutable'
 import moment from 'moment'
@@ -15,15 +14,14 @@
 import antdEnUS from 'antd/es/locale/en_US'
 import antdZhCN from 'antd/es/locale/zh_CN'
 import MKEmitter from '@/utils/events.js'
-import MenuUtils from '@/menu/utils/menuUtils.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import asyncComponent from '@/utils/asyncComponent'
-import { modifyCustomMenu } from '@/store/action'
 
 import './index.scss'
 
 const { Panel } = Collapse
 const { confirm } = Modal
-const _locale = localStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS
+const _locale = sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS
 
 const MenuForm = asyncComponent(() => import('./menuform'))
 const HomeForm = asyncComponent(() => import('./homeform'))
@@ -37,6 +35,7 @@
 const PaddingController = asyncComponent(() => import('@/menu/padcontroller'))
 const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
 const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
+const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
 const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
 const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
 const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
@@ -47,23 +46,23 @@
 sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
 document.body.className = ''
 window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.urlFields = []               // url鍙橀噺
+window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
 
 class MenuDesign extends Component {
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
     MenuType: '',
     MenuId: '',
     ParentId: '',
     MenuName: '',
     MenuNo: '',
-    tableFields: [],
     delButtons: [],
     copyButtons: [],
     thawButtons: [],
     activeKey: 'basedata',
     menuloading: false,
     oriConfig: null,
-    openEdition: '',
     config: null,
     popBtn: null,             // 寮圭獥鏍囩椤�
     visible: false,
@@ -228,10 +227,7 @@
   initPopview = (card, btn) => {
     const { oriConfig, config } = this.state
 
-    let _config = fromJS(config).toJS()
-    delete _config.tableFields
-
-    if (!is(fromJS(oriConfig), fromJS(_config))) {
+    if (!is(fromJS(oriConfig), fromJS(config))) {
       notification.warning({
         top: 92,
         message: '閰嶇疆宸蹭慨鏀癸紝璇蜂繚瀛橈紒',
@@ -240,7 +236,7 @@
       return
     }
 
-    btn.config = _config
+    btn.config = fromJS(config).toJS()
     btn.component = card
 
     sessionStorage.setItem('editMenuType', 'popview') // 缂栬緫寮圭獥鏍囩
@@ -251,7 +247,7 @@
   handleBack = () => {
     this.setState({popBtn: null, delButtons: [], copyButtons: []}, () => {
       sessionStorage.setItem('editMenuType', 'menu')
-      this.props.modifyCustomMenu(this.state.config)
+      window.GLOB.customMenu = this.state.config
       this.setState({visible: false})
     })
   }
@@ -264,10 +260,7 @@
       return
     }
 
-    let _config = fromJS(config).toJS()
-    delete _config.tableFields
-
-    if (!is(fromJS(oriConfig), fromJS(_config))) {
+    if (!is(fromJS(oriConfig), fromJS(config))) {
       confirm({
         title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
         content: '',
@@ -341,13 +334,14 @@
           config.lastCount = config.lastCount || ''
         }
 
+        config.open_edition = result.open_edition || ''
+        window.GLOB.urlFields = config.urlFields || []
+
         this.setState({
           oriConfig: config,
-          config: fromJS(config).toJS(),
-          openEdition: result.open_edition || '',
+          config: fromJS(config).toJS()
         })
-
-        this.props.modifyCustomMenu(config)
+        window.GLOB.customMenu = config
       } else {
         notification.warning({
           top: 92,
@@ -385,6 +379,15 @@
               _sort++
             })
             card.backElements && card.backElements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        } else if (item.type === 'carousel') {
+          item.subcards.forEach(card => {
+            card.elements && card.elements.forEach(cell => {
               if (cell.eleType !== 'button') return
               this.checkBtn(cell)
               buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
@@ -458,7 +461,7 @@
   }
 
   submitConfig = () => {
-    const { openEdition, MenuType, delButtons, copyButtons, thawButtons } = this.state
+    const { MenuType, delButtons, copyButtons, thawButtons } = this.state
     let config = fromJS(this.state.config).toJS()
 
     if (MenuType === 'billPrint' && (!config.firstCount || !config.everyPCount)) {
@@ -495,21 +498,19 @@
         config.enabled = false
       }
 
-      let _config = fromJS(config).toJS()
-      delete _config.tableFields
-
       let param = {
         func: 'sPC_TrdMenu_AddUpt',
-        FstID: _config.fstMenuId || '',
-        SndID: _config.parentId,
-        ParentID: _config.parentId,
-        MenuID: _config.uuid,
-        MenuNo: _config.MenuNo || '',
-        EasyCode: _config.easyCode || '',
+        FstID: config.fstMenuId || '',
+        SndID: config.parentId,
+        ParentID: config.parentId,
+        MenuID: config.uuid,
+        MenuNo: config.MenuNo || '',
+        EasyCode: config.easyCode || '',
         Template: 'CustomPage',
-        MenuName: _config.MenuName || '',
-        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: 'newtab'}),
-        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_config))),
+        MenuName: config.MenuName || '',
+        PageParam: JSON.stringify({Template: 'CustomPage', OpenType: config.OpenType || 'newtab'}),
+        LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
+        open_edition: config.open_edition,
         LText: '',
         LTexttb: ''
       }
@@ -518,15 +519,11 @@
       param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
       param.secretkey = Utils.encrypt(param.LText, param.timestamp)
 
-      if (openEdition) { // 鐗堟湰绠$悊
-        param.open_edition = openEdition
-      }
-
       let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
         func: 'sPC_Button_AddUpt',
         Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
-        ParentID: _config.uuid,
-        MenuNo: _config.MenuNo,
+        ParentID: config.uuid,
+        MenuNo: config.MenuNo,
         Template: 'CustomPage',
         PageParam: '',
         LongParam: '',
@@ -564,7 +561,7 @@
               if (result.status) {
                 Api.getSystemConfig({
                   func: 's_PrintTemplateMSub',
-                  ID: _config.uuid,
+                  ID: config.uuid,
                   Images: Utils.getcloudurl(result.Images),
                   Remark: '',
                   temp_type: 'billprint',
@@ -646,9 +643,9 @@
         if (!res) return
 
         if (res.status) {
+          config.open_edition = res.open_edition || ''
           this.setState({
-            oriConfig: fromJS(_config).toJS(),
-            openEdition: res.open_edition || ''
+            oriConfig: fromJS(config).toJS()
           })
 
           if (btnParam.LText) {
@@ -720,7 +717,7 @@
 
                     let _param = {
                       func: 'sPC_ButtonParam_AddUpt',
-                      ParentID: _config.uuid,
+                      ParentID: config.uuid,
                       MenuID: item.uuid,
                       MenuNo: '',
                       Template: 'CustomPage',
@@ -869,8 +866,7 @@
     this.setState({
       config: config
     })
-
-    this.props.modifyCustomMenu(config)
+    window.GLOB.customMenu = config
   }
 
   insert = (item) => {
@@ -879,23 +875,7 @@
     config.components.push(item)
 
     this.setState({config})
-    this.props.modifyCustomMenu(config)
-  }
-
-  /**
-   * @description 鏇存柊甯哥敤琛ㄤ俊鎭紝蹇嵎娣诲姞鍚庢洿鏂伴厤缃俊鎭�
-   */
-  updatetable = (config, fields) => {
-    const { tableFields } = this.state
-
-    config.tableFields = fields ? fields : tableFields
-
-    this.setState({
-      tableFields: fields ? fields : tableFields,
-      config
-    })
-
-    this.props.modifyCustomMenu(config)
+    window.GLOB.customMenu = config
   }
 
   render () {
@@ -921,6 +901,10 @@
                       MenuNo={MenuNo}
                       updateConfig={this.updateConfig}
                     /> : null}
+                    {config && MenuType === 'custom' ? <UrlFieldComponent
+                      config={config}
+                      updateConfig={this.updateConfig}
+                    /> : null}
                     {config && MenuType === 'home' ? <HomeForm
                       dict={dict}
                       config={config}
@@ -932,7 +916,7 @@
                       updateConfig={this.updateConfig}
                     /> : null}
                     {/* 琛ㄥ悕娣诲姞 */}
-                    {config ? <TableComponent config={config} updatetable={this.updatetable}/> : null}
+                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
                   </Panel>
                   {/* 缁勪欢娣诲姞 */}
                   <Panel header={dict['mob.component']} key="component">
@@ -978,14 +962,4 @@
   }
 }
 
-const mapStateToProps = () => {
-  return {}
-}
-
-const mapDispatchToProps = (dispatch) => {
-  return {
-    modifyCustomMenu: (customMenu) => dispatch(modifyCustomMenu(customMenu))
-  }
-}
-
-export default connect(mapStateToProps, mapDispatchToProps)(MenuDesign)
\ No newline at end of file
+export default MenuDesign
\ No newline at end of file
diff --git a/src/views/menudesign/menuform/index.jsx b/src/views/menudesign/menuform/index.jsx
index b9a5833..92d185d 100644
--- a/src/views/menudesign/menuform/index.jsx
+++ b/src/views/menudesign/menuform/index.jsx
@@ -26,9 +26,6 @@
   UNSAFE_componentWillMount () {
     const { MenuId, config } = this.props
     let _param = {func: 's_get_pc_menus', systemType: options.sysType, debug: 'Y'}
-    if (options.sysType !== 'cloud' && window.GLOB.systemType !== 'production') {
-      _param.linkurl = window.GLOB.linkurl
-    }
     _param.pro_sys = window.GLOB.systemType === 'production' ? 'Y' : ''
 
     Api.getSystemConfig(_param).then(result => {
@@ -110,13 +107,6 @@
     })
   }
 
-  UNSAFE_componentWillReceiveProps(nextProps) {
-    const { config } = this.props
-    if (!config && nextProps.config) {
-      this.props.form.setFieldsValue({easyCode: nextProps.config.easyCode})
-    }
-  }
-
   // 涓�浜岀骇鑿滃崟鍒囨崲
   selectChange = (key, value) => {
     const { config } = this.props
@@ -143,6 +133,8 @@
       this.props.updateConfig({...config, cacheUseful: value})
     } else if (key === 'timeUnit') {
       this.props.updateConfig({...config, timeUnit: value})
+    } else if (key === 'OpenType') {
+      this.props.updateConfig({...config, OpenType: value})
     }
   }
 
@@ -255,6 +247,24 @@
             </Form.Item>
           </Col>
           <Col span={24}>
+            <Form.Item label="鎵撳紑鏂瑰紡">
+              {getFieldDecorator('OpenType', {
+                initialValue: config.OpenType || 'newtab',
+                rules: [
+                  {
+                    required: true,
+                    message: dict['form.required.select'] + dict['model.openway'] + '!'
+                  }
+                ]
+              })(
+                <Select onChange={(value) => {this.selectChange('OpenType', value)}}>
+                  <Select.Option value="newtab">鏍囩椤�</Select.Option>
+                  <Select.Option value="newpage">鏂伴〉闈�</Select.Option>
+                </Select>
+              )}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
             <Form.Item label={
               <Tooltip placement="topLeft" title="瀵逛簬涓嶇粡甯告�у彉鍔ㄧ殑淇℃伅锛岀紦瀛樻暟鎹湁鍔╀簬鎻愰珮鏌ヨ鏁堢巼銆�">
                 <Icon type="question-circle" />
diff --git a/src/views/menudesign/menuform/index.scss b/src/views/menudesign/menuform/index.scss
index 71a1a33..fb20291 100644
--- a/src/views/menudesign/menuform/index.scss
+++ b/src/views/menudesign/menuform/index.scss
@@ -7,4 +7,10 @@
   .ant-input-number {
     width: 100%;
   }
+  .ant-form-item {
+    height: 50px;
+    .ant-form-explain {
+      font-size: 12px;
+    }
+  }
 }
\ No newline at end of file
diff --git a/src/views/mobdesign/index.jsx b/src/views/mobdesign/index.jsx
index b090a70..677496a 100644
--- a/src/views/mobdesign/index.jsx
+++ b/src/views/mobdesign/index.jsx
@@ -1,47 +1,130 @@
 import React, { Component } from 'react'
 import { connect } from 'react-redux'
 import { DndProvider } from 'react-dnd'
+import { withRouter } from 'react-router'
 import { is, fromJS } from 'immutable'
 import moment from 'moment'
 import HTML5Backend from 'react-dnd-html5-backend'
-import { Icon, Tabs, notification, Modal } from 'antd'
-// import html2canvas from 'html2canvas'
+import { ConfigProvider, notification, Modal, Collapse, Switch, Button, message, Spin } from 'antd'
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js'
 import zhCN from '@/locales/zh-CN/mob.js'
 import enUS from '@/locales/en-US/mob.js'
+import antdEnUS from 'antd/es/locale/en_US'
+import antdZhCN from 'antd/es/locale/zh_CN'
+import MKEmitter from '@/utils/events.js'
+import MenuUtils from '@/utils/utils-custom.js'
 import asyncComponent from '@/utils/asyncComponent'
 
 import './index.scss'
 
-const { TabPane } = Tabs
+const { Panel } = Collapse
 const { confirm } = Modal
 
 const Header = asyncComponent(() => import('@/mob/header'))
-const Controller = asyncComponent(() => import('@/mob/controller'))
+const MenuForm = asyncComponent(() => import('./menuform'))
 const MobShell = asyncComponent(() => import('@/mob/mobshell'))
-const SourceWrap = asyncComponent(() => import('@/mob/modelsource'))
-const DataSource = asyncComponent(() => import('@/mob/datasource'))
+const SourceWrap = asyncComponent(() => import('@/mob/modulesource'))
+const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
+const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
+const Quotecomponent = asyncComponent(() => import('@/pc/quotecomponent'))
+const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
+const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
+const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
+const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
+const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
+const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
+const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
 
-class Mobile extends Component {
+sessionStorage.setItem('isEditState', 'true')
+sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
+sessionStorage.setItem('appType', 'mob')       // 搴旂敤绫诲瀷
+document.body.className = ''
+window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.CacheIndependent = new Map()
+window.GLOB.urlFields = []               // url鍙橀噺
+window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
+
+class MobDesign extends Component {
   state = {
-    dict: localStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
-    appId: this.props.match.params.appId,
-    appType: this.props.match.params.appType,
-    appCode: this.props.match.params.appCode,
-    appName: this.props.match.params.appName,
+    localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    loading: true,
+    MenuId: '',
+    MenuName: '',
+    MenuNo: '',
+    delButtons: [],
+    copyButtons: [],
+    thawButtons: [],
+    activeKey: 'basedata',
+    menuloading: false,
     oriConfig: null,
-    parentId: '',
-    openEdition: '',
-    saveIng: false,
     config: null,
-    pageIndex: 0,
-    editElem: null
+    visible: false,
+    customComponents: [],
   }
 
   UNSAFE_componentWillMount() {
-    this.getAppParam(this.props.match.params.appId)
+    if (this.props.memberLevel < 30) return
+    try {
+      let param = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
+
+      if (param.type === 'app') {
+        sessionStorage.setItem('appId', param.ID || '')
+        sessionStorage.setItem('lang', param.lang || 'zh-CN')
+        sessionStorage.setItem('kei_no', param.kei_no || '')
+        sessionStorage.setItem('link_type', param.link_type || 'true')
+        sessionStorage.setItem('role_type', param.role_type || 'true')
+        sessionStorage.setItem('login_types', param.login_types || 'true')
+
+        this.setState({
+          localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
+          dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+        })
+        this.getAppMessage()
+      } else if (param.type === 'view') {
+        this.setState({
+          MenuId: param.MenuID
+        }, () => {
+          this.getMenuParam(param)
+        })
+      }
+    } catch {
+      notification.warning({
+        top: 92,
+        message: '鑿滃崟淇℃伅瑙f瀽閿欒锛�',
+        duration: 5
+      })
+    }
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (this.props.match.params.param !== nextProps.match.params.param) {
+      window.location.reload()
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    if (this.props.memberLevel < 30) {
+      document.getElementById('mk-mob-design-view').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh; height: 100vh; background: #fff;">鏈簲鐢ㄦ病鏈塒C绔〉闈㈢殑缂栬緫鏉冮檺锛岃鑱旂郴绠$悊鍛橈紒</div>'
+      return
+    }
+    MKEmitter.addListener('delButtons', this.delButtons)
+    MKEmitter.addListener('thawButtons', this.thawButtons)
+    MKEmitter.addListener('copyButtons', this.copyButtons)
+    MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
+    setTimeout(() => {
+      this.updateCustomComponent()
+      this.getAppPictures()
+    }, 1000)
   }
 
   /**
@@ -51,239 +134,1320 @@
     this.setState = () => {
       return
     }
+    MKEmitter.removeListener('delButtons', this.delButtons)
+    MKEmitter.removeListener('thawButtons', this.thawButtons)
+    MKEmitter.removeListener('copyButtons', this.copyButtons)
+    MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
   }
 
-  jumpToManage = () => {
+  changeEditMenu = (menu) => {
     const { oriConfig, config } = this.state
-    const _this = this
 
-    if (!is(fromJS(oriConfig), fromJS(config))) {
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    let param = {
+      MenuID: menu.MenuID,
+      copyMenuId: menu.copyMenuId || '',
+      type: 'view'
+    }
+
+    if (menu.fixed && menu.MenuNo && menu.MenuName) {
+      param.fixed = true
+      param.MenuNo = menu.MenuNo
+      param.MenuName = menu.MenuName
+    }
+
+    param = window.btoa(window.encodeURIComponent(JSON.stringify(param)))
+
+    if (param === this.props.match.params.param) return
+
+    this.props.history.push('/mobdesign/' + param)
+  }
+
+  getAppMessage = () => {
+    Api.getSystemConfig({
+      func: 's_get_keyids',
+      bid: sessionStorage.getItem('appId')
+    }).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let homeId = ''
+      let appViewList = []
+      if (res.data && res.data.length > 0) {
+        appViewList = res.data
+        appViewList.forEach(item => {
+          if (item.keys_type === 'index') {
+            homeId = item.keys_id
+          }
+        })
+      }
+
+      if (!homeId) {
+        homeId = Utils.getuuid()
+
+        let param = {
+          func: 's_kei_link_keyids_addupt',
+          BID: sessionStorage.getItem('appId'),
+          exec_type: 'y',
+          LText: ''
+        }
+
+        appViewList.unshift({
+          appkey: window.GLOB.appkey || '',
+          bid: sessionStorage.getItem('appId') || '',
+          kei_no: sessionStorage.getItem('kei_no') || '',
+          keys_id: homeId,
+          keys_type: 'index',
+          remark: '棣栭〉'
+        })
+
+        param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+        param.LText = param.LText.join(' union all ')
+        param.LText = Utils.formatOptions(param.LText)
+  
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt('', param.timestamp)
+
+        Api.getSystemConfig(param).then(result => {
+          if (!result.status) {
+            notification.warning({
+              top: 92,
+              message: result.message,
+              duration: 5
+            })
+          } else {
+            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+            sessionStorage.setItem('appHomeId', homeId)
+            this.props.history.replace('/mobdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
+          }
+        })
+      } else {
+        sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+        sessionStorage.setItem('appHomeId', homeId)
+        this.props.history.replace('/mobdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
+      }
+    })
+  }
+
+  getAppPictures = () => {
+    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
+
+    Api.getSystemConfig({
+      func: 's_url_db_adduptdel',
+      PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+      PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+      typecharone: 'image',
+      type: 'search'
+    }).then(res => {
+      if (res.status) {
+        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+        typecharone: 'video',
+        type: 'search'
+      }).then(res => {
+        if (res.status) {
+          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
+        }
+      })
+    })
+  }
+
+  updateCustomComponent = () => {
+    Api.getSystemConfig({
+      func: 's_get_custom_components',
+      typecharone: ''
+    }).then(res => {
+      let coms = []
+      if (res.cus_list && res.cus_list.length > 0) {
+        res.cus_list.forEach(item => {
+          let config = ''
+
+          try {
+            config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
+          } catch (e) {
+            console.warn('Parse Failure')
+            config = ''
+          }
+
+          if (!config || !item.c_name) return
+
+          window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
+          coms.push({
+            uuid: item.c_id,
+            type: 'menu',
+            title: item.c_name,
+            url: item.images,
+            component: config.type,
+            subtype: config.subtype,
+            width: config.width || 24,
+            config
+          })
+        })
+      }
+      this.setState({customComponents: coms})
+      this.getRoleFields()
+    })
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { config } = this.state
+
+    if (config.uuid !== parentId) return
+
+    let components = config.components.map(item => {
+      if (keys.includes(item.uuid)) {
+        item.style = {...item.style, ...style}
+      }
+      return item
+    })
+
+    this.setState({
+      config: {...config, components: []}
+    }, () => {
+      this.setState({
+        config: {...config, components: components}
+      })
+    })
+  }
+
+  delButtons = (items) => {
+    const { copyButtons, delButtons } = this.state
+
+    this.setState({
+      delButtons: [...delButtons, ...items],
+      copyButtons: copyButtons.filter(item => !items.includes(item.uuid))
+    })
+  }
+
+  copyButtons = (items) => {
+    this.setState({copyButtons: [...this.state.copyButtons, ...items]})
+  }
+  
+  thawButtons = (item) => {
+    this.setState({thawButtons: [...this.state.thawButtons, item]})
+  }
+
+  closeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!config) {
+      window.close()
+      return
+    }
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
       confirm({
-        title: '閰嶇疆宸蹭慨鏀癸紝鏀惧純淇濆瓨鍚楋紵',
+        title: '閰嶇疆淇℃伅鏈繚瀛橈紝纭畾鍏抽棴鍚楋紵',
         content: '',
-        okText: _this.state.dict['mob.confirm'],
-        cancelText: _this.state.dict['mob.cancel'],
         onOk() {
-          _this.props.history.replace('/mobmanage')
+          window.close()
         },
         onCancel() {}
       })
     } else {
-      _this.props.history.replace('/mobmanage')
+      window.close()
     }
   }
 
-  triggerSave = () => {
-    const { config, openEdition, parentId } = this.state
-
-    this.setState({
-      saveIng: true
-    })
+  getMenuParam = (urlParam) => {
+    const { MenuId } = this.state
 
     let param = {
-      func: 'sPC_TrdMenu_AddUpt',
-      ParentID: config.entrance ? '' : parentId,
-      MenuID: config.uuid,
-      MenuNo: config.MenuNo,
-      EasyCode: '',
-      Template: '',
-      MenuName: '',
-      PageParam: '',
-      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(config))),
-      // LText: _vals.func.map(item => `select '${menu.MenuID}' as MenuID,'${item.func}' as ProcName,'${item.label}' as MenuName`),
-      // LTexttb: _tables.map(item => `select '${menu.MenuID}' as MenuID,'${item}' as tbName`),
-      TypeCharOne: 'mob'
-    }
-
-    let _LText = ''
-    // _LText = _LText.join(' union all ')
-    let _LTexttb = ''
-    // _LTexttb = _LTexttb.join(' union all ')
-    
-    param.LText = Utils.formatOptions(_LText)
-    param.LTexttb = Utils.formatOptions(_LTexttb)
-    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
-    param.secretkey = Utils.encrypt(param.LText, param.timestamp)
-
-    if (openEdition) { // 鐗堟湰绠$悊
-      param.open_edition = openEdition
-    }
-
-    Api.getSystemConfig(param).then(response => {
-      if (response.status) {
-        this.setState({
-          oriConfig: fromJS(config).toJS(),
-          openEdition: response.open_edition || '',
-          saveIng: false
-        })
-      } else {
-        notification.warning({
-          top: 92,
-          message: response.message,
-          duration: 5
-        })
-      }
-    })
-    
-  }
-
-  getAppParam = (id) => {
-    Api.getSystemConfig({
       func: 'sPC_Get_LongParam',
-      MenuID: id,
-      TypeCharOne: 'mob'
-    }).then(result => {
-      if (result.status) {
-        let config = null
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'mob',
+      MenuID: MenuId
+    }
 
-        if (result.LongParam) {
-          try {
-            config = JSON.parse(window.decodeURIComponent(window.atob(result.LongParam)))
-          } catch (e) {
-            console.warn('Parse Failure')
-            config = null
-          }
-        }
-
-        if (!config) {
-          config = {
-            version: 1.0,
-            entrance: true,
-            label: '',
-            uuid: this.props.match.params.appId,
-            pageIndex: 0,
-            MenuNo: this.props.match.params.appCode,
-            sourcelist: [],
-            components: []
-          }
-        }
-        this.setState({
-          oriConfig: config,
-          config: fromJS(config).toJS(),
-          openEdition: result.open_edition || '',
-        })
-      } else {
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
         notification.warning({
           top: 92,
           message: result.message,
           duration: 5
         })
+        this.setState({loading: false})
+        return
+      } else if (!result.LongParam && urlParam.copyMenuId) {
+        this.getCopyParam(urlParam)
+      } else {
+        let config = null
+        let isCreate = false
+
+        try {
+          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
+        } catch (e) {
+          console.warn('Parse Failure')
+          config = null
+        }
+
+        if (!config) {
+          isCreate = true
+          config = {
+            version: 1.0,
+            uuid: MenuId,
+            MenuID: MenuId,
+            Template: 'webPage',
+            enabled: false,
+            MenuName: '',
+            MenuNo: '',
+            tables: [],
+            components: [],
+            viewType: 'menu',
+            style: {
+              backgroundColor: '#ffffff', backgroundImage: ''
+            }
+          }
+        }
+        
+        config.uuid = MenuId
+        config.MenuID = MenuId
+        config.open_edition = result.open_edition || ''
+        window.GLOB.urlFields = config.urlFields || []
+
+        if (urlParam.fixed) {
+          config.fixed = true
+          config.MenuName = urlParam.MenuName
+          config.MenuNo = urlParam.MenuNo
+        }
+
+        let indeComs = []
+        config.components.forEach(item => {
+          if (item.type === 'navbar') {
+            indeComs.push(fromJS(item).toJS())
+          }
+        })
+
+        if (indeComs.length === 0) {
+          this.setState({
+            oriConfig: isCreate ? null : config,
+            config: fromJS(config).toJS(),
+            loading: false
+          })
+          window.GLOB.customMenu = config
+        } else {
+          this.jointComponents(config, indeComs, isCreate)
+        }
+      }
+    })
+    this.getAppMenus()
+  }
+
+  getAppMenus = () => {
+    let _param = {
+      func: 's_get_app_menus',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'mob',
+      LText: `select '${window.GLOB.appkey}'`,
+      timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
+    }
+
+    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
+
+    Api.getSystemConfig(_param).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let appIndeList = sessionStorage.getItem('appViewList')
+      appIndeList = JSON.parse(appIndeList)
+      appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
+
+      let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
+      sessionStorage.setItem('appMenus', JSON.stringify(menus))
+    })
+  }
+
+  getCopyParam = (urlParam) => {
+    const { MenuId } = this.state
+
+    let param = {
+      func: 'sPC_Get_LongParam',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'mob',
+      MenuID: urlParam.copyMenuId
+    }
+
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        this.setState({loading: false})
+        return
+      } else if (!result.LongParam) {
+        notification.warning({
+          top: 92,
+          message: '鏈煡璇㈠埌澶嶅埗鑿滃崟閰嶇疆淇℃伅锛�',
+          duration: 5
+        })
+      }
+
+      let config = null
+
+      try {
+        config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
+      } catch (e) {
+        console.warn('Parse Failure')
+        config = null
+      }
+
+      
+      if (!config) {
+        config = {
+          version: 1.0,
+          uuid: MenuId,
+          MenuID: MenuId,
+          Template: 'webPage',
+          enabled: false,
+          MenuName: '',
+          MenuNo: '',
+          tables: [],
+          components: [],
+          viewType: 'menu',
+          style: {
+            backgroundColor: '#ffffff', backgroundImage: '', paddingLeft: '20px', paddingRight: '20px'
+          }
+        }
+      } else {
+        config.components = MenuUtils.resetConfig(config.components)
+        message.success('澶嶅埗鎴愬姛锛屼繚瀛樺悗鐢熸晥銆�')
+      }
+      
+      config.uuid = MenuId
+      config.MenuID = MenuId
+      config.open_edition = ''
+
+      let indeComs = []
+      config.components.forEach(item => {
+        if (item.type === 'navbar') {
+          indeComs.push(fromJS(item).toJS())
+        }
+      })
+
+      if (indeComs.length === 0) {
+        this.setState({
+          oriConfig: null,
+          config: fromJS(config).toJS(),
+          loading: false
+        })
+
+        window.GLOB.customMenu = config
+      } else {
+        this.jointComponents(config, indeComs, true)
       }
     })
   }
 
-  deleteCard = (id) => {
-    let _this = this
+  jointComponents = (config, indeComs, isCreate) => {
+    let deffers = indeComs.map(item => {
+      return new Promise(resolve => {
+        Api.getSystemConfig({
+          func: 'sPC_Get_LongParam',
+          TypeCharOne: sessionStorage.getItem('kei_no'),
+          typename: 'mob',
+          MenuID: item.uuid
+        }).then(res => {
+          res.uuid = item.uuid
+
+          if (!res.status) {
+            notification.warning({
+              top: 92,
+              message: res.message,
+              duration: 5
+            })
+          }
+          
+          resolve(res)
+        })
+      })
+    })
+    Promise.all(deffers).then(result => {
+      let _conf = {}
+      result.forEach(res => {
+        let _config = null
+        try {
+          _config = res.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(res.LongParam))) : null
+        } catch (e) {
+          console.warn('Parse Failure')
+          _config = null
+        }
+
+        if (_config) {
+          _config.open_edition = res.open_edition || ''
+          _conf[res.uuid] = _config
+          window.GLOB.CacheIndependent.set(res.uuid, fromJS(_config).toJS())
+        }
+      })
+
+      let _length = config.components.length
+      config.components = config.components.map(item => {
+        if (item.type === 'navbar') {
+          if (_conf[item.uuid]) {
+            item = _conf[item.uuid]
+          } else {
+            item = null
+          }
+        }
+        return item
+      })
+
+      config.components = config.components.filter(Boolean)
+
+      if (_length > config.components.length) {
+        notification.warning({
+          top: 92,
+          message: '閮ㄥ垎缁勪欢宸插垹闄わ紒',
+          duration: 5
+        })
+      }
+
+      this.setState({
+        oriConfig: isCreate ? null : fromJS(config).toJS(),
+        config: config,
+        loading: false
+      })
+      window.GLOB.customMenu = config
+    })
+  }
+
+  getMenuMessage = () => {
+    const { config } = this.state
+    let buttons = []
+    let _sort = 1
+
+    let traversal = (components) => {
+      components.forEach(item => {
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            traversal(tab.components)
+          })
+        } else if (item.type === 'group') {
+          traversal(item.components)
+        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.subcards.forEach(card => {
+            card.elements && card.elements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+            card.backElements && card.backElements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        } else if (item.type === 'line' || item.type === 'bar') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.cols && item.cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              this.checkBtn(btn)
+              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        }
+      })
+    }
+
+    traversal(config.components)
+
+    return buttons
+  }
+
+  checkBtn = (btn) => {
+    if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+      let hascheck = false
+      btn.verify.scripts.forEach(item => {
+        if (item.status === 'false') return
+  
+        if (/\$check@|@check\$/ig.test(item.sql)) {
+          hascheck = true
+        }
+      })
+      if (hascheck) {
+        notification.warning({
+          top: 92,
+          message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    }
+  }
+
+  filterConfig = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components = this.filterConfig(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.filterConfig(item.components)
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        item.search = item.search.filter(a => !a.origin)
+        item.action = item.action.filter(a => !a.origin)
+        item.cols = item.cols.filter(a => !a.origin)
+      }
+      return item
+    })
+  }
+
+  submitConfig = () => {
+    const { delButtons, copyButtons, thawButtons } = this.state
     let config = fromJS(this.state.config).toJS()
 
-    confirm({
-      title: '纭畾鍒犻櫎鍏冪礌鍚楋紵',
-      content: '',
-      okText: this.state.dict['mob.confirm'],
-      cancelText: this.state.dict['mob.cancel'],
-      onOk() {
-        config.components = config.components.filter(item => item.uuid !== id)
+    if (!config.MenuName || !config.MenuNo || (config.cacheUseful === 'true' && !config.cacheTime)) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
+        duration: 5
+      })
+      this.setState({
+        activeKey: 'basedata'
+      })
+      return
+    }
 
-        _this.setState({
-          config: config
+    this.setState({
+      menuloading: true
+    })
+
+    setTimeout(() => {
+      config.components = this.filterConfig(config.components)
+
+      if (config.enabled && this.verifyConfig()) {
+        config.enabled = false
+      }
+
+      let parMenuId = sessionStorage.getItem('kei_no') + 'pc' + sessionStorage.getItem('lang')
+      let param = {
+        func: 'sPC_TrdMenu_AddUpt',
+        FstID: parMenuId,
+        SndID: parMenuId,
+        ParentID: parMenuId,
+        MenuID: config.uuid,
+        MenuNo: config.MenuNo || '',
+        EasyCode: '',
+        Template: 'webPage',
+        TypeCharOne: sessionStorage.getItem('kei_no'),
+        Typename: 'mob',
+        MenuName: config.MenuName || '',
+        PageParam: JSON.stringify({Template: 'webPage'}),
+        open_edition: config.open_edition,
+        LText: '',
+        LTexttb: ''
+      }
+
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+
+      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
+        func: 'sPC_Button_AddUpt',
+        Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
+        ParentID: config.uuid,
+        MenuNo: config.MenuNo,
+        Template: 'webPage',
+        PageParam: '',
+        LongParam: '',
+        LText: []
+      }
+
+      btnParam.LText = this.getMenuMessage()
+      btnParam.LText = btnParam.LText.join(' union all ')
+
+      let btnIds = btnParam.LText // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
+
+      btnParam.LText = Utils.formatOptions(btnParam.LText)
+      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
+
+      new Promise(resolve => {
+        let _config = fromJS(config).toJS()
+        let indeComs = []
+        _config.components = _config.components.map(item => {
+          if (item.type === 'navbar') {
+            indeComs.push(item)
+            return {
+              type: 'navbar',
+              uuid: item.uuid
+            }
+          }
+          return item
+        })
+
+        param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+
+        if (indeComs.length === 0) {
+          resolve(true)
+        } else {
+          let new_open_edition = {}
+          let deffers = indeComs.map(item => {
+            return new Promise(resolve => {
+              let _item = window.GLOB.CacheIndependent.get(item.uuid)
+              if (_item && is(fromJS(_item), fromJS(item))) {
+                new_open_edition[item.uuid] = item.open_edition || ''
+                resolve()
+                return
+              }
+
+              let _param = {
+                func: 'sPC_TrdMenu_AddUpt',
+                FstID: parMenuId,
+                SndID: parMenuId,
+                ParentID: parMenuId,
+                MenuID: item.uuid,
+                MenuNo: item.wrap.MenuNo || '',
+                EasyCode: '',
+                Template: item.type,
+                TypeCharOne: sessionStorage.getItem('kei_no'),
+                Typename: 'mob',
+                MenuName: item.name || '',
+                PageParam: JSON.stringify({Template: item.type}),
+                open_edition: item.open_edition || '',
+                LText: '',
+                LTexttb: ''
+              }
+
+              _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(item)))
+              _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+              _param.secretkey = Utils.encrypt('', _param.timestamp)
+
+              let appMenuParam = null
+              if (item.type === 'navbar') {
+                appMenuParam = {
+                  func: 's_appmenus_addupt',
+                  exec_type: 'y'
+                }
+  
+                appMenuParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+                appMenuParam.secretkey = Utils.encrypt('', _param.timestamp)
+
+                let LText = []
+                let app_param = []
+                let kei_no = sessionStorage.getItem('kei_no')
+                let userid = sessionStorage.getItem('CloudUserID') || ''
+
+                item.menus.forEach((fst, findex) => {
+                  // LText.push(`select '${fst.MenuID}','${fst.name}','','0','${sessionStorage.getItem('appId')}','0','${(findex + 1) * 10}','10','','${userid}','${window.GLOB.appkey}','${fst.MenuNo || ''}','${kei_no}','pc'`)
+                  LText.push(`select '${fst.MenuID}','${fst.name}','','0','0','0','${(findex + 1) * 10}','10','','${userid}','${window.GLOB.appkey}','${fst.MenuNo || ''}','${kei_no}','pc'`)
+                  app_param.push(`select '${window.GLOB.appkey}','${fst.MenuID}','${userid}','${(findex + 1) * 10}','','${fst.name}','${fst.MenuNo || ''}','0','10','${kei_no}','pc'`)
+                  if (fst.property === 'classify' && fst.sublist.length > 0) {
+                    fst.sublist.forEach(scd => {
+                      LText.push(`select '${scd.MenuID}','${scd.name}','','0','${fst.MenuID}','0','${(findex + 1) * 10}','20','','${userid}','${window.GLOB.appkey}','${scd.MenuNo || ''}','${kei_no}','pc'`)
+                      app_param.push(`select '${window.GLOB.appkey}','${scd.MenuID}','${userid}','${(findex + 1) * 10}','','${scd.name}','${scd.MenuNo || ''}','${fst.MenuID}','20','${kei_no}','pc'`)
+                    
+                      if (scd.property === 'classify' && scd.sublist.length > 0) {
+                        scd.sublist.forEach(thd => {
+                          LText.push(`select '${thd.MenuID}','${thd.name}','','0','${scd.MenuID}','0','${(findex + 1) * 10}','20','','${userid}','${window.GLOB.appkey}','${thd.MenuNo || ''}','${kei_no}','pc'`)
+                          app_param.push(`select '${window.GLOB.appkey}','${thd.MenuID}','${userid}','${(findex + 1) * 10}','','${thd.name}','${thd.MenuNo || ''}','${scd.MenuID}','20','${kei_no}','pc'`)
+                        })
+                      }
+                    })
+                  }
+                })
+                appMenuParam.LText = Utils.formatOptions(LText.join(' union '))
+                appMenuParam.LText1 = Utils.formatOptions(app_param.join(' union '))
+              }
+
+              if (appMenuParam) {
+                Api.getSystemConfig(appMenuParam).then(_res => {
+                  if (!_res.status) {
+                    notification.warning({
+                      top: 92,
+                      message: _res.message,
+                      duration: 5
+                    })
+                    this.setState({ menuloading: false })
+                    return
+                  }
+
+                  Api.getSystemConfig(_param).then(res => {
+                    if (!res.status) {
+                      notification.warning({
+                        top: 92,
+                        message: res.message,
+                        duration: 5
+                      })
+                      this.setState({ menuloading: false })
+                      return
+                    }
+    
+                    new_open_edition[item.uuid] = res.open_edition || ''
+                    
+                    resolve()
+                  })
+                })
+              } else {
+                Api.getSystemConfig(_param).then(res => {
+                  if (!res.status) {
+                    notification.warning({
+                      top: 92,
+                      message: res.message,
+                      duration: 5
+                    })
+                    this.setState({ menuloading: false })
+                    return
+                  }
+  
+                  new_open_edition[item.uuid] = res.open_edition || ''
+                  resolve()
+                })
+              }
+            })
+          })
+          Promise.all(deffers).then(() => {
+            let appViewList = sessionStorage.getItem('appViewList')
+            appViewList = JSON.parse(appViewList)
+            let _length = appViewList.length
+            let appIndeList = appViewList.map(item => item.keys_id).join(',')
+            
+
+            config.components = config.components.map(item => {
+              if (item.type === 'navbar') {
+                item.open_edition = new_open_edition[item.uuid] || ''
+                window.GLOB.CacheIndependent.set(item.uuid, fromJS(item).toJS())
+
+                if (appIndeList.indexOf(item.uuid) === -1) {
+                  appViewList.unshift({
+                    appkey: window.GLOB.appkey || '',
+                    bid: sessionStorage.getItem('appId') || '',
+                    kei_no: sessionStorage.getItem('kei_no') || '',
+                    keys_id: item.uuid,
+                    keys_type: 'navbar',
+                    remark: item.name
+                  })
+                }
+              }
+              return item
+            })
+
+            if (appViewList.length > _length) {
+              let param = {
+                func: 's_kei_link_keyids_addupt',
+                BID: sessionStorage.getItem('appId'),
+                exec_type: 'y',
+                LText: ''
+              }
+    
+              param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+              param.LText = param.LText.join(' union all ')
+              param.LText = Utils.formatOptions(param.LText)
+        
+              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+              param.secretkey = Utils.encrypt('', param.timestamp)
+    
+              Api.getSystemConfig(param).then(result => {
+                if (!result.status) {
+                  notification.warning({
+                    top: 92,
+                    message: result.message,
+                    duration: 5
+                  })
+                  this.setState({ menuloading: false })
+                } else {
+                  sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+                  resolve(true)
+                }
+              })
+            } else {
+              resolve(true)
+            }
+          })
+        }
+      }).then(res => { // 鎸夐挳鎴栬彍鍗曞垹闄�
+        if (!res) return
+
+        if (delButtons.length === 0) {
+          return { status: true, nonexec: true }
+        } else {
+          let appHomeId = sessionStorage.getItem('appHomeId')
+          let _param = {
+            func: 'sPC_MainMenu_Del',
+            MenuID: delButtons.filter(id => id !== appHomeId).join(',')
+          }
+          return Api.getSystemConfig(_param)
+        }
+      }).then(res => { // 鎸夐挳瑙i櫎鍐荤粨
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        } else if (!res.nonexec) { // 鎵ц鍒犻櫎鍚庡埛鏂拌彍鍗曞垪琛�
+          this.getAppMenus()
+        }
+
+        let ids = thawButtons.filter(item => btnIds.indexOf(item) !== -1)
+        if (ids.length === 0) {
+          return { status: true }
+        } else {
+          return Api.getSystemConfig({
+            func: 'sPC_MainMenu_ReDel',
+            MenuID: ids.join(',')
+          })
+        }
+      }).then(res => { // 椤甸潰淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          return Api.getSystemConfig(param)
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          config.open_edition = res.open_edition || ''
+
+          this.setState({
+            oriConfig: fromJS(config).toJS(),
+          })
+
+          if (btnParam.LText) {
+            return Api.getSystemConfig(btnParam)
+          } else {
+            return {
+              status: true
+            }
+          }
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 鎸夐挳澶嶅埗
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+
+        if (copyButtons.length === 0) {
+          return {
+            status: true
+          }
+        } else {
+          return new Promise(resolve => {
+            let deffers = copyButtons.map(item => {
+              return new Promise(resolve => {
+                if (btnIds.indexOf(item.uuid) === -1) { // 澶嶅埗鐨勬寜閽凡鍒犻櫎
+                  resolve({
+                    status: true
+                  })
+                  return
+                }
+
+                Api.getSystemConfig({
+                  func: 'sPC_Get_LongParam',
+                  MenuID: item.$originUuid
+                }).then(result => {
+                  if (result.status) {
+                    let _conf = ''
+              
+                    try {
+                      _conf = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : ''
+                    } catch (e) {
+                      console.warn('Parse Failure')
+                      _conf = ''
+                    }
+                    
+                    if (_conf) {
+                      _conf.components = MenuUtils.resetConfig(_conf.components)
+                      _conf.uuid = item.uuid
+                      _conf.MenuID = item.uuid
+                      _conf.Template = 'webPage'
+                    } else {
+                      resolve({
+                        status: true
+                      })
+                      return
+                    }
+
+                    let _param = {
+                      func: 'sPC_ButtonParam_AddUpt',
+                      ParentID: config.uuid,
+                      MenuID: item.uuid,
+                      MenuNo: '',
+                      Template: 'webPage',
+                      MenuName: item.label,
+                      PageParam: JSON.stringify({Template: 'webPage'}),
+                      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_conf)))
+                    }
+            
+                    Api.getSystemConfig(_param).then(response => {
+                      resolve(response)
+                    })
+                  }
+                })
+              })
+            })
+            Promise.all(deffers).then(result => {
+              let error = null
+              result.forEach(response => {
+                if (!response.status) {
+                  error = response
+                }
+              })
+    
+              if (error) {
+                notification.warning({
+                  top: 92,
+                  message: error.message,
+                  duration: 5
+                })
+                resolve(false)
+              } else {
+                resolve({
+                  status: true
+                })
+              }
+            })
+          })
+        }
+      }).then(res => {
+        if (res && res.status) {
+          this.setState({
+            delButtons: [],
+            copyButtons: [],
+            thawButtons: [],
+            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
+          })
+        }
+      })
+    }, 300)
+  }
+
+  getRoleFields = () => {
+    if (sessionStorage.getItem('sysRoles') || sessionStorage.getItem('permFuncField')) return
+    Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
+      if (res.status) {
+        let _permFuncField = []
+        let _sysRoles = []
+
+        if (res.Roles && res.Roles.length > 0) {
+          _sysRoles = res.Roles.map(role => {
+            return {
+              uuid: Utils.getuuid(),
+              value: role.RoleID,
+              text: role.RoleName
+            }
+          })
+        }
+
+        if (res.sModular && res.sModular.length > 0) {
+          res.sModular.forEach(field => {
+            if (field.ModularNo) {
+              _permFuncField.push(field.ModularNo)
+            }
+          })
+          _permFuncField = _permFuncField.sort()
+        }
+
+        sessionStorage.setItem('sysRoles', JSON.stringify(_sysRoles))
+        sessionStorage.setItem('permFuncField', JSON.stringify(_permFuncField))
+      }
+    })
+  }
+
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    if (!config || (!config.enabled && this.verifyConfig(true))) {
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: !config.enabled}
+    })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+
+    config.components.forEach(item => {
+      if (error) return
+      if (['propcard', 'brafteditor', 'sandbox'].includes(item.subtype) && item.wrap.datatype === 'static') return
+
+      if (item.setting) {
+        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.length === 0) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType && !item.setting.primaryKey) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆涓婚敭锛乣
+        }
+      }
+      if (item.type === 'bar' || item.type === 'line' || item.type === 'pie') {
+        if (!item.plot.Xaxis) {
+          error = `缁勪欢銆�${item.name}銆嬪浘琛ㄥ瓧娈靛皻鏈缃紒`
+        }
+      }
+    })
+
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
+  }
+
+  // 鏇存柊閰嶇疆淇℃伅
+  updateConfig = (config) => {
+    this.setState({
+      config: config
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  insert = (item) => {
+    let config = fromJS(this.state.config).toJS()
+
+    config.components.push(item)
+
+    this.setState({config})
+    window.GLOB.customMenu = config
+  }
+
+  refreshView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    // Api.getSystemConfig({
+    //   func: 'sPC_MainMenu_Del',
+    //   MenuID: '1614740497468ku800sbg853vupf65v4'
+    // })
+
+    sessionStorage.removeItem('sysRoles')
+    sessionStorage.removeItem('permFuncField')
+    sessionStorage.removeItem('app_videos')
+    sessionStorage.removeItem('app_pictures')
+    
+    window.location.reload()
+  }
+
+  setHomeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    let param = {
+      func: 's_kei_link_keyids_addupt',
+      BID: sessionStorage.getItem('appId'),
+      exec_type: 'y',
+      LText: ''
+    }
+
+    let appViewList = sessionStorage.getItem('appViewList')
+    appViewList = appViewList ? JSON.parse(appViewList) : []
+    appViewList = appViewList.filter(item => item.keys_type !== 'index')
+
+    appViewList.unshift({
+      appkey: window.GLOB.appkey || '',
+      bid: sessionStorage.getItem('appId') || '',
+      kei_no: sessionStorage.getItem('kei_no') || '',
+      keys_id: config.MenuID,
+      keys_type: 'index',
+      remark: config.MenuName
+    })
+
+    param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+    param.LText = param.LText.join(' union all ')
+    param.LText = Utils.formatOptions(param.LText)
+
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+
+    confirm({
+      title: '纭畾璁剧疆鏈〉闈负棣栭〉鍚楋紵',
+      content: '',
+      onOk() {
+        Api.getSystemConfig(param).then(result => {
+          if (!result.status) {
+            notification.warning({
+              top: 92,
+              message: result.message,
+              duration: 5
+            })
+          } else {
+            sessionStorage.setItem('appHomeId', config.MenuID)
+            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+          }
         })
       },
       onCancel() {}
     })
   }
 
-  editCard = (element) => {
-    this.setState({
-      editElem: element
-    })
-  }
-
-  updateStyle = (proper) => {
-    const { config } = this.state
-
-    config.components = config.components.map(component => {
-      if (component.uuid === proper.componentId) {
-        Object.keys(component).forEach(key => {
-          let _uuid = component[key].uuid
-          if (_uuid && (_uuid === proper.uuid || _uuid === proper.classId)) {
-            if (component[key].substyle) {
-
-            } else {
-              component[key].style = {...component[key].style, ...proper.style}
-              // eslint-disable-next-line
-              for (let index in component[key].style) {
-                if (component[key].style[index] === '') {
-                  delete component[key].style[index]
-                }
-              }
-            }
-          }
-        })
-      }
-      return component
-    })
-    this.setState({config})
-  }
-
-  updateConfig = (config) => {
-    this.setState({
-      config: config
-    })
-  }
-
   render () {
-    const { saveIng, appType, config, editElem } = this.state
+    const { localedict, loading, activeKey, dict, MenuId, config, menuloading, customComponents } = this.state
 
     return (
-      <div className="mobile-view">
-        <Header view="design" jumpToManage={this.jumpToManage} triggerSave={this.triggerSave} saveIng={saveIng} />
-        <DndProvider backend={HTML5Backend}>
-          <div className="mob-body">
-            <div className="mob-tool">
-              <div className="mob-tool-content">
-                <div className="plus-content">
-                  <Icon type="plus-circle" />娣� 鍔� 缁� 浠�
-                </div>
-                <div className="useable-component">
-                  <SourceWrap appType={appType} />
-                </div>
+      <ConfigProvider locale={localedict}>
+        <div className="mk-mob-view" id="mk-mob-design-view">
+          <Header />
+          {loading ? <Spin className="view-spin" size="large" /> : null}
+          <DndProvider backend={HTML5Backend}>
+            <div className="menu-setting">
+              <div className="pc-setting-tools">
+                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+                  {/* 鍩烘湰淇℃伅 */}
+                  <Panel header={dict['mob.basemsg']} key="basedata">
+                    {/* 鑿滃崟淇℃伅 */}
+                    {config ? <MenuForm
+                      dict={dict}
+                      config={config}
+                      MenuId={MenuId}
+                      updateConfig={this.updateConfig}
+                    /> : null}
+                    {config ? <UrlFieldComponent config={config} updateConfig={this.updateConfig}/> : null}
+                    {/* 琛ㄥ悕娣诲姞 */}
+                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
+                  </Panel>
+                  {/* 缁勪欢娣诲姞 */}
+                  <Panel header={dict['mob.component']} key="component">
+                    <SourceWrap />
+                  </Panel>
+                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
+                    <SourceWrap components={customComponents} />
+                  </Panel> : null}
+                  <Panel header={'椤甸潰鏍峰紡'} key="background">
+                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
+                  </Panel>
+                </Collapse>
               </div>
-              <div className="mob-tool-other"></div>
             </div>
-            {appType === 'mob' && config ?
+            <div className="menu-control">
+              <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} />
+              <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>
+              <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
+            </div>
+            <div className={'menu-body' + (menuloading ? 'saving' : '')}>
               <div className="mob-shell">
-                <MobShell
-                  config={config}
-                  deleteCard={this.deleteCard}
-                  editCard={this.editCard}
-                  editId={editElem ? editElem.uuid : ''}
-                  handleList={this.updateConfig}
-                />
-              </div> : null
-            }
-            <div className="mob-setting">
-              {config ? <Tabs defaultActiveKey="1" animated={false} size="small">
-                <TabPane tab="閰嶇疆" key="1">
-                  <Controller editElem={editElem} updateStyle={this.updateStyle} />
-                </TabPane>
-                <TabPane tab="鏁版嵁婧�" key="2">
-                  <DataSource config={config} updateConfig={this.updateConfig} />
-                </TabPane>
-              </Tabs> : null}
+                {config ? <MobShell menu={config} handleList={this.updateConfig} /> : null}
+              </div>
             </div>
-          </div>
-        </DndProvider>
-      </div>
+          </DndProvider>
+          <StyleController />
+          <StyleCombController />
+          <ModalController />
+        </div>
+      </ConfigProvider>
     )
   }
 }
 
-const mapStateToProps = () => {
-  return {}
+const mapStateToProps = (state) => {
+  return {
+    memberLevel: state.memberLevel
+  }
 }
 
 const mapDispatchToProps = () => {
   return {}
 }
 
-export default connect(mapStateToProps, mapDispatchToProps)(Mobile)
\ No newline at end of file
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MobDesign))
\ No newline at end of file
diff --git a/src/views/mobdesign/index.scss b/src/views/mobdesign/index.scss
index 7251e22..7699f0b 100644
--- a/src/views/mobdesign/index.scss
+++ b/src/views/mobdesign/index.scss
@@ -1,78 +1,77 @@
-.mobile-view {
-  background: #000;
+.mk-mob-view {
   min-height: 100vh;
-  .mob-body {
-    width: 100vw;
-    height: 100vh;
-    overflow-x: hidden;
-    position: relative;
-    background: #262626;
-    padding: 50px 300px 0px 40px;
 
-    .mob-tool {
-      position: fixed;
-      left: 0;
-      top: 48px;
-      height: 100%;
-      width: 40px;
-      background: #262626;
-      box-shadow: 2px 0px 2px #000;
-      .mob-tool-content {
-        width: 100%;
-        .plus-content {
-          position: relative;
+  >.view-spin {
+    position: absolute;
+    z-index: 3;
+    left: calc(50% - 16px);
+    top: calc(50vh - 70px);
+  }
+  .modal-form-board {
+    padding-top: 0;
+  }
+  .menu-setting {
+    position: fixed;
+    left: 0;
+    top: 48px;
+    z-index: 10;
+    transition: left 0.3s;
+
+    .pc-setting-tools {
+      height: calc(100vh - 48px);
+      width: 300px;
+      background: #ffffff;
+      overflow-y: auto;
+      overflow-x: hidden;
+
+      > .ant-collapse {
+        background-color: #ffffff;
+        .ant-collapse-item.ant-collapse-item-active {
+          border-bottom: 1px solid #d9d9d9;
+        }
+        .ant-collapse-header {
+          padding: 11px 16px 10px 40px;
+          border-bottom: 1px solid #d9d9d9;
+          background: #1890ff;
           color: #ffffff;
-          width: 100%;
-          display: flex;
-          align-items: center;
-          writing-mode: tb-rl;
-          padding: 16px 0;
-          border-bottom: 1px solid #000;
-          cursor: pointer;
-          z-index: 10;
-          background: #202735;
-          i {
-            margin-bottom: 5px;
-            margin-left: 2px;
+        }
+        .ant-collapse-content-box {
+          .ant-form-item {
+            margin-bottom: 10px;
+          }
+          .model-table-tablemanage-view {
+            >.ant-list {
+              margin-top: 20px;
+              .ant-list-item {
+                display: -webkit-box;
+                padding-right: 20px;
+                position: relative;
+                padding-left: 5px;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                min-height: 55px;
+                width: 100%;
+                .anticon {
+                  position: absolute;
+                  top: 0px;
+                  right: 0px;
+                  padding: 3px 3px 10px 10px;
+                  cursor: pointer;
+                }
+              }
+            }
+            >.tables {
+              width: 66.66666667%!important;
+            }
+            >.ant-form-item-label {
+              width: 33.33333333%;
+            }
           }
         }
-
-        .useable-component {
-          position: absolute;
-          width: 305px;
-          top: 0;
-          bottom: 0;
-          left: -340px;
-          background: #fff;
-          opacity: 0;
-          transition: left 0.3s linear 0.1s, opacity 0.3s linear 0.1s;
-          overflow-y: auto;
-        }
       }
-      .mob-tool-content:hover {
-        .useable-component {
-          opacity: 1;
-          left: 40px;
-        }
-      }
-      .mob-tool-other {
-        position: relative;
-        z-index: 10;
-        height: 1000px;
-        background: #202735;
-      }
-    }
-
-    .mob-setting {
-      position: fixed;
-      right: 0;
-      top: 0;
-      z-index: 10;
-      height: 100%;
-      width: 300px;
-      background: #202735;
-      box-shadow: 0px 2px 2px #000;
-
+  
       >.ant-tabs {
         >.ant-tabs-bar {
           border-bottom: 1px solid #181F29;
@@ -88,62 +87,113 @@
         }
       }
     }
+    .pc-setting-tools::-webkit-scrollbar {
+      width: 4px;
+    }
+    .pc-setting-tools::-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);
+    }
+    .pc-setting-tools::-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);
+    }
+  }
+  .mob-shell {
+    width: 375px;
+    height: 680px;
+    margin: 0 auto;
+    background: #000000;
+    background-size: 100% 100%;
+    padding: 25px 13px 40px;
+    border-radius: 30px;
 
-    .mob-shell {
-      width: 375px;
-      height: 680px;
-      margin: 0 auto;
+    .mob-shell-inner {
+      width: 100%;
+      height: 100%;
+      overflow-y: auto;
+      overflow-x: hidden;
       background: #ffffff;
-      background-size: 100% 100%;
-      padding: 25px 13px 40px;
-      border-radius: 30px;
+      box-shadow: 0px 0px 2px #000000;
+    }
+    .mob-shell-inner::-webkit-scrollbar {
+      width: 2px;
+    }
+    .mob-shell-inner::-webkit-scrollbar-thumb {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
+      background: rgba(0, 0, 0, 0.23);
+      border-radius: 5px;
+    }
+    .mob-shell-inner::-webkit-scrollbar-track {
+      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+      border: 1px solid rgba(0, 0, 0, 0.07);
+      background: rgba(0, 0, 0, 0);
+      border-radius: 3px;
+    }
+  }
+  .menu-control {
+    position: fixed;
+    right: 0;
+    top: 48px;
+    height: 100vh;
+    padding: 20px 10px;
+    background: #ffffff;
+    z-index: 10;
+    transition: right 0.3s;
 
-      .mob-shell-inner {
-        width: 100%;
-        height: 100%;
-        overflow-y: auto;
-        overflow-x: hidden;
-        box-shadow: 0px 0px 2px #000000;
+    div:not(.draw), button:not(.ant-switch) {
+      display: block!important;
+      margin-bottom: 15px;
+      width: 100%;
+    }
+    .ant-switch.big {
+      min-width: 60px;
+      height: 24px;
+      line-height: 24px;
+      margin-bottom: 15px;
+      .ant-switch-inner {
+        font-size: 14px;
       }
-      .mob-shell-inner::-webkit-scrollbar {
-        width: 2px;
-      }
-      .mob-shell-inner::-webkit-scrollbar-thumb {
-        box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
-        background: rgba(0, 0, 0, 0.23);
-        border-radius: 5px;
-      }
-      .mob-shell-inner::-webkit-scrollbar-track {
-        box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
-        border: 1px solid rgba(0, 0, 0, 0.07);
-        background: rgba(0, 0, 0, 0);
-        border-radius: 3px;
-      }
+    }
+    .ant-switch.big:after {
+      width: 22px;
+      height: 22px;
     }
   }
 
-  .flex-container {
-    margin: 0 15px;
+  .menu-body {
+    width: 100vw;
+    height: 100vh;
+    overflow-x: hidden;
+    position: relative;
+    background: #959595;
+    padding: 50px 0px 0px;
+    overflow-y: auto;
+    .menu-shell-inner {
+      min-height: 100vh;
+      margin: 0 auto;
+    }
   }
-  .flex-container .inline {
-    width: 80px!important;
-    margin: 9px 9px 9px 0;
+  .menu-body.saving {
+    .anticon-tool {
+      display: none;
+    }
   }
-  .flex-container .small {
-    height: 20px!important;
-    line-height: 20px!important;
+  .menu-body::-webkit-scrollbar {
+    width: 7px;
   }
-  .sub-title {
-    color: #888;
-    font-size: 14px;
-    padding: 30px 0 18px 0;
+  .menu-body::-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);
   }
-  .placeholder {
-    background-color: #ebebef;
-    color: #bbb;
-    text-align: center;
-    height: 30px;
-    line-height: 30px;
-    width: 100%;
+  .menu-body::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
   }
-}
+}
\ No newline at end of file
diff --git a/src/views/mobdesign/menuform/index.jsx b/src/views/mobdesign/menuform/index.jsx
new file mode 100644
index 0000000..2335bcf
--- /dev/null
+++ b/src/views/mobdesign/menuform/index.jsx
@@ -0,0 +1,138 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Icon, Tooltip, InputNumber } from 'antd'
+
+import './index.scss'
+
+class CustomMenuForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object, // 瀛楀吀椤�
+    config: PropTypes.object,
+    MenuId: PropTypes.string,
+    updateConfig: PropTypes.func
+  }
+
+  state = {}
+
+  // 涓�浜岀骇鑿滃崟鍒囨崲
+  selectChange = (key, value) => {
+    const { config } = this.props
+
+    if (key === 'cacheUseful') {
+      this.props.updateConfig({...config, cacheUseful: value})
+    } else if (key === 'timeUnit') {
+      this.props.updateConfig({...config, timeUnit: value})
+    }
+  }
+
+  // 鑿滃崟鍚嶇О
+  changeName = (e) => {
+    this.props.updateConfig({...this.props.config, MenuName: e.target.value})
+  }
+
+  // 鑿滃崟鍙傛暟
+  changeNo = (e) => {
+    this.props.updateConfig({...this.props.config, MenuNo: e.target.value})
+  }
+
+  changeCacheDay = (val) => {
+    if (typeof(val) !== 'number') {
+      val = ''
+    }
+    this.props.updateConfig({...this.props.config, cacheTime: val})
+  }
+
+  render() {
+    const { dict, config } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="custom-menu-form">
+        <Row>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.name']}>
+              {getFieldDecorator('MenuName', {
+                initialValue: config.MenuName,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.name'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeName}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.param']}>
+              {getFieldDecorator('MenuNo', {
+                initialValue: config.MenuNo,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.param'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeNo}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="瀵逛簬涓嶇粡甯告�у彉鍔ㄧ殑淇℃伅锛岀紦瀛樻暟鎹湁鍔╀簬鎻愰珮鏌ヨ鏁堢巼銆�">
+                <Icon type="question-circle" />
+                缂撳瓨鏁版嵁
+              </Tooltip>
+            }>
+              {getFieldDecorator('cacheUseful', {
+                initialValue: config.cacheUseful || 'false'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('cacheUseful', e.target.value)}}>
+                  <Radio value="true">浣跨敤</Radio>
+                  <Radio value="false">涓嶄娇鐢�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鍗曚綅">
+              {getFieldDecorator('timeUnit', {
+                initialValue: config.timeUnit || 'day'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('timeUnit', e.target.value)}}>
+                  <Radio value="day">澶�</Radio>
+                  <Radio value="hour">灏忔椂</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col> : null}
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鏃堕暱">
+              {getFieldDecorator('cacheTime', {
+                initialValue: config.cacheTime,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + '鏃堕暱!'
+                  }
+                ]
+              })(
+                <InputNumber min={1} max={config.timeUnit !== 'hour' ? 7 : 23} precision={0} onChange={this.changeCacheDay}/>
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomMenuForm)
\ No newline at end of file
diff --git a/src/views/mobdesign/menuform/index.scss b/src/views/mobdesign/menuform/index.scss
new file mode 100644
index 0000000..fb20291
--- /dev/null
+++ b/src/views/mobdesign/menuform/index.scss
@@ -0,0 +1,16 @@
+.custom-menu-form {
+  .anticon-question-circle {
+    color: #c49f47;
+    position: relative;
+    left: -3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .ant-form-item {
+    height: 50px;
+    .ant-form-explain {
+      font-size: 12px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/views/mobmanage/index.jsx b/src/views/mobmanage/index.jsx
deleted file mode 100644
index 11f5baf..0000000
--- a/src/views/mobmanage/index.jsx
+++ /dev/null
@@ -1,36 +0,0 @@
-import React, {Component} from 'react'
-import asyncComponent from '@/utils/asyncComponent'
-import './index.scss'
-
-const Header = asyncComponent(() => import('@/mob/header'))
-const MobCard = asyncComponent(() => import('@/mob/mobcard'))
-
-class MobileManage extends Component {
-  /**
-   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
-   */
-  componentWillUnmount () {
-    this.setState = () => {
-      return
-    }
-  }
-
-  jumpMenu = (card) => {
-    let _type = 'mob'
-    if (card.type === 'pc') {
-      _type = 'pc'
-    }
-    this.props.history.replace(`/mobdesign/${card.uuid}/${_type}/${card.keiNo}/${card.name}`)
-  }
-
-  render () {
-    return (
-      <div className="mobile-manage">
-        <Header view="manage" />
-        <MobCard jumpMenu={this.jumpMenu}/>
-      </div>
-    )
-  }
-}
-
-export default MobileManage
\ No newline at end of file
diff --git a/src/views/mobmanage/index.scss b/src/views/mobmanage/index.scss
deleted file mode 100644
index 3178be3..0000000
--- a/src/views/mobmanage/index.scss
+++ /dev/null
@@ -1,10 +0,0 @@
-.mobile-manage {
-  background: #fff;
-  min-height: 100vh;
-
-  .mob-header-container {
-    padding-right: 0px;
-    position: relative;
-    z-index: 10;
-  }
-}
diff --git a/src/views/pcdesign/index.jsx b/src/views/pcdesign/index.jsx
new file mode 100644
index 0000000..29818ec
--- /dev/null
+++ b/src/views/pcdesign/index.jsx
@@ -0,0 +1,1485 @@
+import React, { Component } from 'react'
+import { connect } from 'react-redux'
+import { DndProvider } from 'react-dnd'
+import { withRouter } from 'react-router'
+import { is, fromJS } from 'immutable'
+import moment from 'moment'
+import HTML5Backend from 'react-dnd-html5-backend'
+import { ConfigProvider, notification, Modal, Collapse, Switch, Button, Icon, message, Spin } from 'antd'
+
+import Api from '@/api'
+import Utils from '@/utils/utils.js'
+import zhCN from '@/locales/zh-CN/mob.js'
+import enUS from '@/locales/en-US/mob.js'
+import antdEnUS from 'antd/es/locale/en_US'
+import antdZhCN from 'antd/es/locale/zh_CN'
+import MKEmitter from '@/utils/events.js'
+import MenuUtils from '@/utils/utils-custom.js'
+import asyncComponent from '@/utils/asyncComponent'
+
+import './index.scss'
+
+const { Panel } = Collapse
+const { confirm } = Modal
+
+const MenuForm = asyncComponent(() => import('./menuform'))
+const MenuShell = asyncComponent(() => import('@/pc/menushell'))
+const SourceWrap = asyncComponent(() => import('@/pc/modulesource'))
+const BgController = asyncComponent(() => import('@/pc/bgcontroller'))
+const Quotecomponent = asyncComponent(() => import('@/pc/quotecomponent'))
+const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
+const StyleController = asyncComponent(() => import('@/menu/stylecontroller'))
+const SysInterface = asyncComponent(() => import('@/menu/sysinterface'))
+const UrlFieldComponent = asyncComponent(() => import('@/menu/urlfieldcomponent'))
+const PictureController = asyncComponent(() => import('@/menu/picturecontroller'))
+const ModalController = asyncComponent(() => import('@/menu/modalconfig/controller'))
+const StyleCombController = asyncComponent(() => import('@/menu/stylecombcontroller'))
+const StyleCombControlButton = asyncComponent(() => import('@/menu/stylecombcontrolbutton'))
+const TableComponent = asyncComponent(() => import('@/templates/sharecomponent/tablecomponent'))
+
+sessionStorage.setItem('isEditState', 'true')
+sessionStorage.setItem('editMenuType', 'menu') // 缂栬緫鑿滃崟绫诲瀷
+sessionStorage.setItem('appType', 'pc')        // 搴旂敤绫诲瀷
+document.body.className = ''
+window.GLOB.UserComponentMap = new Map() // 缂撳瓨鐢ㄦ埛鑷畾涔夌粍浠�
+window.GLOB.CacheIndependent = new Map()
+window.GLOB.urlFields = []               // url鍙橀噺
+window.GLOB.customMenu = null            // 淇濆瓨鑿滃崟淇℃伅
+
+class MenuDesign extends Component {
+  state = {
+    localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
+    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
+    loading: true,
+    MenuId: '',
+    MenuName: '',
+    MenuNo: '',
+    delButtons: [],
+    copyButtons: [],
+    thawButtons: [],
+    activeKey: 'basedata',
+    menuloading: false,
+    oriConfig: null,
+    config: null,
+    popBtn: null,             // 寮圭獥鏍囩椤�
+    visible: false,
+    customComponents: [],
+    settingshow: sessionStorage.getItem('settingshow') !== 'false',
+    controlshow: sessionStorage.getItem('controlshow') !== 'false',
+  }
+
+  UNSAFE_componentWillMount() {
+    if (this.props.memberLevel < 30) return
+    try {
+      let param = JSON.parse(window.decodeURIComponent(window.atob(this.props.match.params.param)))
+
+      if (param.type === 'app') {
+        sessionStorage.setItem('appId', param.ID || '')
+        sessionStorage.setItem('lang', param.lang || 'zh-CN')
+        sessionStorage.setItem('kei_no', param.kei_no || '')
+        sessionStorage.setItem('link_type', param.link_type || 'true')
+        sessionStorage.setItem('role_type', param.role_type || 'true')
+        sessionStorage.setItem('login_types', param.login_types || 'true')
+
+        this.setState({
+          localedict: sessionStorage.getItem('lang') !== 'en-US' ? antdZhCN : antdEnUS,
+          dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
+        })
+        this.getAppMessage()
+      } else if (param.type === 'view') {
+        this.setState({
+          MenuId: param.MenuID
+        }, () => {
+          this.getMenuParam(param)
+        })
+      }
+    } catch {
+      notification.warning({
+        top: 92,
+        message: '鑿滃崟淇℃伅瑙f瀽閿欒锛�',
+        duration: 5
+      })
+    }
+  }
+
+  UNSAFE_componentWillReceiveProps(nextProps) {
+    if (this.props.match.params.param !== nextProps.match.params.param) {
+      window.location.reload()
+    }
+  }
+
+  shouldComponentUpdate (nextProps, nextState) {
+    return !is(fromJS(this.state), fromJS(nextState))
+  }
+
+  componentDidMount () {
+    if (this.props.memberLevel < 30) {
+      document.getElementById('mk-pc-design-view').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh; height: 100vh; background: #fff;">鏈簲鐢ㄦ病鏈塒C绔〉闈㈢殑缂栬緫鏉冮檺锛岃鑱旂郴绠$悊鍛橈紒</div>'
+      return
+    }
+    MKEmitter.addListener('delButtons', this.delButtons)
+    MKEmitter.addListener('thawButtons', this.thawButtons)
+    MKEmitter.addListener('copyButtons', this.copyButtons)
+    MKEmitter.addListener('changePopview', this.initPopview)
+    MKEmitter.addListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.addListener('updateCustomComponent', this.updateCustomComponent)
+    setTimeout(() => {
+      this.updateCustomComponent()
+      this.getAppPictures()
+    }, 1000)
+  }
+
+  /**
+   * @description 缁勪欢閿�姣侊紝娓呴櫎state鏇存柊
+   */
+  componentWillUnmount () {
+    this.setState = () => {
+      return
+    }
+    MKEmitter.removeListener('delButtons', this.delButtons)
+    MKEmitter.removeListener('thawButtons', this.thawButtons)
+    MKEmitter.removeListener('copyButtons', this.copyButtons)
+    MKEmitter.removeListener('changePopview', this.initPopview)
+    MKEmitter.removeListener('changeEditMenu', this.changeEditMenu)
+    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
+    MKEmitter.removeListener('updateCustomComponent', this.updateCustomComponent)
+  }
+
+  changeEditMenu = (menu) => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    let param = {
+      MenuID: menu.MenuID,
+      copyMenuId: menu.copyMenuId || '',
+      type: 'view'
+    }
+
+    if (menu.fixed && menu.MenuNo && menu.MenuName) {
+      param.fixed = true
+      param.MenuNo = menu.MenuNo
+      param.MenuName = menu.MenuName
+    }
+
+    param = window.btoa(window.encodeURIComponent(JSON.stringify(param)))
+
+    if (param === this.props.match.params.param) return
+
+    this.props.history.push('/pcdesign/' + param)
+  }
+
+  getAppMessage = () => {
+    Api.getSystemConfig({
+      func: 's_get_keyids',
+      bid: sessionStorage.getItem('appId')
+    }).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let homeId = ''
+      let appViewList = []
+      if (res.data && res.data.length > 0) {
+        appViewList = res.data
+        appViewList.forEach(item => {
+          if (item.keys_type === 'index') {
+            homeId = item.keys_id
+          }
+        })
+      }
+
+      if (!homeId) {
+        homeId = Utils.getuuid()
+
+        let param = {
+          func: 's_kei_link_keyids_addupt',
+          BID: sessionStorage.getItem('appId'),
+          exec_type: 'y',
+          LText: ''
+        }
+
+        appViewList.unshift({
+          appkey: window.GLOB.appkey || '',
+          bid: sessionStorage.getItem('appId') || '',
+          kei_no: sessionStorage.getItem('kei_no') || '',
+          keys_id: homeId,
+          keys_type: 'index',
+          remark: '棣栭〉'
+        })
+
+        param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+        param.LText = param.LText.join(' union all ')
+        param.LText = Utils.formatOptions(param.LText)
+  
+        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+        param.secretkey = Utils.encrypt('', param.timestamp)
+
+        Api.getSystemConfig(param).then(result => {
+          if (!result.status) {
+            notification.warning({
+              top: 92,
+              message: result.message,
+              duration: 5
+            })
+          } else {
+            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+            sessionStorage.setItem('appHomeId', homeId)
+            this.props.history.replace('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
+          }
+        })
+      } else {
+        sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+        sessionStorage.setItem('appHomeId', homeId)
+        this.props.history.replace('/pcdesign/' + window.btoa(window.encodeURIComponent(JSON.stringify({MenuID: homeId, type: 'view'}))))
+      }
+    })
+  }
+
+  getAppPictures = () => {
+    if (sessionStorage.getItem('app_videos') || sessionStorage.getItem('app_pictures')) return
+
+    Api.getSystemConfig({
+      func: 's_url_db_adduptdel',
+      PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+      PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+      typecharone: 'image',
+      type: 'search'
+    }).then(res => {
+      if (res.status) {
+        sessionStorage.setItem('app_pictures', JSON.stringify(res.data || []))
+      }
+
+      Api.getSystemConfig({
+        func: 's_url_db_adduptdel',
+        PageIndex: 0,  // 0 浠h〃鍏ㄩ儴
+        PageSize: 0,   // 0 浠h〃鍏ㄩ儴
+        typecharone: 'video',
+        type: 'search'
+      }).then(res => {
+        if (res.status) {
+          sessionStorage.setItem('app_videos', JSON.stringify(res.data || []))
+        }
+      })
+    })
+  }
+
+  updateCustomComponent = () => {
+    Api.getSystemConfig({
+      func: 's_get_custom_components',
+      typecharone: ''
+    }).then(res => {
+      let coms = []
+      if (res.cus_list && res.cus_list.length > 0) {
+        res.cus_list.forEach(item => {
+          let config = ''
+
+          try {
+            config = JSON.parse(window.decodeURIComponent(window.atob(item.long_param)))
+          } catch (e) {
+            console.warn('Parse Failure')
+            config = ''
+          }
+
+          if (!config || !item.c_name) return
+
+          window.GLOB.UserComponentMap.set(item.c_id, item.c_name)
+          coms.push({
+            uuid: item.c_id,
+            type: 'menu',
+            title: item.c_name,
+            url: item.images,
+            component: config.type,
+            subtype: config.subtype,
+            width: config.width || 24,
+            config
+          })
+        })
+      }
+      this.setState({customComponents: coms})
+      this.getRoleFields()
+    })
+  }
+
+  updateComponentStyle = (parentId, keys, style) => {
+    const { config } = this.state
+
+    if (config.uuid !== parentId) return
+
+    let components = config.components.map(item => {
+      if (keys.includes(item.uuid)) {
+        item.style = {...item.style, ...style}
+      }
+      return item
+    })
+
+    this.setState({
+      config: {...config, components: []}
+    }, () => {
+      this.setState({
+        config: {...config, components: components}
+      })
+    })
+  }
+
+  delButtons = (items) => {
+    const { copyButtons, delButtons } = this.state
+
+    this.setState({
+      delButtons: [...delButtons, ...items],
+      copyButtons: copyButtons.filter(item => !items.includes(item.uuid))
+    })
+  }
+
+  copyButtons = (items) => {
+    this.setState({copyButtons: [...this.state.copyButtons, ...items]})
+  }
+  
+  thawButtons = (item) => {
+    this.setState({thawButtons: [...this.state.thawButtons, item]})
+  }
+
+  initPopview = (card, btn) => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    btn.config = fromJS(config).toJS()
+    btn.component = card
+
+    sessionStorage.setItem('editMenuType', 'popview') // 缂栬緫寮圭獥鏍囩
+
+    this.setState({popBtn: btn, visible: true})
+  }
+
+  closeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!config) {
+      window.close()
+      return
+    }
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      confirm({
+        title: '閰嶇疆淇℃伅鏈繚瀛橈紝纭畾鍏抽棴鍚楋紵',
+        content: '',
+        onOk() {
+          window.close()
+        },
+        onCancel() {}
+      })
+    } else {
+      window.close()
+    }
+  }
+
+  getMenuParam = (urlParam) => {
+    const { MenuId } = this.state
+
+    let param = {
+      func: 'sPC_Get_LongParam',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'pc',
+      MenuID: MenuId
+    }
+
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        this.setState({loading: false})
+        return
+      } else if (!result.LongParam && urlParam.copyMenuId) {
+        this.getCopyParam(urlParam)
+      } else {
+        let config = null
+        let isCreate = false
+
+        try {
+          config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
+        } catch (e) {
+          console.warn('Parse Failure')
+          config = null
+        }
+
+        if (!config) {
+          isCreate = true
+          config = {
+            version: 1.0,
+            uuid: MenuId,
+            MenuID: MenuId,
+            Template: 'webPage',
+            enabled: false,
+            MenuName: '',
+            MenuNo: '',
+            tables: [],
+            components: [],
+            viewType: 'menu',
+            style: {
+              backgroundColor: '#ffffff', backgroundImage: '', paddingLeft: '20px', paddingRight: '20px'
+            }
+          }
+        }
+        
+        config.uuid = MenuId
+        config.MenuID = MenuId
+        config.open_edition = result.open_edition || ''
+        window.GLOB.urlFields = config.urlFields || []
+
+        if (urlParam.fixed) {
+          config.fixed = true
+          config.MenuName = urlParam.MenuName
+          config.MenuNo = urlParam.MenuNo
+        }
+
+        let indeComs = []
+        config.components.forEach(item => {
+          if (item.type === 'navbar') {
+            indeComs.push(fromJS(item).toJS())
+          }
+        })
+
+        if (indeComs.length === 0) {
+          this.setState({
+            oriConfig: isCreate ? null : config,
+            config: fromJS(config).toJS(),
+            loading: false
+          })
+
+          window.GLOB.customMenu = config
+        } else {
+          this.jointComponents(config, indeComs, isCreate)
+        }
+      }
+    })
+    this.getAppMenus()
+  }
+
+  getAppMenus = () => {
+    let _param = {
+      func: 's_get_app_menus',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'pc',
+      LText: `select '${window.GLOB.appkey}'`,
+      timestamp: moment().format('YYYY-MM-DD HH:mm:ss')
+    }
+
+    _param.secretkey = Utils.encrypt(_param.LText, _param.timestamp)
+
+    Api.getSystemConfig(_param).then(res => {
+      if (!res.status) {
+        notification.warning({
+          top: 92,
+          message: res.message,
+          duration: 5
+        })
+        return
+      }
+
+      let appIndeList = sessionStorage.getItem('appViewList')
+      appIndeList = JSON.parse(appIndeList)
+      appIndeList = appIndeList.map(item => (item.keys_type !== 'index' ? item.keys_id : '')).join(',')
+
+      let menus = res.menus.filter(item => appIndeList.indexOf(item.MenuID) === -1)
+      sessionStorage.setItem('appMenus', JSON.stringify(menus))
+    })
+  }
+
+  getCopyParam = (urlParam) => {
+    const { MenuId } = this.state
+
+    let param = {
+      func: 'sPC_Get_LongParam',
+      TypeCharOne: sessionStorage.getItem('kei_no'),
+      typename: 'pc',
+      MenuID: urlParam.copyMenuId
+    }
+
+    Api.getSystemConfig(param).then(result => {
+      if (!result.status) {
+        notification.warning({
+          top: 92,
+          message: result.message,
+          duration: 5
+        })
+        this.setState({loading: false})
+        return
+      } else if (!result.LongParam) {
+        notification.warning({
+          top: 92,
+          message: '鏈煡璇㈠埌澶嶅埗鑿滃崟閰嶇疆淇℃伅锛�',
+          duration: 5
+        })
+      }
+
+      let config = null
+
+      try {
+        config = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : null
+      } catch (e) {
+        console.warn('Parse Failure')
+        config = null
+      }
+
+      
+      if (!config) {
+        config = {
+          version: 1.0,
+          uuid: MenuId,
+          MenuID: MenuId,
+          Template: 'webPage',
+          enabled: false,
+          MenuName: '',
+          MenuNo: '',
+          tables: [],
+          components: [],
+          viewType: 'menu',
+          style: {
+            backgroundColor: '#ffffff', backgroundImage: '', paddingLeft: '20px', paddingRight: '20px'
+          }
+        }
+      } else {
+        config.components = MenuUtils.resetConfig(config.components)
+        message.success('澶嶅埗鎴愬姛锛屼繚瀛樺悗鐢熸晥銆�')
+      }
+      
+      config.uuid = MenuId
+      config.MenuID = MenuId
+      config.open_edition = ''
+
+      let indeComs = []
+      config.components.forEach(item => {
+        if (item.type === 'navbar') {
+          indeComs.push(fromJS(item).toJS())
+        }
+      })
+
+      if (indeComs.length === 0) {
+        this.setState({
+          oriConfig: null,
+          config: fromJS(config).toJS(),
+          loading: false
+        })
+        window.GLOB.customMenu = config
+      } else {
+        this.jointComponents(config, indeComs, true)
+      }
+    })
+  }
+
+  jointComponents = (config, indeComs, isCreate) => {
+    let deffers = indeComs.map(item => {
+      return new Promise(resolve => {
+        Api.getSystemConfig({
+          func: 'sPC_Get_LongParam',
+          TypeCharOne: sessionStorage.getItem('kei_no'),
+          typename: 'pc',
+          MenuID: item.uuid
+        }).then(res => {
+          res.uuid = item.uuid
+
+          if (!res.status) {
+            notification.warning({
+              top: 92,
+              message: res.message,
+              duration: 5
+            })
+          }
+          
+          resolve(res)
+        })
+      })
+    })
+    Promise.all(deffers).then(result => {
+      let _conf = {}
+      result.forEach(res => {
+        let _config = null
+        try {
+          _config = res.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(res.LongParam))) : null
+        } catch (e) {
+          console.warn('Parse Failure')
+          _config = null
+        }
+
+        if (_config) {
+          _config.open_edition = res.open_edition || ''
+          _conf[res.uuid] = _config
+          window.GLOB.CacheIndependent.set(res.uuid, fromJS(_config).toJS())
+        }
+      })
+
+      let _length = config.components.length
+      config.components = config.components.map(item => {
+        if (item.type === 'navbar') {
+          if (_conf[item.uuid]) {
+            item = _conf[item.uuid]
+          } else {
+            item = null
+          }
+        }
+        return item
+      })
+
+      config.components = config.components.filter(Boolean)
+
+      if (_length > config.components.length) {
+        notification.warning({
+          top: 92,
+          message: '閮ㄥ垎缁勪欢宸插垹闄わ紒',
+          duration: 5
+        })
+      }
+
+      this.setState({
+        oriConfig: isCreate ? null : fromJS(config).toJS(),
+        config: config,
+        loading: false
+      })
+
+      window.GLOB.customMenu = config
+    })
+  }
+
+  getMenuMessage = () => {
+    const { config } = this.state
+    let buttons = []
+    let _sort = 1
+
+    let traversal = (components) => {
+      components.forEach(item => {
+        if (item.type === 'tabs') {
+          item.subtabs.forEach(tab => {
+            traversal(tab.components)
+          })
+        } else if (item.type === 'group') {
+          traversal(item.components)
+        } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.subcards.forEach(card => {
+            card.elements && card.elements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+            card.backElements && card.backElements.forEach(cell => {
+              if (cell.eleType !== 'button') return
+              this.checkBtn(cell)
+              buttons.push(`select '${cell.uuid}' as menuid, '${item.name + '-' + cell.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        } else if (item.type === 'line' || item.type === 'bar') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+        } else if (item.type === 'table' && item.subtype === 'normaltable') {
+          item.action && item.action.forEach(btn => {
+            this.checkBtn(btn)
+            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+            _sort++
+          })
+          item.cols && item.cols.forEach(col => {
+            if (col.type !== 'action') return
+            col.elements.forEach(btn => {
+              this.checkBtn(btn)
+              buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
+              _sort++
+            })
+          })
+        }
+      })
+    }
+
+    traversal(config.components)
+
+    return buttons
+  }
+
+  checkBtn = (btn) => {
+    if (['prompt', 'exec', 'pop'].includes(btn.OpenType) && btn.Ot === 'required' && btn.verify && btn.verify.scripts && btn.verify.scripts.length > 0) {
+      let hascheck = false
+      btn.verify.scripts.forEach(item => {
+        if (item.status === 'false') return
+  
+        if (/\$check@|@check\$/ig.test(item.sql)) {
+          hascheck = true
+        }
+      })
+      if (hascheck) {
+        notification.warning({
+          top: 92,
+          message: `鍙�夋嫨澶氳鐨勬寜閽��${btn.label}銆嬩腑 $check@ 鎴� @check$ 灏嗕笉浼氱敓鏁堬紒`,
+          duration: 5
+        })
+      }
+    }
+  }
+
+  filterConfig = (components) => {
+    return components.map(item => {
+      if (item.type === 'tabs') {
+        item.subtabs.forEach(tab => {
+          tab.components = this.filterConfig(tab.components)
+        })
+      } else if (item.type === 'group') {
+        item.components = this.filterConfig(item.components)
+      } else if (item.type === 'table' && item.subtype === 'normaltable') {
+        item.search = item.search.filter(a => !a.origin)
+        item.action = item.action.filter(a => !a.origin)
+        item.cols = item.cols.filter(a => !a.origin)
+      }
+      return item
+    })
+  }
+
+  submitConfig = () => {
+    const { delButtons, copyButtons, thawButtons } = this.state
+    let config = fromJS(this.state.config).toJS()
+
+    if (!config.MenuName || !config.MenuNo || (config.cacheUseful === 'true' && !config.cacheTime)) {
+      notification.warning({
+        top: 92,
+        message: '璇峰畬鍠勮彍鍗曞熀鏈俊鎭紒',
+        duration: 5
+      })
+      sessionStorage.setItem('settingshow', 'true')
+      this.setState({
+        settingshow: true,
+        activeKey: 'basedata'
+      })
+      return
+    }
+
+    this.setState({
+      menuloading: true
+    })
+
+    setTimeout(() => {
+      config.components = this.filterConfig(config.components)
+
+      if (config.enabled && this.verifyConfig()) {
+        config.enabled = false
+      }
+
+      let parMenuId = sessionStorage.getItem('kei_no') + 'pc' + sessionStorage.getItem('lang')
+      let param = {
+        func: 'sPC_TrdMenu_AddUpt',
+        FstID: parMenuId,
+        SndID: parMenuId,
+        ParentID: parMenuId,
+        MenuID: config.uuid,
+        MenuNo: config.MenuNo || '',
+        EasyCode: '',
+        Template: 'webPage',
+        TypeCharOne: sessionStorage.getItem('kei_no'),
+        Typename: 'pc',
+        MenuName: config.MenuName || '',
+        PageParam: JSON.stringify({Template: 'webPage'}),
+        open_edition: config.open_edition,
+        LText: '',
+        LTexttb: ''
+      }
+
+      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      param.secretkey = Utils.encrypt('', param.timestamp)
+
+      let btnParam = {             // 娣诲姞鑿滃崟鎸夐挳
+        func: 'sPC_Button_AddUpt',
+        Type: 40,                  // 娣诲姞鑿滃崟涓嬬殑鎸夐挳type涓�40锛屾寜閽笅鐨勬寜閽畉ype涓�60
+        ParentID: config.uuid,
+        MenuNo: config.MenuNo,
+        Template: 'webPage',
+        PageParam: '',
+        LongParam: '',
+        LText: []
+      }
+
+      btnParam.LText = this.getMenuMessage()
+      btnParam.LText = btnParam.LText.join(' union all ')
+
+      let btnIds = btnParam.LText // 鐢ㄤ簬澶嶅埗鎸夐挳鐨勮繃婊�
+
+      btnParam.LText = Utils.formatOptions(btnParam.LText)
+      btnParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+      btnParam.secretkey = Utils.encrypt(btnParam.LText, btnParam.timestamp)
+
+      new Promise(resolve => {
+        let _config = fromJS(config).toJS()
+        let indeComs = []
+        _config.components = _config.components.map(item => {
+          if (item.type === 'navbar') {
+            indeComs.push(item)
+            return {
+              type: 'navbar',
+              uuid: item.uuid
+            }
+          }
+          return item
+        })
+
+        param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(_config)))
+
+        if (indeComs.length === 0) {
+          resolve(true)
+        } else {
+          let new_open_edition = {}
+          let deffers = indeComs.map(item => {
+            return new Promise(resolve => {
+              let _item = window.GLOB.CacheIndependent.get(item.uuid)
+              if (_item && is(fromJS(_item), fromJS(item))) {
+                new_open_edition[item.uuid] = item.open_edition || ''
+                resolve()
+                return
+              }
+
+              let _param = {
+                func: 'sPC_TrdMenu_AddUpt',
+                FstID: parMenuId,
+                SndID: parMenuId,
+                ParentID: parMenuId,
+                MenuID: item.uuid,
+                MenuNo: item.wrap.MenuNo || '',
+                EasyCode: '',
+                Template: item.type,
+                TypeCharOne: sessionStorage.getItem('kei_no'),
+                Typename: 'pc',
+                MenuName: item.name || '',
+                PageParam: JSON.stringify({Template: item.type}),
+                open_edition: item.open_edition || '',
+                LText: '',
+                LTexttb: ''
+              }
+
+              _param.LongParam = window.btoa(window.encodeURIComponent(JSON.stringify(item)))
+              _param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+              _param.secretkey = Utils.encrypt('', _param.timestamp)
+
+              let appMenuParam = null
+              if (item.type === 'navbar') {
+                appMenuParam = {
+                  func: 's_appmenus_addupt',
+                  exec_type: 'y'
+                }
+  
+                appMenuParam.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+                appMenuParam.secretkey = Utils.encrypt('', _param.timestamp)
+
+                let LText = []
+                let app_param = []
+                let kei_no = sessionStorage.getItem('kei_no')
+                let userid = sessionStorage.getItem('CloudUserID') || ''
+
+                item.menus.forEach((fst, findex) => {
+                  // LText.push(`select '${fst.MenuID}','${fst.name}','','0','${sessionStorage.getItem('appId')}','0','${(findex + 1) * 10}','10','','${userid}','${window.GLOB.appkey}','${fst.MenuNo || ''}','${kei_no}','pc'`)
+                  LText.push(`select '${fst.MenuID}','${fst.name}','','0','0','0','${(findex + 1) * 10}','10','','${userid}','${window.GLOB.appkey}','${fst.MenuNo || ''}','${kei_no}','pc'`)
+                  app_param.push(`select '${window.GLOB.appkey}','${fst.MenuID}','${userid}','${(findex + 1) * 10}','','${fst.name}','${fst.MenuNo || ''}','0','10','${kei_no}','pc'`)
+                  if (fst.property === 'classify' && fst.sublist.length > 0) {
+                    fst.sublist.forEach(scd => {
+                      LText.push(`select '${scd.MenuID}','${scd.name}','','0','${fst.MenuID}','0','${(findex + 1) * 10}','20','','${userid}','${window.GLOB.appkey}','${scd.MenuNo || ''}','${kei_no}','pc'`)
+                      app_param.push(`select '${window.GLOB.appkey}','${scd.MenuID}','${userid}','${(findex + 1) * 10}','','${scd.name}','${scd.MenuNo || ''}','${fst.MenuID}','20','${kei_no}','pc'`)
+                    
+                      if (scd.property === 'classify' && scd.sublist.length > 0) {
+                        scd.sublist.forEach(thd => {
+                          LText.push(`select '${thd.MenuID}','${thd.name}','','0','${scd.MenuID}','0','${(findex + 1) * 10}','20','','${userid}','${window.GLOB.appkey}','${thd.MenuNo || ''}','${kei_no}','pc'`)
+                          app_param.push(`select '${window.GLOB.appkey}','${thd.MenuID}','${userid}','${(findex + 1) * 10}','','${thd.name}','${thd.MenuNo || ''}','${scd.MenuID}','20','${kei_no}','pc'`)
+                        })
+                      }
+                    })
+                  }
+                })
+                appMenuParam.LText = Utils.formatOptions(LText.join(' union '))
+                appMenuParam.LText1 = Utils.formatOptions(app_param.join(' union '))
+              }
+
+              if (appMenuParam) {
+                Api.getSystemConfig(appMenuParam).then(_res => {
+                  if (!_res.status) {
+                    notification.warning({
+                      top: 92,
+                      message: _res.message,
+                      duration: 5
+                    })
+                    this.setState({ menuloading: false })
+                    return
+                  }
+
+                  Api.getSystemConfig(_param).then(res => {
+                    if (!res.status) {
+                      notification.warning({
+                        top: 92,
+                        message: res.message,
+                        duration: 5
+                      })
+                      this.setState({ menuloading: false })
+                      return
+                    }
+    
+                    new_open_edition[item.uuid] = res.open_edition || ''
+                    
+                    resolve()
+                  })
+                })
+              } else {
+                Api.getSystemConfig(_param).then(res => {
+                  if (!res.status) {
+                    notification.warning({
+                      top: 92,
+                      message: res.message,
+                      duration: 5
+                    })
+                    this.setState({ menuloading: false })
+                    return
+                  }
+  
+                  new_open_edition[item.uuid] = res.open_edition || ''
+                  resolve()
+                })
+              }
+            })
+          })
+          Promise.all(deffers).then(() => {
+            let appViewList = sessionStorage.getItem('appViewList')
+            appViewList = JSON.parse(appViewList)
+            let _length = appViewList.length
+            let appIndeList = appViewList.map(item => item.keys_id).join(',')
+            
+
+            config.components = config.components.map(item => {
+              if (item.type === 'navbar') {
+                item.open_edition = new_open_edition[item.uuid] || ''
+                window.GLOB.CacheIndependent.set(item.uuid, fromJS(item).toJS())
+
+                if (appIndeList.indexOf(item.uuid) === -1) {
+                  appViewList.unshift({
+                    appkey: window.GLOB.appkey || '',
+                    bid: sessionStorage.getItem('appId') || '',
+                    kei_no: sessionStorage.getItem('kei_no') || '',
+                    keys_id: item.uuid,
+                    keys_type: 'navbar',
+                    remark: item.name
+                  })
+                }
+              }
+              return item
+            })
+
+            if (appViewList.length > _length) {
+              let param = {
+                func: 's_kei_link_keyids_addupt',
+                BID: sessionStorage.getItem('appId'),
+                exec_type: 'y',
+                LText: ''
+              }
+    
+              param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+              param.LText = param.LText.join(' union all ')
+              param.LText = Utils.formatOptions(param.LText)
+        
+              param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+              param.secretkey = Utils.encrypt('', param.timestamp)
+    
+              Api.getSystemConfig(param).then(result => {
+                if (!result.status) {
+                  notification.warning({
+                    top: 92,
+                    message: result.message,
+                    duration: 5
+                  })
+                  this.setState({ menuloading: false })
+                } else {
+                  sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+                  resolve(true)
+                }
+              })
+            } else {
+              resolve(true)
+            }
+          })
+        }
+      }).then(res => { // 鎸夐挳鎴栬彍鍗曞垹闄�
+        if (!res) return
+
+        if (delButtons.length === 0) {
+          return { status: true, nonexec: true }
+        } else {
+          let appHomeId = sessionStorage.getItem('appHomeId')
+          let _param = {
+            func: 'sPC_MainMenu_Del',
+            MenuID: delButtons.filter(id => id !== appHomeId).join(',')
+          }
+          return Api.getSystemConfig(_param)
+        }
+      }).then(res => { // 鎸夐挳瑙i櫎鍐荤粨
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        } else if (!res.nonexec) { // 鎵ц鍒犻櫎鍚庡埛鏂拌彍鍗曞垪琛�
+          this.getAppMenus()
+        }
+
+        let ids = thawButtons.filter(item => btnIds.indexOf(item) !== -1)
+        if (ids.length === 0) {
+          return { status: true }
+        } else {
+          return Api.getSystemConfig({
+            func: 'sPC_MainMenu_ReDel',
+            MenuID: ids.join(',')
+          })
+        }
+      }).then(res => { // 椤甸潰淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          return Api.getSystemConfig(param)
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 椤甸潰鎸夐挳鍏崇郴淇濆瓨
+        if (!res) return
+
+        if (res.status) {
+          config.open_edition = res.open_edition || ''
+
+          this.setState({
+            oriConfig: fromJS(config).toJS(),
+          })
+
+          if (btnParam.LText) {
+            return Api.getSystemConfig(btnParam)
+          } else {
+            return {
+              status: true
+            }
+          }
+        } else {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+      }).then(res => { // 鎸夐挳澶嶅埗
+        if (!res) return
+        if (!res.status) {
+          notification.warning({
+            top: 92,
+            message: res.message,
+            duration: 5
+          })
+          return false
+        }
+
+        if (copyButtons.length === 0) {
+          return {
+            status: true
+          }
+        } else {
+          return new Promise(resolve => {
+            let deffers = copyButtons.map(item => {
+              return new Promise(resolve => {
+                if (btnIds.indexOf(item.uuid) === -1) { // 澶嶅埗鐨勬寜閽凡鍒犻櫎
+                  resolve({
+                    status: true
+                  })
+                  return
+                }
+
+                Api.getSystemConfig({
+                  func: 'sPC_Get_LongParam',
+                  MenuID: item.$originUuid
+                }).then(result => {
+                  if (result.status) {
+                    let _conf = ''
+              
+                    try {
+                      _conf = result.LongParam ? JSON.parse(window.decodeURIComponent(window.atob(result.LongParam))) : ''
+                    } catch (e) {
+                      console.warn('Parse Failure')
+                      _conf = ''
+                    }
+                    
+                    if (_conf) {
+                      _conf.components = MenuUtils.resetConfig(_conf.components)
+                      _conf.uuid = item.uuid
+                      _conf.MenuID = item.uuid
+                      _conf.Template = 'webPage'
+                    } else {
+                      resolve({
+                        status: true
+                      })
+                      return
+                    }
+
+                    let _param = {
+                      func: 'sPC_ButtonParam_AddUpt',
+                      ParentID: config.uuid,
+                      MenuID: item.uuid,
+                      MenuNo: '',
+                      Template: 'webPage',
+                      MenuName: item.label,
+                      PageParam: JSON.stringify({Template: 'webPage'}),
+                      LongParam: window.btoa(window.encodeURIComponent(JSON.stringify(_conf)))
+                    }
+            
+                    Api.getSystemConfig(_param).then(response => {
+                      resolve(response)
+                    })
+                  }
+                })
+              })
+            })
+            Promise.all(deffers).then(result => {
+              let error = null
+              result.forEach(response => {
+                if (!response.status) {
+                  error = response
+                }
+              })
+    
+              if (error) {
+                notification.warning({
+                  top: 92,
+                  message: error.message,
+                  duration: 5
+                })
+                resolve(false)
+              } else {
+                resolve({
+                  status: true
+                })
+              }
+            })
+          })
+        }
+      }).then(res => {
+        if (res && res.status) {
+          this.setState({
+            delButtons: [],
+            copyButtons: [],
+            thawButtons: [],
+            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
+          })
+        }
+      })
+    }, 300)
+  }
+
+  getRoleFields = () => {
+    if (sessionStorage.getItem('sysRoles') || sessionStorage.getItem('permFuncField')) return
+    Api.getSystemConfig({func: 'sPC_Get_Roles_sModular'}).then(res => {
+      if (res.status) {
+        let _permFuncField = []
+        let _sysRoles = []
+
+        if (res.Roles && res.Roles.length > 0) {
+          _sysRoles = res.Roles.map(role => {
+            return {
+              uuid: Utils.getuuid(),
+              value: role.RoleID,
+              text: role.RoleName
+            }
+          })
+        }
+
+        if (res.sModular && res.sModular.length > 0) {
+          res.sModular.forEach(field => {
+            if (field.ModularNo) {
+              _permFuncField.push(field.ModularNo)
+            }
+          })
+          _permFuncField = _permFuncField.sort()
+        }
+
+        sessionStorage.setItem('sysRoles', JSON.stringify(_sysRoles))
+        sessionStorage.setItem('permFuncField', JSON.stringify(_permFuncField))
+      }
+    })
+  }
+
+  onEnabledChange = () => {
+    const { config } = this.state
+
+    if (!config || (!config.enabled && this.verifyConfig(true))) {
+      return
+    }
+
+    this.setState({
+      config: {...config, enabled: !config.enabled}
+    })
+  }
+
+  verifyConfig = (show) => {
+    const { config } = this.state
+    let error = ''
+
+    config.components.forEach(item => {
+      if (error) return
+      if (['propcard', 'brafteditor', 'sandbox'].includes(item.subtype) && item.wrap.datatype === 'static') return
+
+      if (item.setting) {
+        if (item.setting.interType === 'system' && item.setting.execute !== 'false' && !item.setting.dataresource) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType === 'system' && item.setting.execute === 'false' && item.scripts.length === 0) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆鏁版嵁婧愶紒`
+        } else if (item.setting.interType && !item.setting.primaryKey) {
+          error = `缁勪欢銆�${item.name}銆嬫湭璁剧疆涓婚敭锛乣
+        }
+      }
+      if (item.type === 'bar' || item.type === 'line' || item.type === 'pie') {
+        if (!item.plot.Xaxis) {
+          error = `缁勪欢銆�${item.name}銆嬪浘琛ㄥ瓧娈靛皻鏈缃紒`
+        }
+      }
+    })
+
+    if (show && error) {
+      notification.warning({
+        top: 92,
+        message: error,
+        duration: 5
+      })
+    }
+
+    return error
+  }
+
+  // 鏇存柊閰嶇疆淇℃伅
+  updateConfig = (config) => {
+    this.setState({
+      config: config
+    })
+
+    window.GLOB.customMenu = config
+  }
+
+  insert = (item) => {
+    let config = fromJS(this.state.config).toJS()
+
+    config.components.push(item)
+
+    this.setState({config})
+    window.GLOB.customMenu = config
+  }
+
+  refreshView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    // Api.getSystemConfig({
+    //   func: 'sPC_MainMenu_Del',
+    //   MenuID: '1614740497468ku800sbg853vupf65v4'
+    // })
+
+    sessionStorage.removeItem('sysRoles')
+    sessionStorage.removeItem('permFuncField')
+    sessionStorage.removeItem('app_videos')
+    sessionStorage.removeItem('app_pictures')
+    
+    window.location.reload()
+  }
+
+  setHomeView = () => {
+    const { oriConfig, config } = this.state
+
+    if (!oriConfig || !is(fromJS(oriConfig), fromJS(config))) {
+      notification.warning({
+        top: 92,
+        message: '閰嶇疆淇℃伅鏈繚瀛橈紒',
+        duration: 5
+      })
+      return
+    }
+
+    let param = {
+      func: 's_kei_link_keyids_addupt',
+      BID: sessionStorage.getItem('appId'),
+      exec_type: 'y',
+      LText: ''
+    }
+
+    let appViewList = sessionStorage.getItem('appViewList')
+    appViewList = appViewList ? JSON.parse(appViewList) : []
+    appViewList = appViewList.filter(item => item.keys_type !== 'index')
+
+    appViewList.unshift({
+      appkey: window.GLOB.appkey || '',
+      bid: sessionStorage.getItem('appId') || '',
+      kei_no: sessionStorage.getItem('kei_no') || '',
+      keys_id: config.MenuID,
+      keys_type: 'index',
+      remark: config.MenuName
+    })
+
+    param.LText = appViewList.map(item => `select '${item.keys_id}','${item.keys_type}','${item.kei_no}','${item.appkey}','${item.bid}','${sessionStorage.getItem('CloudUserID')}','${item.remark}'`)
+    param.LText = param.LText.join(' union all ')
+    param.LText = Utils.formatOptions(param.LText)
+
+    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
+    param.secretkey = Utils.encrypt('', param.timestamp)
+
+    confirm({
+      title: '纭畾璁剧疆鏈〉闈负棣栭〉鍚楋紵',
+      content: '',
+      onOk() {
+        Api.getSystemConfig(param).then(result => {
+          if (!result.status) {
+            notification.warning({
+              top: 92,
+              message: result.message,
+              duration: 5
+            })
+          } else {
+            sessionStorage.setItem('appHomeId', config.MenuID)
+            sessionStorage.setItem('appViewList', JSON.stringify(appViewList))
+          }
+        })
+      },
+      onCancel() {}
+    })
+  }
+
+  render () {
+    const { localedict, loading, activeKey, settingshow, controlshow, dict, MenuId, config, menuloading, customComponents } = this.state
+
+    return (
+      <ConfigProvider locale={localedict}>
+        <div className={'mk-pc-view '} id="mk-pc-design-view">
+          {loading ? <Spin className="view-spin" size="large" /> : null}
+          <DndProvider backend={HTML5Backend}>
+            <div className={'menu-setting ' + (!settingshow ? 'hidden' : '')}>
+              <div className="draw">
+                {settingshow ? <Icon onClick={() => {sessionStorage.setItem('settingshow', 'false'); this.setState({settingshow: false})}} type="double-left" /> : null}
+                {!settingshow ? <Icon onClick={() => {sessionStorage.setItem('settingshow', 'true'); this.setState({settingshow: true})}} type="double-right" /> : null}
+              </div>
+              <div className="pc-setting-tools">
+                <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
+                  {/* 鍩烘湰淇℃伅 */}
+                  <Panel header={dict['mob.basemsg']} key="basedata">
+                    {/* 鑿滃崟淇℃伅 */}
+                    {config ? <MenuForm
+                      dict={dict}
+                      config={config}
+                      MenuId={MenuId}
+                      updateConfig={this.updateConfig}
+                    /> : null}
+                    {config ? <UrlFieldComponent config={config} updateConfig={this.updateConfig}/> : null}
+                    {/* 琛ㄥ悕娣诲姞 */}
+                    {config ? <TableComponent config={config} updatetable={this.updateConfig}/> : null}
+                  </Panel>
+                  {/* 缁勪欢娣诲姞 */}
+                  <Panel header={dict['mob.component']} key="component">
+                    <SourceWrap />
+                  </Panel>
+                  {customComponents && customComponents.length ? <Panel header="鑷畾涔夌粍浠�" key="cuscomponent">
+                    <SourceWrap components={customComponents} />
+                  </Panel> : null}
+                  <Panel header={'椤甸潰鏍峰紡'} key="background">
+                    {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
+                  </Panel>
+                </Collapse>
+              </div>
+            </div>
+            <div className={'menu-control ' + (!controlshow ? 'hidden' : '')}>
+              <div className="draw">
+                {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} />
+              <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>
+              <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
+            </div>
+            <div className={'menu-body ' + (menuloading ? 'saving' : '')}>
+              {config && config.components ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
+            </div>
+          </DndProvider>
+          <StyleController />
+          <StyleCombController />
+          <ModalController />
+        </div>
+      </ConfigProvider>
+    )
+  }
+}
+
+const mapStateToProps = (state) => {
+  return {
+    memberLevel: state.memberLevel
+  }
+}
+
+const mapDispatchToProps = () => {
+  return {}
+}
+
+export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MenuDesign))
\ No newline at end of file
diff --git a/src/views/pcdesign/index.scss b/src/views/pcdesign/index.scss
new file mode 100644
index 0000000..01fc2a0
--- /dev/null
+++ b/src/views/pcdesign/index.scss
@@ -0,0 +1,204 @@
+.mk-pc-view {
+  background: #000;
+  min-height: 100vh;
+
+  >.view-spin {
+    position: absolute;
+    z-index: 3;
+    left: calc(50% - 16px);
+    top: calc(50vh - 70px);
+  }
+  .modal-form-board {
+    padding-top: 0;
+  }
+  .menu-setting {
+    position: fixed;
+    left: 0;
+    top: 0px;
+    z-index: 10;
+    transition: left 0.3s;
+
+    .draw {
+      position: absolute;
+      z-index: 1;
+      background: #ffffff;
+      right: -20px;
+      top: 0px;
+      box-shadow: 0 0 1px #959595;
+      border-radius: 0 2px 2px 0px;
+
+      i {
+        padding: 12px 3px;
+      }
+    }
+
+    .pc-setting-tools {
+      height: 100vh;
+      width: 300px;
+      background: #ffffff;
+      box-shadow: 0px 2px 5px #bcbcbc;
+      overflow-y: auto;
+      overflow-x: hidden;
+
+      > .ant-collapse {
+        background-color: #ffffff;
+        .ant-collapse-item.ant-collapse-item-active {
+          border-bottom: 1px solid #d9d9d9;
+        }
+        .ant-collapse-header {
+          padding: 11px 16px 10px 40px;
+          border-bottom: 1px solid #d9d9d9;
+          background: #1890ff;
+          color: #ffffff;
+        }
+        .ant-collapse-content-box {
+          .ant-form-item {
+            margin-bottom: 10px;
+          }
+          .model-table-tablemanage-view {
+            >.ant-list {
+              margin-top: 20px;
+              .ant-list-item {
+                display: -webkit-box;
+                padding-right: 20px;
+                position: relative;
+                padding-left: 5px;
+                overflow: hidden;
+                text-overflow: ellipsis;
+                -webkit-line-clamp: 2;
+                -webkit-box-orient: vertical;
+                min-height: 55px;
+                width: 100%;
+                .anticon {
+                  position: absolute;
+                  top: 0px;
+                  right: 0px;
+                  padding: 3px 3px 10px 10px;
+                  cursor: pointer;
+                }
+              }
+            }
+            >.tables {
+              width: 66.66666667%!important;
+            }
+            >.ant-form-item-label {
+              width: 33.33333333%;
+            }
+          }
+        }
+      }
+  
+      >.ant-tabs {
+        >.ant-tabs-bar {
+          border-bottom: 1px solid #181F29;
+          margin-bottom: 0px;
+          min-height: 48px;
+          .ant-tabs-tab {
+            padding: 14px 16px;
+            color: rgba(255, 255, 255, 0.85);
+          }
+          .ant-tabs-tab-active.ant-tabs-tab {
+            color: #1890ff;
+          }
+        }
+      }
+    }
+    .pc-setting-tools::-webkit-scrollbar {
+      width: 4px;
+    }
+    .pc-setting-tools::-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);
+    }
+    .pc-setting-tools::-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-setting.hidden {
+    left: -300px;
+  }
+
+  .menu-control {
+    position: fixed;
+    right: 0;
+    top: 0px;
+    height: 100vh;
+    padding: 20px 10px;
+    background: #ffffff;
+    z-index: 10;
+    transition: right 0.3s;
+    box-shadow: 0px 0px 5px #bcbcbc;
+
+    .draw {
+      position: absolute;
+      z-index: 1;
+      background: #ffffff;
+      left: -21px;
+      top: 0px;
+      box-shadow: 0 0 1px #959595;
+      border-radius: 0 2px 2px 0px;
+
+      i {
+        padding: 12px 3px;
+      }
+    }
+    div:not(.draw), button:not(.ant-switch) {
+      display: block!important;
+      margin-bottom: 15px;
+      width: 100%;
+    }
+    .ant-switch.big {
+      min-width: 60px;
+      height: 24px;
+      line-height: 24px;
+      margin-bottom: 15px;
+      .ant-switch-inner {
+        font-size: 14px;
+      }
+    }
+    .ant-switch.big:after {
+      width: 22px;
+      height: 22px;
+    }
+  }
+  .menu-control.hidden {
+    right: -130px;
+  }
+
+  .menu-body {
+    width: 100vw;
+    height: 100vh;
+    overflow-x: hidden;
+    position: relative;
+    background: #ffffff;
+    padding: 0px;
+    overflow-y: auto;
+    .menu-shell-inner {
+      min-height: 100vh;
+      margin: 0 auto;
+    }
+  }
+  .menu-body.saving {
+    .anticon-tool {
+      display: none;
+    }
+  }
+  .menu-body::-webkit-scrollbar {
+    width: 7px;
+  }
+  .menu-body::-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);
+  }
+  .menu-body::-webkit-scrollbar-track {
+    box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
+    border-radius: 3px;
+    border: 1px solid rgba(0, 0, 0, 0.07);
+    background: rgba(0, 0, 0, 0);
+  }
+}
\ No newline at end of file
diff --git a/src/views/pcdesign/menuform/index.jsx b/src/views/pcdesign/menuform/index.jsx
new file mode 100644
index 0000000..2335bcf
--- /dev/null
+++ b/src/views/pcdesign/menuform/index.jsx
@@ -0,0 +1,138 @@
+import React, {Component} from 'react'
+import PropTypes from 'prop-types'
+import { Form, Row, Col, Input, Radio, Icon, Tooltip, InputNumber } from 'antd'
+
+import './index.scss'
+
+class CustomMenuForm extends Component {
+  static propTpyes = {
+    dict: PropTypes.object, // 瀛楀吀椤�
+    config: PropTypes.object,
+    MenuId: PropTypes.string,
+    updateConfig: PropTypes.func
+  }
+
+  state = {}
+
+  // 涓�浜岀骇鑿滃崟鍒囨崲
+  selectChange = (key, value) => {
+    const { config } = this.props
+
+    if (key === 'cacheUseful') {
+      this.props.updateConfig({...config, cacheUseful: value})
+    } else if (key === 'timeUnit') {
+      this.props.updateConfig({...config, timeUnit: value})
+    }
+  }
+
+  // 鑿滃崟鍚嶇О
+  changeName = (e) => {
+    this.props.updateConfig({...this.props.config, MenuName: e.target.value})
+  }
+
+  // 鑿滃崟鍙傛暟
+  changeNo = (e) => {
+    this.props.updateConfig({...this.props.config, MenuNo: e.target.value})
+  }
+
+  changeCacheDay = (val) => {
+    if (typeof(val) !== 'number') {
+      val = ''
+    }
+    this.props.updateConfig({...this.props.config, cacheTime: val})
+  }
+
+  render() {
+    const { dict, config } = this.props
+    const { getFieldDecorator } = this.props.form
+    const formItemLayout = {
+      labelCol: {
+        xs: { span: 24 },
+        sm: { span: 8 }
+      },
+      wrapperCol: {
+        xs: { span: 24 },
+        sm: { span: 16 }
+      }
+    }
+
+    return (
+      <Form {...formItemLayout} className="custom-menu-form">
+        <Row>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.name']}>
+              {getFieldDecorator('MenuName', {
+                initialValue: config.MenuName,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.name'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeName}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={dict['mob.menu'] + dict['mob.param']}>
+              {getFieldDecorator('MenuNo', {
+                initialValue: config.MenuNo,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + dict['mob.menu'] + dict['mob.param'] + '!'
+                  }
+                ]
+              })(<Input placeholder="" disabled={!!(config.fixed && config.MenuName)} autoComplete="off" onChange={this.changeNo}/>)}
+            </Form.Item>
+          </Col>
+          <Col span={24}>
+            <Form.Item label={
+              <Tooltip placement="topLeft" title="瀵逛簬涓嶇粡甯告�у彉鍔ㄧ殑淇℃伅锛岀紦瀛樻暟鎹湁鍔╀簬鎻愰珮鏌ヨ鏁堢巼銆�">
+                <Icon type="question-circle" />
+                缂撳瓨鏁版嵁
+              </Tooltip>
+            }>
+              {getFieldDecorator('cacheUseful', {
+                initialValue: config.cacheUseful || 'false'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('cacheUseful', e.target.value)}}>
+                  <Radio value="true">浣跨敤</Radio>
+                  <Radio value="false">涓嶄娇鐢�</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col>
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鍗曚綅">
+              {getFieldDecorator('timeUnit', {
+                initialValue: config.timeUnit || 'day'
+              })(
+                <Radio.Group onChange={(e) => {this.selectChange('timeUnit', e.target.value)}}>
+                  <Radio value="day">澶�</Radio>
+                  <Radio value="hour">灏忔椂</Radio>
+                </Radio.Group>
+              )}
+            </Form.Item>
+          </Col> : null}
+          {config.cacheUseful === 'true' ? <Col span={24}>
+            <Form.Item label="鏃堕暱">
+              {getFieldDecorator('cacheTime', {
+                initialValue: config.cacheTime,
+                rules: [
+                  {
+                    required: true,
+                    message: dict['mob.required.input'] + '鏃堕暱!'
+                  }
+                ]
+              })(
+                <InputNumber min={1} max={config.timeUnit !== 'hour' ? 7 : 23} precision={0} onChange={this.changeCacheDay}/>
+              )}
+            </Form.Item>
+          </Col> : null}
+        </Row>
+      </Form>
+    )
+  }
+}
+
+export default Form.create()(CustomMenuForm)
\ No newline at end of file
diff --git a/src/views/pcdesign/menuform/index.scss b/src/views/pcdesign/menuform/index.scss
new file mode 100644
index 0000000..fb20291
--- /dev/null
+++ b/src/views/pcdesign/menuform/index.scss
@@ -0,0 +1,16 @@
+.custom-menu-form {
+  .anticon-question-circle {
+    color: #c49f47;
+    position: relative;
+    left: -3px;
+  }
+  .ant-input-number {
+    width: 100%;
+  }
+  .ant-form-item {
+    height: 50px;
+    .ant-form-explain {
+      font-size: 12px;
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/views/sso/index.jsx b/src/views/sso/index.jsx
index 077a30b..7af3a65 100644
--- a/src/views/sso/index.jsx
+++ b/src/views/sso/index.jsx
@@ -6,7 +6,7 @@
 
 import Api from '@/api'
 import Utils from '@/utils/utils.js' 
-import options from '@/store/options.js'
+import { styles } from '@/store/options.js'
 import { modifyMemberLevel } from '@/store/action'
 import './index.scss'
 
@@ -105,8 +105,8 @@
         window.GLOB.mainlogo = systemMsg.mainlogo
         window.GLOB.style = systemMsg.style
     
-        if (window.GLOB.style && options.styles[window.GLOB.style]) {
-          document.getElementById('root').className = options.styles[window.GLOB.style]
+        if (window.GLOB.style && styles[window.GLOB.style]) {
+          document.body.className = styles[window.GLOB.style]
         }
 
         if (res.titlelogo && window.GLOB.favicon !== res.titlelogo) {

--
Gitblit v1.8.0