king
2021-04-07 f3167f8371d19d0ea8fe7d0e7af5517ff0b08cd2
2021-04-07
6 文件已重命名
239个文件已修改
6 文件已复制
98个文件已添加
30个文件已删除
28393 ■■■■■ 已修改文件
package-lock.json 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/README.txt 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/admin.html 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/index.html 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/manifest.json 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/options.json 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/build.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
scripts/start.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cacheutils.js 371 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 636 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/global.scss 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/viewstyle.scss 128 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/carousel.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/carousel1.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/form.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/mobile.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/navbar-mob.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/navbar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Image/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/breadview/index.jsx 161 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/breadview/index.scss 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.scss 59 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/loginform.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/querylog/index.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/sidemenu/index.jsx 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/sidemenu/index.scss 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.jsx 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.scss 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/en-US/model.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/model.js 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/bgcontroller/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/card.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.scss 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 78 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/settingform/index.jsx 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/settingform/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/cardcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/index.jsx 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/settingform/index.jsx 194 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.jsx 276 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.scss 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx 214 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.jsx 358 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.scss 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.jsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/editorcode/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/editorcontent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/editor/braft-editor/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragtitle/card.jsx 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragtitle/index.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/dragtitle/index.scss 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/actionform/index.jsx 367 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/actionform/index.scss 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/formconfig.jsx 292 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.jsx 253 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.scss 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/groupform/index.jsx 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/groupform/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.jsx 704 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.scss 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.jsx 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 114 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/clockcomponent/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/clockcomponent/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/clockcomponent/settingform/index.jsx 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/clockcomponent/settingform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/logcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/markcomponent/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/index.jsx 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/usercomponent/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/index.jsx 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 19 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tablabelform/index.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/index.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/utils.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.jsx 632 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/option.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/padcontroller/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/picturecontroller/editform/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/picturecontroller/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/popview/index.jsx 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecombcontroller/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/styleInput/index.jsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/settingform/baseform/index.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/settingform/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/settingform/simplescript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/settingform/utils.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/urlfieldcomponent/index.jsx 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/urlfieldcomponent/index.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/urlfieldcomponent/settingform/index.jsx 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/urlfieldcomponent/settingform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-1/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-2/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/contdelete/index.jsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/contdelete/index.scss 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/controller/index.jsx 480 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/controller/index.scss 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/contupdate/index.jsx 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/contupdate/index.scss 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/index.jsx 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/index.scss 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/columnform/index.jsx 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/customscript/index.jsx 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/customscript/index.scss 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/index.jsx 519 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/index.scss 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/settingform/index.jsx 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/settingform/index.scss 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/settingform/utils.jsx 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/datasource/verifycard/utils.jsx 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/header/index.jsx 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/header/index.scss 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/home/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobcard/index.jsx 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobcard/index.scss 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobcard/mutilform/index.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/card.jsx 74 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/index.jsx 136 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modelsource/dragsource/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modelsource/dragsource/index.scss 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modelsource/index.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modelsource/option.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/dragsource/index.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/dragsource/index.scss 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/index.jsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/option.jsx 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/bgcontroller/index.jsx 172 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/bgcontroller/index.scss 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/index.jsx 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/index.scss 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/index.jsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/index.jsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx 207 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx 549 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/card.jsx 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.jsx 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.scss 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/dragsource/index.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/dragsource/index.scss 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/index.jsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/option.jsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/quotecomponent/index.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/quotecomponent/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/quotecomponent/settingform/index.jsx 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/quotecomponent/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/setupProxy.js 103 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/action-type.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/action.js 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/options.js 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/reducer.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/calendar/index.jsx 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 71 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardItem/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.jsx 180 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/cardItem/index.jsx 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/cardItem/index.scss 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.jsx 357 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.scss 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.jsx 363 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.scss 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.jsx 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.jsx 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/normal-form/index.jsx 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/normal-form/index.scss 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/group/normal-group/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.jsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.scss 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/tabtransfer/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/iframe/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/rolemanage/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/scriptmanage/actionList/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/scriptmanage/config.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/scriptmanage/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtable/index.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/treepage/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/verupmanage/actionList/index.jsx 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/verupmanage/config.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/verupmanage/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/verupmanage/subtabtable/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/excelInbutton/excelin/index.jsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/excelInbutton/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exceloutbutton/index.jsx 38 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 160 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/printbutton/index.jsx 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/cardcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/chartcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/dategroup/quarterpicker/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/dategroup/quarterpicker/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/checkCard/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/customTextArea/index.jsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.jsx 427 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.scss 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.jsx 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.scss 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/settingcomponent/editTable/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/calcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/index.jsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/source.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/tabcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/source.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/source.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editfirstmenu/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editfirstmenu/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editsecmenu/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editsecmenu/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editthdmenu/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editthdmenu/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editthdmenu/menuform/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/menuelement/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/checkCard/index.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/card.jsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.jsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.scss 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/groupform/index.jsx 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 406 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.scss 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/source.jsx 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/actionform/index.jsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/index.jsx 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/cardcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/chartcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/chartgroupcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/columncomponent/columnform/index.jsx 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/columncomponent/columnform/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/columncomponent/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/fieldscomponent/index.jsx 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/index.jsx 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/index.jsx 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/index.jsx 25 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/utils.jsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/tablecomponent/index.jsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/tabscomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/index.jsx 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/source.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/treepageconfig/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/treepageconfig/source.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/createinterface/index.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/customscript/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editTable/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editcomponent/index.jsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 120 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/index.jsx 158 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/callbackcustomscript/index.jsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/customscript/index.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/index.jsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/devutils.js 459 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-datamanage.js 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-update.js 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 515 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.jsx 587 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.scss 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/mutilform/index.jsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/mutilform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.jsx 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/header/index.jsx 382 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/header/index.scss 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/index.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/config.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/index.jsx 323 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/index.scss 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.jsx 95 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/logincloudform.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/loginform.jsx 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/main/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/menuform/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/menuform/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 1566 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.scss 282 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/menuform/index.jsx 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/menuform/index.scss 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobmanage/index.jsx 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobmanage/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 1485 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.scss 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/menuform/index.jsx 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/menuform/index.scss 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/sso/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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",
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",
public/README.txt
@@ -6,4 +6,6 @@
systemType        -- 判断业务系统为测试 (空) 或正式 (production) ,正式系统开发权限只含有系统升级等限定功能
externalDatabase  -- 外联库,不使用时默认为false
lineColor         -- 登录页分割线颜色
filter            -- 页面滤镜,值为'true'时,页面显示为黑白色
filter            -- 页面滤镜,值为'true'时,页面显示为黑白色
defaultApp        -- 默认应用,系统需默认打开某个子应用时需填写应用编码,空值时跳转到管理后台
defaultLang       -- 默认打开的子应用语言类型,填入defaultApp时有效
public/admin.html
New file
@@ -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>
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>
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",
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/"
}
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';
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';
src/api/cacheutils.js
New file
@@ -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 将数据写入websql
   */
  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 将数据写入IndexedDB
   */
  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
    }
  }
}
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 将数据写入websql
   */
  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
      })
src/assets/css/global.scss
File was deleted
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) {
src/assets/mobimg/carousel.png
src/assets/mobimg/carousel1.png
src/assets/mobimg/form.png
src/assets/mobimg/mobile.png
Binary files differ
src/assets/mobimg/navbar-mob.png
src/assets/mobimg/navbar.png
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
src/components/breadview/index.jsx
New file
@@ -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)
src/components/breadview/index.scss
New file
@@ -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;
}
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') { // 编辑状态时,增加参数debug
      _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') { // 正式系统版本升级后,页面刷新
      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为打开外部页面地址,newpage为打开系统菜单
        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>
        {/* 正常菜单 */}
        {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
        {/* 正常菜单 */}
        {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())
  }
}
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;
  }
}
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 = () => {
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: []
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))
export default connect(mapStateToProps, mapDispatchToProps)(Sidemenu)
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页中为iframe时
  max-height: 100vh;
  overflow-y: scroll;
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)
export default connect(mapStateToProps, mapDispatchToProps)(TabViews)
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);
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`)
// 新系统文件置于admin中 ../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
    }
    // 只有业务系统才可以设置为正式系统
@@ -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', {
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',
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': '拼接参数',
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: '',
  }
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>
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}
            />
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;
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 {
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 {
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
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)
export default CardCellComponent
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,        // 编辑组
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>
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%;
    }
  }
}
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)
export default DataCardEditComponent
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
  }
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)
export default PropCardEditComponent
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,        // 编辑组
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)
export default TableCardEditComponent
src/menu/components/carousel/cardcomponent/index.jsx
New file
@@ -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
src/menu/components/carousel/cardcomponent/index.scss
copy from src/mob/datasource/verifycard/columnform/index.scss copy to src/menu/components/carousel/cardcomponent/index.scss
src/menu/components/carousel/cardcomponent/settingform/index.jsx
New file
@@ -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)
src/menu/components/carousel/cardcomponent/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/components/carousel/data-card/index.jsx
New file
@@ -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
src/menu/components/carousel/data-card/index.scss
New file
@@ -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;
}
src/menu/components/carousel/data-card/wrapsetting/index.jsx
New file
@@ -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
src/menu/components/carousel/data-card/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx
New file
@@ -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)
src/menu/components/carousel/data-card/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/menu/components/carousel/prop-card/index.jsx
New file
@@ -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
src/menu/components/carousel/prop-card/index.scss
New file
@@ -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;
}
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: '当前窗口' }
      ]
    }
  ]
}
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)
export default antvBarLineChart
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: '当前窗口' }
      ]
    }
  ]
}
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)
export default antvBarLineChart
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: '',
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)
export default CodeSandBox
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
  }
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
  }
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)
export default BraftEditorComponent
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
  }
src/menu/components/form/dragtitle/card.jsx
New file
@@ -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
src/menu/components/form/dragtitle/index.jsx
New file
@@ -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
src/menu/components/form/dragtitle/index.scss
New file
@@ -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;
    }
  }
}
src/menu/components/form/formaction/actionform/index.jsx
New file
@@ -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)
src/menu/components/form/formaction/actionform/index.scss
New file
@@ -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;
  }
}
src/menu/components/form/formaction/formconfig.jsx
New file
@@ -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: '正式地址',
      initVal: card.proInterface || '',
      tooltip: '正式系统所使用的接口地址。',
      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: '如果自定义接口不支持跨域请求,会通过当前系统转发。',
      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: '隐藏'
      }]
    },
  ]
}
src/menu/components/form/formaction/index.jsx
New file
@@ -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
src/menu/components/form/formaction/index.scss
New file
@@ -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;
    }
  }
}
src/menu/components/form/normal-form/groupform/index.jsx
New file
@@ -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)
src/menu/components/form/normal-form/groupform/index.scss
File was renamed from src/templates/modalconfig/groupform/index.scss
@@ -10,4 +10,9 @@
  .ant-input-number {
    width: 100%;
  }
  .anticon-question-circle {
    color: #c49f47;
    position: relative;
    left: -3px;
  }
}
src/menu/components/form/normal-form/index.jsx
New file
@@ -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
src/menu/components/form/normal-form/index.scss
New file
@@ -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;
}
src/menu/components/form/wrapsetting/index.jsx
New file
@@ -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
src/menu/components/form/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/form/wrapsetting/settingform/index.jsx
New file
@@ -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)
src/menu/components/form/wrapsetting/settingform/index.scss
New file
@@ -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;
  }
}
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
  }
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)
export default NormalGroup
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)
export default MainSearchComponent
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
  }
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)
export default Form.create()(ActionForm)
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: '如果自定义接口不支持跨域请求,会通过当前系统转发。',
      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
    },
    {
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)
export default ActionComponent
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 {
src/menu/components/share/clockcomponent/index.jsx
New file
@@ -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
src/menu/components/share/clockcomponent/index.scss
New file
@@ -0,0 +1,7 @@
.clock-component-wrap {
  display: inline-block;
  >.anticon-clock-circle {
    color: rgb(38, 194, 129);
  }
}
src/menu/components/share/clockcomponent/settingform/index.jsx
New file
@@ -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)
src/menu/components/share/clockcomponent/settingform/index.scss
copy from src/mob/datasource/verifycard/columnform/index.scss copy to src/menu/components/share/clockcomponent/settingform/index.scss
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: [
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 }) }}
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)
export default SearchComponent
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
export default UserComponent
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',
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
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%;
    }
  }
}
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,    // 强制刷新
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)
export default TableCardEditComponent
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
  }
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)
export default antvTabs
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') {
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
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>
    )
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
  }
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)
export default DataSource
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%'}}>
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})}/>
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>
src/menu/datasource/verifycard/settingform/index.scss
@@ -21,4 +21,7 @@
  .ant-input-number {
    width: 100%;
  }
  .ant-radio-group {
    white-space: nowrap;
  }
}
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')
      })
    }
    
    // 正则替换
    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) {
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') {
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)
    }
  })
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)
export default ComModalConfig
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: '';
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 },
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: '',
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,
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">
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,按钮下的按钮type为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)
export default MenuDesign
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}
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}
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 = ''
src/menu/sysinterface/settingform/baseform/index.jsx
@@ -206,6 +206,22 @@
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'如果自定义接口不支持跨域请求,会通过当前系统转发。'}>
                  <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'
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
    }
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'}}>
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)
src/menu/urlfieldcomponent/index.jsx
New file
@@ -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
src/menu/urlfieldcomponent/index.scss
New file
@@ -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;
    }
  }
}
src/menu/urlfieldcomponent/settingform/index.jsx
New file
@@ -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)
src/menu/urlfieldcomponent/settingform/index.scss
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',
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',
src/mob/contdelete/index.jsx
File was deleted
src/mob/contdelete/index.scss
File was deleted
src/mob/controller/index.jsx
File was deleted
src/mob/controller/index.scss
File was deleted
src/mob/contupdate/index.jsx
File was deleted
src/mob/contupdate/index.scss
File was deleted
src/mob/datasource/index.jsx
File was deleted
src/mob/datasource/index.scss
File was deleted
src/mob/datasource/verifycard/columnform/index.jsx
File was deleted
src/mob/datasource/verifycard/customscript/index.jsx
File was deleted
src/mob/datasource/verifycard/customscript/index.scss
File was deleted
src/mob/datasource/verifycard/index.jsx
File was deleted
src/mob/datasource/verifycard/index.scss
File was deleted
src/mob/datasource/verifycard/settingform/index.jsx
File was deleted
src/mob/datasource/verifycard/settingform/index.scss
File was deleted
src/mob/datasource/verifycard/settingform/utils.jsx
File was deleted
src/mob/datasource/verifycard/utils.jsx
File was deleted
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))
export default MobHeader
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;
    }
  }
}
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) {
src/mob/mobcard/index.jsx
File was deleted
src/mob/mobcard/index.scss
File was deleted
src/mob/mobcard/mutilform/index.jsx
File was deleted
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>
  )
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
src/mob/modelsource/dragsource/index.jsx
File was deleted
src/mob/modelsource/dragsource/index.scss
File was deleted
src/mob/modelsource/index.jsx
File was deleted
src/mob/modelsource/option.jsx
File was deleted
src/mob/modulesource/dragsource/index.jsx
New file
@@ -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
src/mob/modulesource/dragsource/index.scss
New file
@@ -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;
  }
}
src/mob/modulesource/index.jsx
New file
@@ -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
src/mob/modulesource/index.scss
File was renamed from src/mob/modelsource/index.scss
@@ -1,5 +1,5 @@
.mob-card-source-box {
  padding: 20px 0px 20px 15px;
  padding: 20px 0px;
  position: relative;
  p {
src/mob/modulesource/option.jsx
New file
@@ -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 },
]
src/pc/bgcontroller/index.jsx
New file
@@ -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
src/pc/bgcontroller/index.scss
New file
@@ -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;
  }
}
src/pc/components/navbar/normal-navbar/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/index.scss
New file
@@ -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;
  }
}
src/pc/components/navbar/normal-navbar/linksetting/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/linksetting/index.scss
New file
@@ -0,0 +1,11 @@
.model-link-setting-wrap {
  display: inline-block;
  >.anticon-link {
    color: rgb(38, 194, 129);
  }
  >.anticon-edit {
    color: #1890ff;
  }
}
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx
New file
@@ -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)
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss
copy from src/mob/datasource/verifycard/columnform/index.scss copy to src/pc/components/navbar/normal-navbar/linksetting/linkform/index.scss
src/pc/components/navbar/normal-navbar/linksetting/linktable/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/linksetting/linktable/index.scss
New file
@@ -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;
  }
}
src/pc/components/navbar/normal-navbar/menusetting/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/menusetting/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-menu {
    color: purple;
  }
  >.anticon-edit {
    color: #1890ff;
  }
}
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx
New file
@@ -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)
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
copy from src/mob/datasource/verifycard/columnform/index.scss copy to src/pc/components/navbar/normal-navbar/menusetting/menuform/index.scss
src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/menusetting/menutable/index.scss
New file
@@ -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;
      }
    }
  }
}
src/pc/components/navbar/normal-navbar/wrapsetting/index.jsx
New file
@@ -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
src/pc/components/navbar/normal-navbar/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
New file
@@ -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)
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/pc/menushell/card.jsx
New file
@@ -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
src/pc/menushell/index.jsx
New file
@@ -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
src/pc/menushell/index.scss
New file
@@ -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;
  }
}
src/pc/modulesource/dragsource/index.jsx
New file
@@ -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
src/pc/modulesource/dragsource/index.scss
New file
@@ -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;
  }
}
src/pc/modulesource/index.jsx
New file
@@ -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
src/pc/modulesource/index.scss
copy from src/mob/modelsource/index.scss copy to src/pc/modulesource/index.scss
File was copied from src/mob/modelsource/index.scss
@@ -1,5 +1,5 @@
.mob-card-source-box {
  padding: 20px 0px 20px 15px;
  padding: 20px 0px;
  position: relative;
  p {
src/pc/modulesource/option.jsx
New file
@@ -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 },
]
src/pc/quotecomponent/index.jsx
New file
@@ -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
src/pc/quotecomponent/index.scss
New file
@@ -0,0 +1,6 @@
.quote-wrap {
  button {
    border-color: #40a9ff;
    color: #40a9ff;
  }
}
src/pc/quotecomponent/settingform/index.jsx
New file
@@ -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)
src/pc/quotecomponent/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
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)
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': '/'
    }
  }))
}
//   app.use(proxy('/trans', {
//     target: `${host}/${service}trans`,
//     secure: false,
//     changeOrigin: true,
//     pathRewrite: {
//     '^/trans': '/'
//     }
//   }))
// }
src/store/action-type.js
@@ -13,14 +13,8 @@
// 修改窗口样式,区分iframe与正常页面
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'
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
  }
}
src/store/options.js
@@ -1,37 +1,47 @@
// 系统配置
/**
 * @description 系统信息
 * 1、yun ( Y2xv$mkdWQ= ) 、 dandian ( U1$mkNP ) 、 yewu ( bG9j$mkYWw= )
 * 2、window.btoa('') 域名 不带 /
 * 3、baoshide old ( aHR0cDovL2Nsb3VkLnBv$mkc2l0ZWNncm91cC5jb206ODA4MA== )
 * 4、baoshide ( 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',
}
src/store/reducer.js
@@ -27,12 +27,10 @@
  tabviews: [],         // 导航栏
  collapse: _collapse,  // 是否收起侧边栏导航
  isiframe: false,      // 是否为iframe窗口
  editState: false,     // 是否为编辑状态,值为false、true
  editLevel: null,      // 编辑菜单级别,值为level1、level2、level3、HS
  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
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) {
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'     // 是否分页,转为boolean 统一格式
      config.setting.execute = config.setting.default !== 'false'     // 默认sql是否执行,转为boolean 统一格式
      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)
    }
  }
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 {
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,            // 卡片信息,包括正反面
  }
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>
      )
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('菜单参数解析错误!')
        }
      } 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
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)
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;
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)                   // 关键字符替换,base64加密
    _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('菜单参数解析错误!')
        }
      } 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
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)
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;
src/tabviews/custom/components/carousel/cardItem/index.jsx
New file
@@ -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
src/tabviews/custom/components/carousel/cardItem/index.scss
New file
@@ -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);
  }
}
src/tabviews/custom/components/carousel/data-card/index.jsx
New file
@@ -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('菜单参数解析错误!')
        }
      } 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)
src/tabviews/custom/components/carousel/data-card/index.scss
New file
@@ -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;
}
src/tabviews/custom/components/carousel/prop-card/index.jsx
New file
@@ -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('菜单参数解析错误!')
        }
      } 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)
src/tabviews/custom/components/carousel/prop-card/index.scss
New file
@@ -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;
}
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)                   // 关键字符替换,base64加密
    _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())
          }
  
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)                   // 关键字符替换,base64加密
    _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())
          }
  
src/tabviews/custom/components/form/normal-form/index.jsx
New file
@@ -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 => {
        // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
        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)
src/tabviews/custom/components/form/normal-form/index.scss
New file
@@ -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;
}
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}
        `
      }
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('菜单参数解析错误!')
        }
      } 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)) {
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 {
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}
        `
      }
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 {
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;
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}>
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)
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'
  }
src/tabviews/rolemanage/index.jsx
@@ -117,6 +117,11 @@
      let _openKeys = []
      // MenuID(菜单Id)、MenuName(菜单名称)、OnlySelf(值为true,表示三级菜单,增加-仅页面)、Type(菜单级别)
      // ParentID(父级Id)、Selected(是否选中-已失效)、Tabs( 标签类型)、TypeCharOne 菜单类型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) {
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 = {
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"}
    ]
  }
}
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,
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)
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 = '未知的返回结果!'
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,
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({
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': {
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,
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
        }),
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: '',
        }, () => {
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
        })
      }
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
      }, {
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}
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 => {
      // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
      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 => {
                // 数据源sql语句,预处理, 权限黑名单字段设置为隐藏表单
                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 => {
              // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
              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({
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 => {
      // 数据源sql语句,预处理,权限黑名单字段设置为隐藏表单
      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 => {
                // 数据源sql语句,预处理
                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 => {
              // 数据源sql语句,预处理
              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({
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') {
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}
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
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'
    }
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 {
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)
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;
      }
    }
  }
}
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)) {
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 {
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 truncated after the above file
src/tabviews/zshare/mutilform/index.scss src/tabviews/zshare/normalTable/index.jsx src/tabviews/zshare/normalTable/index.scss src/tabviews/zshare/settingcomponent/editTable/index.jsx src/tabviews/zshare/topSearch/index.jsx src/templates/calendarconfig/calcomponent/index.jsx src/templates/calendarconfig/index.jsx src/templates/calendarconfig/source.jsx src/templates/calendarconfig/tabcomponent/index.jsx src/templates/comtableconfig/index.jsx src/templates/comtableconfig/source.jsx src/templates/formtabconfig/index.jsx src/templates/formtabconfig/source.jsx src/templates/menuconfig/editfirstmenu/index.jsx src/templates/menuconfig/editfirstmenu/index.scss src/templates/menuconfig/editsecmenu/index.jsx src/templates/menuconfig/editsecmenu/index.scss src/templates/menuconfig/editthdmenu/index.jsx src/templates/menuconfig/editthdmenu/index.scss src/templates/menuconfig/editthdmenu/menuform/index.jsx src/templates/menuconfig/menuelement/card.jsx src/templates/modalconfig/checkCard/index.jsx src/templates/modalconfig/dragelement/card.jsx src/templates/modalconfig/dragelement/index.jsx src/templates/modalconfig/dragelement/index.scss src/templates/modalconfig/groupform/index.jsx (deleted) src/templates/modalconfig/index.jsx src/templates/modalconfig/index.scss src/templates/modalconfig/settingform/index.jsx src/templates/modalconfig/source.jsx src/templates/sharecomponent/actioncomponent/actionform/index.jsx src/templates/sharecomponent/actioncomponent/index.jsx src/templates/sharecomponent/actioncomponent/index.scss src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx src/templates/sharecomponent/actioncomponent/verifyexcelin/index.jsx src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx src/templates/sharecomponent/cardcomponent/index.jsx src/templates/sharecomponent/chartcomponent/index.jsx src/templates/sharecomponent/chartgroupcomponent/index.jsx src/templates/sharecomponent/columncomponent/columnform/index.jsx src/templates/sharecomponent/columncomponent/columnform/index.scss src/templates/sharecomponent/columncomponent/index.jsx src/templates/sharecomponent/columncomponent/markcolumn/markform/index.jsx src/templates/sharecomponent/fieldscomponent/index.jsx src/templates/sharecomponent/searchcomponent/index.jsx src/templates/sharecomponent/settingcalcomponent/index.jsx src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.scss src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx src/templates/sharecomponent/settingcomponent/index.jsx src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx src/templates/sharecomponent/settingcomponent/settingform/index.jsx src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx src/templates/sharecomponent/settingcomponent/settingform/utils.jsx src/templates/sharecomponent/tablecomponent/index.jsx src/templates/sharecomponent/tabscomponent/index.jsx src/templates/sharecomponent/treesettingcomponent/index.jsx src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx src/templates/subtableconfig/index.jsx src/templates/subtableconfig/source.jsx src/templates/treepageconfig/index.jsx src/templates/treepageconfig/source.jsx src/templates/zshare/createinterface/index.jsx src/templates/zshare/customscript/index.jsx src/templates/zshare/editTable/index.jsx src/templates/zshare/editcomponent/index.jsx src/templates/zshare/formconfig.jsx src/templates/zshare/modalform/index.jsx src/templates/zshare/verifycard/callbackcustomscript/index.jsx src/templates/zshare/verifycard/customscript/index.jsx src/templates/zshare/verifycard/index.jsx src/utils/devutils.js (deleted) src/utils/utils-custom.js src/utils/utils-datamanage.js src/utils/utils-update.js src/utils/utils.js src/views/appmanage/index.jsx src/views/appmanage/index.scss src/views/appmanage/mutilform/index.jsx src/views/appmanage/mutilform/index.scss src/views/appmanage/submutilform/index.jsx src/views/appmanage/submutilform/index.scss src/views/billprint/index.jsx src/views/design/header/index.jsx src/views/design/header/index.scss src/views/design/index.jsx src/views/design/index.scss src/views/design/sidemenu/config.jsx src/views/design/sidemenu/index.jsx src/views/design/sidemenu/index.scss src/views/login/index.jsx src/views/login/logincloudform.jsx src/views/login/loginform.jsx src/views/main/index.jsx src/views/menudesign/index.jsx src/views/menudesign/menuform/index.jsx src/views/menudesign/menuform/index.scss src/views/mobdesign/index.jsx src/views/mobdesign/index.scss src/views/mobdesign/menuform/index.jsx src/views/mobdesign/menuform/index.scss src/views/mobmanage/index.jsx (deleted) src/views/mobmanage/index.scss (deleted) src/views/pcdesign/index.jsx src/views/pcdesign/index.scss src/views/pcdesign/menuform/index.jsx src/views/pcdesign/menuform/index.scss src/views/sso/index.jsx