king
2021-07-28 137fb8ea6af2789b3238b22bac31d80bced41dfe
2021-07-28
7 文件已重命名
299个文件已修改
14 文件已复制
157个文件已添加
11个文件已删除
41447 ■■■■ 已修改文件
config/env.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/modules.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/paths.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/pnpTs.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/webpack.config.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/webpackDevServer.config.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
package-lock.json 706 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
package.json 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/admin.html 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
public/options.json 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/cacheutils.js 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/index.js 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/main.scss 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/css/viewstyle.scss 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/rolemanage.jpg 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/dashboard.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/login.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/menubar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/navtop-mob.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/nest.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/ratioboard.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/scatter.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/mobimg/tree.png 补丁 | 查看 | 原始文档 | blame | 历史
src/components/editor/index.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/header/index.jsx 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/mkIcon/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/mkIcon/index.scss 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/paste/index.jsx 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/paste/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/components/tabview/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/index.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/locales/zh-CN/model.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/bgcontroller/index.jsx 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/index.jsx 237 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/index.scss 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/wrapsetting/settingform/index.jsx 415 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/balcony/wrapsetting/settingform/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/action.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/card.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/dragaction/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/elementform/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/formconfig.jsx 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.jsx 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcellcomponent/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/index.jsx 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/pastecomponent/index.jsx 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/pastecomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/settingform/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/cardcomponent/settingform/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.jsx 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/index.scss 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/prop-card/index.jsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/cardcomponent/index.jsx 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/cardcomponent/settingform/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/card/table-card/index.jsx 64 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/cardcomponent/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/index.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/carousel/prop-card/index.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx 342 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/index.jsx 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/chartcompile/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.jsx 658 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-bar/index.scss 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx 233 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/chartcompile/index.jsx 333 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/chartcompile/index.scss 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/index.jsx 538 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-dashboard/index.scss 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx 77 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/chartcompile/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.jsx 384 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-pie/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/chartcompile/index.jsx 271 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/chartcompile/index.scss 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/index.jsx 404 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/chart/antv-scatter/index.scss 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/code/sandbox/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/actionform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/formconfig.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.jsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/formaction/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/groupform/index.jsx 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/normal-form/index.jsx 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.jsx 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/form/wrapsetting/settingform/index.scss 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupcomponents/card.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupcomponents/index.jsx 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupcomponents/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/groupsetting/settingform/index.jsx 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.jsx 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/group/normal-group/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/card.jsx 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/dragsearch/index.jsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.jsx 43 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/index.scss 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/actionform/index.jsx 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/dragaction/card.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/formconfig.jsx 115 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.jsx 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/actioncomponent/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/logcomponent/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/markcomponent/index.jsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/mobPagination/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/mobPagination/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalheader/index.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/normalheader/index.scss 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/pastecomponent/index.jsx 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/dategroup/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/dategroup/index.scss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/dragsearch/card.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/dragsearch/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/searchcomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/sourcecomponent/index.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/sourcecomponent/inputform/index.jsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/styleInput/index.jsx 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/styleInput/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/share/usercomponent/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx 72 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/editColumn/index.jsx 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/index.jsx 84 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/columns/index.scss 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/index.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/wrapsetting/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/table/normal-table/wrapsetting/settingform/index.jsx 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/dragabletabs.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.jsx 86 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/antv-tabs/index.scss 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/card.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.jsx 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabcomponents/index.scss 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tablabelform/index.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tabs/tabsetting/settingform/index.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.jsx 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/index.scss 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/wrapsetting/settingform/index.jsx 262 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/components/tree/antd-tree/wrapsetting/settingform/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/index.jsx 164 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/customscript/index.jsx 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/index.jsx 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.jsx 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/settingform/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/datasource/verifycard/utils.jsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/card.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/menushell/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/controller.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.jsx 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modalconfig/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/modulesource/option.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/pastecontroller/index.jsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/picturecontroller/editform/index.jsx 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/popview/index.jsx 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/popview/index.scss 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/replaceField/index.jsx 381 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/replaceField/index.scss 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/replaceField/settingform/index.jsx 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/replaceField/settingform/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecombcontrolbutton/index.jsx 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecombcontrolbutton/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/stylecontroller/index.jsx 83 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/menu/sysinterface/settingform/baseform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/colorsketch/index.jsx 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/colorsketch/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/card.jsx 200 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/index.jsx 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/formdragelement/index.scss 245 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-1/index.jsx 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-1/index.scss 192 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-2/index.jsx 423 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/login/mob-login-2/index.scss 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/index.jsx 265 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/index.scss 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/index.jsx 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.jsx 302 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.scss 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/wrapsetting/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.jsx 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/index.jsx 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/index.scss 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/index.jsx 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx 198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.scss 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/index.jsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx 118 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.jsx 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/index.scss 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/header/index.jsx 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/header/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/home/index.jsx 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/home/index.scss 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/card.jsx 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/index.jsx 65 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/mobshell/index.scss 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/controller.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/index.jsx 466 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/index.scss 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/pastecomponent/index.jsx 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/pastecomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modalconfig/source.jsx 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/modulesource/option.jsx 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/controller.jsx 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/card.jsx 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/index.jsx 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupdragelement/index.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupform/index.jsx 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/groupform/index.scss 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/index.jsx 574 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/index.scss 365 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/pastecomponent/index.jsx 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/pastecomponent/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/card.jsx 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/index.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/searchdragelement/index.scss 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/settingform/index.jsx 222 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/settingform/index.scss 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/mob/searchconfig/source.jsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/bgcontroller/index.jsx 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/normal-login/index.jsx 170 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/normal-login/index.scss 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/normal-login/loginform.jsx 139 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/wrapsetting/index.jsx 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/wrapsetting/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/wrapsetting/settingform/index.jsx 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/login/wrapsetting/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx 26 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx 62 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/createview/index.jsx 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/createview/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/createview/settingform/index.jsx 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/createview/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/card.jsx 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/menushell/index.jsx 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/modulesource/option.jsx 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/quotecomponent/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/transfer/index.jsx 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/transfer/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/transfer/settingform/index.jsx 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pc/transfer/settingform/index.scss 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/router/index.js 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/calendar/index.jsx 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/commontable/index.jsx 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/balcony/index.jsx 301 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/balcony/index.scss 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.jsx 200 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/cardcellList/index.scss 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.jsx 144 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/data-card/index.scss 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.jsx 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/prop-card/index.scss 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/card/table-card/index.jsx 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/data-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.jsx 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/carousel/prop-card/index.scss 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.jsx 1061 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-bar-line/index.scss 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-dashboard/index.jsx 618 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-dashboard/index.scss 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.jsx 525 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-pie/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-scatter/index.jsx 432 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/chart/antv-scatter/index.scss 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/code/sand-box/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/editor/braft-editor/index.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/normal-form/index.jsx 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/form/normal-form/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/group/normal-group/index.jsx 211 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.jsx 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalTable/index.scss 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalheader/index.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/normalheader/index.scss 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/share/tabtransfer/index.jsx 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.jsx 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/table/normal-table/index.scss 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tabs/antv-tabs/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.jsx 452 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/tree/antd-tree/index.scss 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.jsx 366 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/formtab/formgroup/index.jsx 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/home/index.jsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/scriptmanage/config.jsx 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/scriptmanage/index.jsx 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtable/index.jsx 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/subtabtable/index.jsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/tabmanage/index.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/treepage/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/verupmanage/config.jsx 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/asyncButtonComponent.jsx 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/changeuserbutton/index.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/excelInbutton/index.jsx 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/exceloutbutton/index.jsx 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/newpagebutton/index.jsx 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/normalbutton/index.jsx 279 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/popupbutton/index.jsx 60 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/printbutton/index.jsx 119 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/actionList/tabbutton/index.jsx 49 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/dategroup/index.jsx 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.jsx 208 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/fileupload/index.scss 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/imgScale/index.jsx 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/imgScale/index.scss 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/checkCard/index.jsx 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/checkCard/index.scss 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/customSwitch/index.jsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/customTextArea/index.jsx 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/index.jsx 1605 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkCheckCard/index.jsx 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkCheckCard/index.scss 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkCheckbox/index.jsx 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkCheckbox/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkColor/index.jsx 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkColor/index.scss 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkDatePicker/index.jsx 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkDatePicker/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkInput/index.jsx 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkInput/index.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkNumberInput/index.jsx 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkNumberInput/index.scss 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkRadio/index.jsx 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkRadio/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkSelect/index.jsx 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkSelect/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkSwitch/index.jsx 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkSwitch/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkTextArea/index.jsx 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/mutilform/mkTextArea/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/normalTable/index.jsx 55 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/advanceform/index.jsx 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/advanceform/index.scss 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/index.jsx 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/quarterpicker/index.jsx 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/quarterpicker/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/yearpicker/index.jsx 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/dategroup/yearpicker/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/index.jsx 765 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/index.scss 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/mkDatePicker/index.jsx 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/mkDatePicker/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/mkSelect/index.jsx 121 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/zshare/topSearch/mkSelect/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/calendarconfig/source.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/comtableconfig/source.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/index.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/formtabconfig/settingform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editfirstmenu/index.jsx 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editsecmenu/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/menuconfig/editthdmenu/index.jsx 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/checkCard/index.jsx 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/checkCard/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/card.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/dragelement/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.jsx 90 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/modalconfig/settingform/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/actionform/index.jsx 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/columnform/index.jsx 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/columncomponent/columnform/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/fieldscomponent/index.jsx 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/index.jsx 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/index.scss 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/searcheditable/index.jsx 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/searchform/index.jsx 296 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/searchcomponent/searchform/index.scss 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx 23 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.jsx 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcalcomponent/verifycard/utils.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/index.jsx 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/datasource/index.jsx 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/datasource/index.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/index.jsx 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/simplescript/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/settingcomponent/settingform/utils.jsx 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/tablecomponent/index.jsx 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/datasource/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/sharecomponent/treesettingcomponent/settingform/utils.jsx 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/index.jsx 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/subtableconfig/source.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/treepageconfig/index.jsx 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/basetransferform/index.jsx 4 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/customscript/index.jsx 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editTable/index.jsx 61 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editTable/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/editcomponent/index.jsx 82 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/formconfig.jsx 717 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/datatable/index.jsx 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/datatable/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/index.jsx 105 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/index.scss 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/modalform/modaleditable/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/callbackcustomscript/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/customform/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/customscript/index.jsx 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/templates/zshare/verifycard/index.jsx 47 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/option.js 164 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-custom.js 177 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils-datamanage.js 179 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/utils/utils.js 406 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/header/index.jsx 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/header/index.scss 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.jsx 913 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/index.scss 132 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/scriptform/index.jsx 242 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/scriptform/index.scss 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.jsx 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/submutilform/index.scss 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/transform/index.jsx 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/appmanage/transform/index.scss 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.jsx 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/billprint/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/header/index.jsx 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/header/index.scss 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/index.jsx 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/design/sidemenu/config.jsx 20 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/api/index.js 175 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/header/index.jsx 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/header/index.scss 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/history/index.jsx 290 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/history/index.scss 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/index.jsx 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/index.scss 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/editTable/index.jsx 249 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/editTable/index.scss 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/index.jsx 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/index.scss 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/request/index.jsx 310 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/interface/workspace/request/index.scss 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/index.scss 109 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/login/loginform.jsx 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/homeform/index.jsx 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.jsx 113 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/printmenuform/index.jsx 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/menudesign/printmenuform/index.scss 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.jsx 706 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/index.scss 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobdesign/menuform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.jsx 540 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/index.scss 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/pcdesign/menuform/index.jsx 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/printTemplate/index.jsx 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/printTemplate/mutilform/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/printTemplate/mutilform/index.scss 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/printTemplate/option.js 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/printTemplate/print.js 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/header/index.jsx 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/header/index.scss 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/index.jsx 813 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/rolemanage/index.scss 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
config/env.js
@@ -1,4 +1,4 @@
'use strict';
// 'use strict';
const fs = require('fs');
const path = require('path');
config/modules.js
@@ -1,4 +1,4 @@
'use strict';
// 'use strict';
const fs = require('fs');
const path = require('path');
config/paths.js
@@ -1,4 +1,4 @@
'use strict';
// 'use strict';
const path = require('path');
const fs = require('fs');
config/pnpTs.js
@@ -1,4 +1,4 @@
'use strict';
// 'use strict';
const { resolveModuleName } = require('ts-pnp');
config/webpack.config.js
@@ -173,14 +173,16 @@
      pathinfo: isEnvDevelopment,
      // There will be one main bundle, and one file per asynchronous chunk.
      // In development, it does not produce real files.
      // contenthash => hash
      filename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].js'
        ? 'static/js/[name].[hash:8].js'
        : isEnvDevelopment && 'static/js/bundle.js',
      // TODO: remove this when upgrading to webpack 5
      futureEmitAssets: true,
      // There are also additional JS chunk files if you use code splitting.
      // contenthash => hash
      chunkFilename: isEnvProduction
        ? 'static/js/[name].[contenthash:8].chunk.js'
        ? 'static/js/[name].[hash:8].chunk.js'
        : isEnvDevelopment && 'static/js/[name].chunk.js',
      // We inferred the "public path" (such as / or /my-project) from homepage.
      // We use "/" in development.
@@ -565,8 +567,9 @@
        new MiniCssExtractPlugin({
          // Options similar to the same options in webpackOptions.output
          // both options are optional
          filename: 'static/css/[name].[contenthash:8].css',
          chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
          // contenthash => hash
          filename: 'static/css/[name].[hash:8].css',
          chunkFilename: 'static/css/[name].[hash:8].chunk.css',
        }),
      // Generate a manifest file which contains a mapping of all asset filenames
      // to their corresponding output file so that tools can pick it up without
config/webpackDevServer.config.js
@@ -1,4 +1,4 @@
'use strict';
// 'use strict';
const errorOverlayMiddleware = require('react-dev-utils/errorOverlayMiddleware');
const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
package-lock.json
@@ -60,47 +60,74 @@
      }
    },
    "@antv/color-util": {
      "version": "2.0.5",
      "resolved": "https://registry.npmjs.org/@antv/color-util/-/color-util-2.0.5.tgz",
      "integrity": "sha512-eQktA9YDnWCj03rfjpg0ajvCfRkHXzBzsZa9z94pY6Jb7e3XtPUp7vDpB8KhaKm9GjPtGzQDneh+gnqkEK8mtQ==",
      "version": "2.0.6",
      "resolved": "https://registry.npmjs.org/@antv/color-util/-/color-util-2.0.6.tgz",
      "integrity": "sha512-KnPEaAH+XNJMjax9U35W67nzPI+QQ2x27pYlzmSIWrbj4/k8PGrARXfzDTjwoozHJY8qG62Z+Ww6Alhu2FctXQ==",
      "requires": {
        "@antv/util": "^2.0.9",
        "tslib": "^1.10.0"
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "@antv/util": {
          "version": "2.0.9",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.9.tgz",
          "integrity": "sha512-JblWzne7msAPDdxkUhEk8zAz0Wd6igKwqymGbvIeyOydGrhBhGjA3nEayFj4IlG+XixCvGFKsCB4yuFS4glRIA==",
          "version": "2.0.13",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.13.tgz",
          "integrity": "sha512-mfYL7K8XJIeDmal33K+6abr8Yb526YXKg5XQlddNo+X1Doll+gun6HxnbdySoLv21vW4bLkcbVPjqxWl7ZJAFA==",
          "requires": {
            "tslib": "^1.10.0"
            "tslib": "^2.0.3"
          }
        },
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/component": {
      "version": "0.8.2",
      "resolved": "https://registry.npmjs.org/@antv/component/-/component-0.8.2.tgz",
      "integrity": "sha512-aU65T9jD85H+3WhhmgPz8kiuqxJGPuHFgRiBYmR+vQUnAA2nOW2bCafiNx/bSEAqmVgsr+8e+9IDQZ6PowIgtQ==",
      "version": "0.8.11",
      "resolved": "https://registry.npmjs.org/@antv/component/-/component-0.8.11.tgz",
      "integrity": "sha512-7hl7IPPF/QmE25lrZhppEW8BYJzU3SE6IGq4/NhrvcDk79Xhm1L4KOcAAcZ0zYBRaOwQL3yMcraIL+/Z1UhLjw==",
      "requires": {
        "@antv/dom-util": "~2.0.1",
        "@antv/g-base": "~0.5.0",
        "@antv/g-base": "0.5.6",
        "@antv/matrix-util": "^3.1.0-beta.1",
        "@antv/path-util": "~2.0.7",
        "@antv/scale": "~0.3.1",
        "@antv/util": "~2.0.0",
        "fecha": "~4.2.0",
        "tslib": "^1.10.0"
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/coord": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/@antv/coord/-/coord-0.3.0.tgz",
      "integrity": "sha512-lm5Ct+r62mFVqhziKoDFN3PQjnkNBMOoOG+kBsPq3M3GqlQt5Jc7euOHMFcYSZM9HJmsKkGcih6EWDVVliMEZg==",
      "version": "0.3.1",
      "resolved": "https://registry.npmjs.org/@antv/coord/-/coord-0.3.1.tgz",
      "integrity": "sha512-rFE94C8Xzbx4xmZnHh2AnlB3Qm1n5x0VT3OROy257IH6Rm4cuzv1+tZaUBATviwZd99S+rOY9telw/+6C9GbRw==",
      "requires": {
        "@antv/matrix-util": "^3.1.0-beta.2",
        "@antv/util": "~2.0.3",
        "tslib": "^1.10.0"
        "@antv/util": "~2.0.12",
        "tslib": "^2.1.0"
      },
      "dependencies": {
        "@antv/util": {
          "version": "2.0.13",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.13.tgz",
          "integrity": "sha512-mfYL7K8XJIeDmal33K+6abr8Yb526YXKg5XQlddNo+X1Doll+gun6HxnbdySoLv21vW4bLkcbVPjqxWl7ZJAFA==",
          "requires": {
            "tslib": "^2.0.3"
          }
        },
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/data-set": {
@@ -127,11 +154,18 @@
      }
    },
    "@antv/dom-util": {
      "version": "2.0.2",
      "resolved": "https://registry.npmjs.org/@antv/dom-util/-/dom-util-2.0.2.tgz",
      "integrity": "sha512-i/rh385casRd6OA4rbpbA2jyiwY/w7PtkA+74mH43PiBzOcLpDg1jEWR9dMO0tIqUzHQWWqKNNjmXVubl4GS6g==",
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/@antv/dom-util/-/dom-util-2.0.3.tgz",
      "integrity": "sha512-dUHsUT4U9X1T1/Y9bH3jRMe0MzvWJk2jSQm1vm3w9NX+Ra0ftq5VUBiGTNbthm3nFwG0fFFjip904rYjl50g4A==",
      "requires": {
        "tslib": "^1.10.0"
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/event-emitter": {
@@ -140,81 +174,105 @@
      "integrity": "sha512-6C6NJOdoNVptCr5y9BVOhKkCgW7LFs/SpcRyAExUeSjAm0zJqcqNkSIRGsXYhj4PJI+CZICHzGwwiSnIsE68Ug=="
    },
    "@antv/g-base": {
      "version": "0.5.1",
      "resolved": "https://registry.npmjs.org/@antv/g-base/-/g-base-0.5.1.tgz",
      "integrity": "sha512-gbv+uz/SvzM4/p3PLjAiEZUp6kdzKkCbVWTCdBXB1cvNMttlEzWEB8MOFbEkcIAy7TtjQJJRv8ThI/ngFzU+fg==",
      "version": "0.5.6",
      "resolved": "https://registry.npmjs.org/@antv/g-base/-/g-base-0.5.6.tgz",
      "integrity": "sha512-szxqFQ/xdCnfaeSEEC2kVjXdKxJnvKKJNT0MvaOG3UXOfsjPDLgb3IKLr+bU3sLvTAQfPhsbtYh7mWb03+mGjA==",
      "requires": {
        "@antv/event-emitter": "^0.1.1",
        "@antv/g-math": "^0.1.5",
        "@antv/g-math": "^0.1.6",
        "@antv/matrix-util": "^3.1.0-beta.1",
        "@antv/path-util": "~2.0.5",
        "@antv/util": "~2.0.0",
        "@types/d3-timer": "^1.0.9",
        "@types/d3-timer": "^2.0.0",
        "d3-ease": "^1.0.5",
        "d3-interpolate": "^1.3.2",
        "d3-timer": "^1.0.9",
        "detect-browser": "^5.1.0"
        "detect-browser": "^5.1.0",
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/g-canvas": {
      "version": "0.5.3",
      "resolved": "https://registry.npmjs.org/@antv/g-canvas/-/g-canvas-0.5.3.tgz",
      "integrity": "sha512-80k1BbiY05heHKUm4o6IL6KVRZS+uAgzdIF2OaC9grQc6KxrJoK2dCxKpmna3NBHTU9Sm+/rsiGcL7lp7S+ecQ==",
      "version": "0.5.6",
      "resolved": "https://registry.npmjs.org/@antv/g-canvas/-/g-canvas-0.5.6.tgz",
      "integrity": "sha512-r6f6KqYuNZWunf2Vnf1ACopn8aic6TBFhaN3XuckenhQNqR+/PI/4Mft7z14zo7WH58X2Xs2Npq9awJXBEUzaw==",
      "requires": {
        "@antv/g-base": "^0.5.1",
        "@antv/g-math": "^0.1.5",
        "@antv/g-base": "^0.5.3",
        "@antv/g-math": "^0.1.6",
        "@antv/matrix-util": "^3.1.0-beta.1",
        "@antv/path-util": "~2.0.5",
        "@antv/util": "~2.0.0",
        "gl-matrix": "^3.0.0"
        "gl-matrix": "^3.0.0",
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/g-math": {
      "version": "0.1.5",
      "resolved": "https://registry.npmjs.org/@antv/g-math/-/g-math-0.1.5.tgz",
      "integrity": "sha512-29B3p33jzUPIyx1xYfNCexbp7jvahD6bD8FKKyWqfYZHQbvrFfV8ZNUu66RJLfDrl0KaKT6C5whfKs/WrVaflQ==",
      "version": "0.1.7",
      "resolved": "https://registry.npmjs.org/@antv/g-math/-/g-math-0.1.7.tgz",
      "integrity": "sha512-xGyXaloD1ynfp7gS4VuV+MjSptZIwHvLHr8ekXJSFAeWPYLu84yOW2wOZHDdp1bzDAIuRv6xDBW58YGHrWsFcA==",
      "requires": {
        "@antv/util": "~2.0.0",
        "gl-matrix": "^3.0.0"
      }
    },
    "@antv/g-svg": {
      "version": "0.5.2",
      "resolved": "https://registry.npmjs.org/@antv/g-svg/-/g-svg-0.5.2.tgz",
      "integrity": "sha512-T0PYjIM+WX6zv3yUgSkyRcHFq4hlio0MeRGoJR60P5U5MNSdkZnblcu79cpU2i42Z7wBr404Kv1dplCGxC38PA==",
      "version": "0.5.6",
      "resolved": "https://registry.npmjs.org/@antv/g-svg/-/g-svg-0.5.6.tgz",
      "integrity": "sha512-Xve1EUGk4HMbl2nq4ozR4QLh6GyoZ8Xw/+9kHYI4B5P2lIUQU95MuRsaLFfW5NNpZDx85ZeH97tqEmC9L96E7A==",
      "requires": {
        "@antv/g-base": "^0.5.1",
        "@antv/g-math": "^0.1.5",
        "@antv/g-base": "^0.5.3",
        "@antv/g-math": "^0.1.6",
        "@antv/util": "~2.0.0",
        "detect-browser": "^5.0.0"
        "detect-browser": "^5.0.0",
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/g2": {
      "version": "4.1.0-beta.18",
      "resolved": "https://registry.npmjs.org/@antv/g2/-/g2-4.1.0-beta.18.tgz",
      "integrity": "sha512-RW3e95V2aUtys36guS7PNHbfgyYZgigu18OLSYPkgyRLF0pPABcSrIot+xnVGQ4Cx0ZDmhJVTvpXjU4QMa96kw==",
      "version": "4.1.14",
      "resolved": "https://registry.npmjs.org/@antv/g2/-/g2-4.1.14.tgz",
      "integrity": "sha512-JdU+0suxkDqjTZNyG915zQ7eM+bdT2fHV48VGc1LvCroUHzCncCnbx5HZ19aZhvuij2hM6eh52vb8mwIXIzMBw==",
      "requires": {
        "@antv/adjust": "^0.2.1",
        "@antv/attr": "^0.3.1",
        "@antv/color-util": "^2.0.2",
        "@antv/component": "^0.8.0",
        "@antv/component": "^0.8.7",
        "@antv/coord": "^0.3.0",
        "@antv/dom-util": "^2.0.2",
        "@antv/event-emitter": "~0.1.0",
        "@antv/g-base": "^0.5.0",
        "@antv/g-canvas": "^0.5.0",
        "@antv/g-svg": "^0.5.0",
        "@antv/g-base": "0.5.6",
        "@antv/g-canvas": "0.5.6",
        "@antv/g-svg": "0.5.6",
        "@antv/matrix-util": "^3.1.0-beta.1",
        "@antv/path-util": "^2.0.3",
        "@antv/scale": "^0.3.1",
        "@antv/scale": "^0.3.7",
        "@antv/util": "~2.0.5",
        "tslib": "^2.0.0"
      },
      "dependencies": {
        "tslib": {
          "version": "2.0.3",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
          "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
@@ -237,38 +295,50 @@
      },
      "dependencies": {
        "@antv/util": {
          "version": "2.0.9",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.9.tgz",
          "integrity": "sha512-JblWzne7msAPDdxkUhEk8zAz0Wd6igKwqymGbvIeyOydGrhBhGjA3nEayFj4IlG+XixCvGFKsCB4yuFS4glRIA==",
          "version": "2.0.13",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.13.tgz",
          "integrity": "sha512-mfYL7K8XJIeDmal33K+6abr8Yb526YXKg5XQlddNo+X1Doll+gun6HxnbdySoLv21vW4bLkcbVPjqxWl7ZJAFA==",
          "requires": {
            "tslib": "^1.10.0"
            "tslib": "^2.0.3"
          },
          "dependencies": {
            "tslib": {
              "version": "2.2.0",
              "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
              "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
            }
          }
        }
      }
    },
    "@antv/path-util": {
      "version": "2.0.8",
      "resolved": "https://registry.npmjs.org/@antv/path-util/-/path-util-2.0.8.tgz",
      "integrity": "sha512-g5gt12MY1oEzh/j5XfLNRCfJU7E+Us+2yM5Hqc0y8xeWhb5l013XG5BPV37KmOf6WAys9KNxklNniHCZ6SqCKw==",
      "version": "2.0.9",
      "resolved": "https://registry.npmjs.org/@antv/path-util/-/path-util-2.0.9.tgz",
      "integrity": "sha512-kunEz4dNheQMVn4rVFsoBDx+n9Knfi3uRLvDk9SojZAqpninsjFhdoiYtbExwJGz1FYGtiV10Y6N1tp73kZFcg==",
      "requires": {
        "@antv/util": "^2.0.9",
        "tslib": "^1.10.0"
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "@antv/util": {
          "version": "2.0.9",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.9.tgz",
          "integrity": "sha512-JblWzne7msAPDdxkUhEk8zAz0Wd6igKwqymGbvIeyOydGrhBhGjA3nEayFj4IlG+XixCvGFKsCB4yuFS4glRIA==",
          "version": "2.0.13",
          "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.13.tgz",
          "integrity": "sha512-mfYL7K8XJIeDmal33K+6abr8Yb526YXKg5XQlddNo+X1Doll+gun6HxnbdySoLv21vW4bLkcbVPjqxWl7ZJAFA==",
          "requires": {
            "tslib": "^1.10.0"
            "tslib": "^2.0.3"
          }
        },
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/scale": {
      "version": "0.3.4",
      "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.3.4.tgz",
      "integrity": "sha512-mZaf3MtMoQJtd+KAgTANVbvKpaXVpzLAFbKO/wSKaLFpnWO0bwMVDYzrs+5DZhfEfMbZpP/A7qUZ2/DLh0T3Tg==",
      "version": "0.3.9",
      "resolved": "https://registry.npmjs.org/@antv/scale/-/scale-0.3.9.tgz",
      "integrity": "sha512-m56Rs4Zta7XnuF+aNbJXMsgBqATO1M4kvu+dEmhzLYsPK5D3dHpJTOGh/Zy55eirekb9F7jiu29SJqPNnhxq+g==",
      "requires": {
        "@antv/util": "~2.0.3",
        "fecha": "~4.2.0",
@@ -276,18 +346,25 @@
      },
      "dependencies": {
        "tslib": {
          "version": "2.0.3",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
          "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@antv/util": {
      "version": "2.0.8",
      "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.8.tgz",
      "integrity": "sha512-G9QRygQJ8UNGMi8L1dfMIa4SofbEO+jkXwvRY4ek/MLd04Q01UN0U28JeMFzw6FCKJdxiFu+2uwT/zjoFr3QoQ==",
      "version": "2.0.13",
      "resolved": "https://registry.npmjs.org/@antv/util/-/util-2.0.13.tgz",
      "integrity": "sha512-mfYL7K8XJIeDmal33K+6abr8Yb526YXKg5XQlddNo+X1Doll+gun6HxnbdySoLv21vW4bLkcbVPjqxWl7ZJAFA==",
      "requires": {
        "tslib": "^1.10.0"
        "tslib": "^2.0.3"
      },
      "dependencies": {
        "tslib": {
          "version": "2.2.0",
          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.2.0.tgz",
          "integrity": "sha512-gS9GVHRU+RGn5KQM2rllAlR3dU6m7AcpJKdtH8gFvQiC4Otgk98XnmMU+nZenHt/+VhnBPWwgrJsyrdcw6i23w=="
        }
      }
    },
    "@babel/code-frame": {
@@ -1464,6 +1541,33 @@
      "resolved": "https://registry.npmjs.org/@csstools/normalize.css/-/normalize.css-9.0.1.tgz",
      "integrity": "sha512-6It2EVfGskxZCQhuykrfnALg7oVeiI6KclWSmGDqB0AiInVrTGB9Jp9i4/Ad21u9Jde/voVQz6eFX/eSg/UsPA=="
    },
    "@fast-csv/format": {
      "version": "4.3.5",
      "resolved": "https://registry.npmjs.org/@fast-csv/format/-/format-4.3.5.tgz",
      "integrity": "sha512-8iRn6QF3I8Ak78lNAa+Gdl5MJJBM5vRHivFtMRUWINdevNo00K7OXxS2PshawLKTejVwieIlPmK5YlLu6w4u8A==",
      "requires": {
        "@types/node": "^14.0.1",
        "lodash.escaperegexp": "^4.1.2",
        "lodash.isboolean": "^3.0.3",
        "lodash.isequal": "^4.5.0",
        "lodash.isfunction": "^3.0.9",
        "lodash.isnil": "^4.0.0"
      }
    },
    "@fast-csv/parse": {
      "version": "4.3.6",
      "resolved": "https://registry.npmjs.org/@fast-csv/parse/-/parse-4.3.6.tgz",
      "integrity": "sha512-uRsLYksqpbDmWaSmzvJcuApSEe38+6NQZBUsuAyMZKqHxH0g1wcJgsKUvN3WC8tewaqFjBMMGrkHmC+T7k8LvA==",
      "requires": {
        "@types/node": "^14.0.1",
        "lodash.escaperegexp": "^4.1.2",
        "lodash.groupby": "^4.6.0",
        "lodash.isfunction": "^3.0.9",
        "lodash.isnil": "^4.0.0",
        "lodash.isundefined": "^3.0.1",
        "lodash.uniq": "^4.5.0"
      }
    },
    "@hapi/address": {
      "version": "2.1.1",
      "resolved": "https://registry.npmjs.org/@hapi/address/-/address-2.1.1.tgz",
@@ -2376,9 +2480,9 @@
      "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ=="
    },
    "@types/d3-timer": {
      "version": "1.0.10",
      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-1.0.10.tgz",
      "integrity": "sha512-ZnAbquVqy+4ZjdW0cY6URp+qF/AzTVNda2jYyOzpR2cPT35FTXl78s15Bomph9+ckOiI1TtkljnWkwbIGAb6rg=="
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-2.0.0.tgz",
      "integrity": "sha512-l6stHr1VD1BWlW6u3pxrjLtJfpPZq9I3XmKIQtq7zHM/s6fwEtI1Yn6Sr5/jQTrUDCC5jkS6gWqlFGCDArDqNg=="
    },
    "@types/eslint-visitor-keys": {
      "version": "1.0.0",
@@ -2425,6 +2529,11 @@
      "version": "7.0.3",
      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
      "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A=="
    },
    "@types/node": {
      "version": "14.14.44",
      "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.44.tgz",
      "integrity": "sha512-+gaugz6Oce6ZInfI/tK4Pq5wIIkJMEJUu92RB3Eu93mtj4wjjjz9EB5mLp5s1pSsLXdC/CPut/xF20ZzAQJbTA=="
    },
    "@types/prop-types": {
      "version": "15.7.2",
@@ -3151,6 +3260,61 @@
      "version": "1.2.0",
      "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
      "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
    },
    "archiver": {
      "version": "5.3.0",
      "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.0.tgz",
      "integrity": "sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==",
      "requires": {
        "archiver-utils": "^2.1.0",
        "async": "^3.2.0",
        "buffer-crc32": "^0.2.1",
        "readable-stream": "^3.6.0",
        "readdir-glob": "^1.0.0",
        "tar-stream": "^2.2.0",
        "zip-stream": "^4.1.0"
      },
      "dependencies": {
        "async": {
          "version": "3.2.0",
          "resolved": "https://registry.npmjs.org/async/-/async-3.2.0.tgz",
          "integrity": "sha512-TR2mEZFVOj2pLStYxLht7TyfuRzaydfpxr3k9RpHIzMgw7A64dzsdqCxH1WJyQdoe8T10nDXd9wnEigmiuHIZw=="
        },
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "archiver-utils": {
      "version": "2.1.0",
      "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
      "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
      "requires": {
        "glob": "^7.1.4",
        "graceful-fs": "^4.2.0",
        "lazystream": "^1.0.0",
        "lodash.defaults": "^4.2.0",
        "lodash.difference": "^4.5.0",
        "lodash.flatten": "^4.4.0",
        "lodash.isplainobject": "^4.0.6",
        "lodash.union": "^4.6.0",
        "normalize-path": "^3.0.0",
        "readable-stream": "^2.0.0"
      },
      "dependencies": {
        "normalize-path": {
          "version": "3.0.0",
          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
        }
      }
    },
    "are-we-there-yet": {
      "version": "1.1.5",
@@ -4033,10 +4197,50 @@
      "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
      "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
    },
    "binary": {
      "version": "0.3.0",
      "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
      "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
      "requires": {
        "buffers": "~0.1.1",
        "chainsaw": "~0.1.0"
      }
    },
    "binary-extensions": {
      "version": "1.13.1",
      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz",
      "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw=="
    },
    "bl": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
      "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
      "requires": {
        "buffer": "^5.5.0",
        "inherits": "^2.0.4",
        "readable-stream": "^3.4.0"
      },
      "dependencies": {
        "buffer": {
          "version": "5.7.1",
          "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
          "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
          "requires": {
            "base64-js": "^1.3.1",
            "ieee754": "^1.1.13"
          }
        },
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "block-stream": {
      "version": "0.0.9",
@@ -4373,10 +4577,20 @@
      "resolved": "https://registry.npmjs.org/buffer-indexof/-/buffer-indexof-1.1.1.tgz",
      "integrity": "sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g=="
    },
    "buffer-indexof-polyfill": {
      "version": "1.0.2",
      "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz",
      "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A=="
    },
    "buffer-xor": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
      "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk="
    },
    "buffers": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
      "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s="
    },
    "builtin-status-codes": {
      "version": "3.0.0",
@@ -4550,6 +4764,21 @@
        "commander": "^2.16.0",
        "crc-32": "~1.2.0",
        "printj": "~1.1.2"
      }
    },
    "chainsaw": {
      "version": "0.1.0",
      "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
      "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
      "requires": {
        "traverse": ">=0.3.0 <0.4"
      },
      "dependencies": {
        "traverse": {
          "version": "0.3.9",
          "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz",
          "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk="
        }
      }
    },
    "chalk": {
@@ -4875,6 +5104,34 @@
        "arity-n": "^1.0.4"
      }
    },
    "compress-commons": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.0.tgz",
      "integrity": "sha512-ofaaLqfraD1YRTkrRKPCrGJ1pFeDG/MVCkVVV2FNGeWquSlqw5wOrwOfPQ1xF2u+blpeWASie5EubHz+vsNIgA==",
      "requires": {
        "buffer-crc32": "^0.2.13",
        "crc32-stream": "^4.0.1",
        "normalize-path": "^3.0.0",
        "readable-stream": "^3.6.0"
      },
      "dependencies": {
        "normalize-path": {
          "version": "3.0.0",
          "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
          "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
        },
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "compressible": {
      "version": "2.0.17",
      "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.17.tgz",
@@ -5088,6 +5345,27 @@
      "requires": {
        "exit-on-epipe": "~1.0.1",
        "printj": "~1.1.0"
      }
    },
    "crc32-stream": {
      "version": "4.0.2",
      "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.2.tgz",
      "integrity": "sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==",
      "requires": {
        "crc-32": "^1.2.0",
        "readable-stream": "^3.4.0"
      },
      "dependencies": {
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "create-ecdh": {
@@ -7166,6 +7444,14 @@
      "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
      "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
    },
    "duplexer2": {
      "version": "0.1.4",
      "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz",
      "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=",
      "requires": {
        "readable-stream": "^2.0.2"
      }
    },
    "duplexify": {
      "version": "3.7.1",
      "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
@@ -7966,6 +8252,63 @@
        "safe-buffer": "^5.1.1"
      }
    },
    "exceljs": {
      "version": "4.2.1",
      "resolved": "https://registry.npmjs.org/exceljs/-/exceljs-4.2.1.tgz",
      "integrity": "sha512-EogoTdXH1X1PxqD9sV8caYd1RIfXN3PVlCV+mA/87CgdO2h4X5xAEbr7CaiP8tffz7L4aBFwsdMbjfMXi29NjA==",
      "requires": {
        "archiver": "^5.0.0",
        "dayjs": "^1.8.34",
        "fast-csv": "^4.3.1",
        "jszip": "^3.5.0",
        "readable-stream": "^3.6.0",
        "saxes": "^5.0.1",
        "tmp": "^0.2.0",
        "unzipper": "^0.10.11",
        "uuid": "^8.3.0"
      },
      "dependencies": {
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        },
        "rimraf": {
          "version": "3.0.2",
          "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
          "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
          "requires": {
            "glob": "^7.1.3"
          }
        },
        "saxes": {
          "version": "5.0.1",
          "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz",
          "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==",
          "requires": {
            "xmlchars": "^2.2.0"
          }
        },
        "tmp": {
          "version": "0.2.1",
          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
          "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==",
          "requires": {
            "rimraf": "^3.0.0"
          }
        },
        "uuid": {
          "version": "8.3.2",
          "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
          "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
        }
      }
    },
    "exec-sh": {
      "version": "0.3.2",
      "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.2.tgz",
@@ -8301,6 +8644,15 @@
        "time-stamp": "^1.0.0"
      }
    },
    "fast-csv": {
      "version": "4.3.6",
      "resolved": "https://registry.npmjs.org/fast-csv/-/fast-csv-4.3.6.tgz",
      "integrity": "sha512-2RNSpuwwsJGP0frGsOmTb9oUF+VkFSM4SyLTDgwf2ciHWTarN0lQTC+F2f/t5J9QjW+c65VFIAAu85GsvMIusw==",
      "requires": {
        "@fast-csv/format": "4.3.5",
        "@fast-csv/parse": "4.3.6"
      }
    },
    "fast-deep-equal": {
      "version": "2.0.1",
      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
@@ -8435,9 +8787,9 @@
      }
    },
    "fecha": {
      "version": "4.2.0",
      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.0.tgz",
      "integrity": "sha512-aN3pcx/DSmtyoovUudctc8+6Hl4T+hI9GBBHLjA76jdZl7+b1sgh5g4k+u/GL3dTy1/pnYzKp69FpJ0OicE3Wg=="
      "version": "4.2.1",
      "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.1.tgz",
      "integrity": "sha512-MMMQ0ludy/nBs1/o0zVOiKTpG7qMbonKUzjJgQFEuvq6INZ1OraKPRAWkBq5vlKLOUMpmNYG1JoN3oDPUQ9m3Q=="
    },
    "figgy-pudding": {
      "version": "3.5.1",
@@ -8700,6 +9052,11 @@
        "inherits": "^2.0.1",
        "readable-stream": "^2.0.0"
      }
    },
    "fs-constants": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
      "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
    },
    "fs-extra": {
      "version": "7.0.1",
@@ -9940,6 +10297,11 @@
      "version": "0.6.3",
      "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.6.3.tgz",
      "integrity": "sha512-47xSUiQioGaB96nqtp5/q55m0aBQSQdyIloMOc/x+QVTDZLNmXE892IIDrJ0hM1A5vcNUDD5tDffkSP5lCaIIA=="
    },
    "immediate": {
      "version": "3.0.6",
      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
      "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
    },
    "immer": {
      "version": "1.10.0",
@@ -11421,6 +11783,11 @@
      "resolved": "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz",
      "integrity": "sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g=="
    },
    "js-table2excel": {
      "version": "1.0.3",
      "resolved": "https://registry.npmjs.org/js-table2excel/-/js-table2excel-1.0.3.tgz",
      "integrity": "sha512-ivzOdgYqOD3zqzJZfW0Nm35P9BWffxD0Unwr+2QBeEawV7hhRY9RHBVNcvO6A9PhGkMyqPVL/u4/NeufaTTTXw=="
    },
    "js-tokens": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -11587,6 +11954,17 @@
        "object.assign": "^4.1.0"
      }
    },
    "jszip": {
      "version": "3.6.0",
      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.6.0.tgz",
      "integrity": "sha512-jgnQoG9LKnWO3mnVNBnfhkh0QknICd1FGSrXcgrl67zioyJ4wgx25o9ZqwNtrROSflGBCGYnJfjrIyRIby1OoQ==",
      "requires": {
        "lie": "~3.3.0",
        "pako": "~1.0.2",
        "readable-stream": "~2.3.6",
        "set-immediate-shim": "~1.0.1"
      }
    },
    "killable": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -11634,6 +12012,14 @@
      "resolved": "https://registry.npmjs.org/lazy-cache/-/lazy-cache-1.0.4.tgz",
      "integrity": "sha1-odePw6UEdMuAhF07O24dpJpEbo4="
    },
    "lazystream": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz",
      "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=",
      "requires": {
        "readable-stream": "^2.0.5"
      }
    },
    "lcid": {
      "version": "1.0.0",
      "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
@@ -11660,6 +12046,19 @@
        "prelude-ls": "~1.1.2",
        "type-check": "~0.3.2"
      }
    },
    "lie": {
      "version": "3.3.0",
      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
      "requires": {
        "immediate": "~3.0.5"
      }
    },
    "listenercount": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz",
      "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc="
    },
    "load-json-file": {
      "version": "1.1.0",
@@ -11773,6 +12172,31 @@
      "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
      "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168="
    },
    "lodash.defaults": {
      "version": "4.2.0",
      "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
      "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw="
    },
    "lodash.difference": {
      "version": "4.5.0",
      "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
      "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
    },
    "lodash.escaperegexp": {
      "version": "4.1.2",
      "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz",
      "integrity": "sha1-ZHYsSGGAglGKw99Mz11YhtriA0c="
    },
    "lodash.flatten": {
      "version": "4.4.0",
      "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
      "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8="
    },
    "lodash.groupby": {
      "version": "4.6.0",
      "resolved": "https://registry.npmjs.org/lodash.groupby/-/lodash.groupby-4.6.0.tgz",
      "integrity": "sha1-Cwih3PaDl8OXhVwyOXg4Mt90A9E="
    },
    "lodash.isarguments": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
@@ -11782,6 +12206,36 @@
      "version": "3.0.4",
      "resolved": "https://registry.npmjs.org/lodash.isarray/-/lodash.isarray-3.0.4.tgz",
      "integrity": "sha1-eeTriMNqgSKvhvhEqpvNhRtfu1U="
    },
    "lodash.isboolean": {
      "version": "3.0.3",
      "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
      "integrity": "sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY="
    },
    "lodash.isequal": {
      "version": "4.5.0",
      "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
      "integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
    },
    "lodash.isfunction": {
      "version": "3.0.9",
      "resolved": "https://registry.npmjs.org/lodash.isfunction/-/lodash.isfunction-3.0.9.tgz",
      "integrity": "sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw=="
    },
    "lodash.isnil": {
      "version": "4.0.0",
      "resolved": "https://registry.npmjs.org/lodash.isnil/-/lodash.isnil-4.0.0.tgz",
      "integrity": "sha1-SeKM1VkBNFjIFMVHnTxmOiG/qmw="
    },
    "lodash.isplainobject": {
      "version": "4.0.6",
      "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
      "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs="
    },
    "lodash.isundefined": {
      "version": "3.0.1",
      "resolved": "https://registry.npmjs.org/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz",
      "integrity": "sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g="
    },
    "lodash.keys": {
      "version": "3.1.2",
@@ -11829,6 +12283,11 @@
      "version": "4.0.1",
      "resolved": "https://registry.npmjs.org/lodash.unescape/-/lodash.unescape-4.0.1.tgz",
      "integrity": "sha1-vyJJiGzlFM2hEvrpIYzcBlIR/Jw="
    },
    "lodash.union": {
      "version": "4.6.0",
      "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
      "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg="
    },
    "lodash.uniq": {
      "version": "4.5.0",
@@ -17057,6 +17516,14 @@
        }
      }
    },
    "readdir-glob": {
      "version": "1.1.1",
      "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.1.tgz",
      "integrity": "sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==",
      "requires": {
        "minimatch": "^3.0.4"
      }
    },
    "readdirp": {
      "version": "2.2.1",
      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz",
@@ -17991,6 +18458,11 @@
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
      "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
    },
    "set-immediate-shim": {
      "version": "1.0.1",
      "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
      "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E="
    },
    "set-value": {
      "version": "2.0.1",
@@ -19022,6 +19494,30 @@
        "inherits": "2"
      }
    },
    "tar-stream": {
      "version": "2.2.0",
      "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
      "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
      "requires": {
        "bl": "^4.0.3",
        "end-of-stream": "^1.4.1",
        "fs-constants": "^1.0.0",
        "inherits": "^2.0.3",
        "readable-stream": "^3.1.1"
      },
      "dependencies": {
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "temp": {
      "version": "0.8.3",
      "resolved": "https://registry.npmjs.org/temp/-/temp-0.8.3.tgz",
@@ -19778,6 +20274,30 @@
          "version": "1.0.0",
          "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
          "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
        }
      }
    },
    "unzipper": {
      "version": "0.10.11",
      "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz",
      "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==",
      "requires": {
        "big-integer": "^1.6.17",
        "binary": "~0.3.0",
        "bluebird": "~3.4.1",
        "buffer-indexof-polyfill": "~1.0.0",
        "duplexer2": "~0.1.4",
        "fstream": "^1.0.12",
        "graceful-fs": "^4.2.2",
        "listenercount": "~1.0.1",
        "readable-stream": "~2.3.6",
        "setimmediate": "~1.0.4"
      },
      "dependencies": {
        "bluebird": {
          "version": "3.4.7",
          "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz",
          "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM="
        }
      }
    },
@@ -20736,6 +21256,28 @@
        }
      }
    },
    "zip-stream": {
      "version": "4.1.0",
      "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.0.tgz",
      "integrity": "sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==",
      "requires": {
        "archiver-utils": "^2.1.0",
        "compress-commons": "^4.1.0",
        "readable-stream": "^3.6.0"
      },
      "dependencies": {
        "readable-stream": {
          "version": "3.6.0",
          "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz",
          "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==",
          "requires": {
            "inherits": "^2.0.3",
            "string_decoder": "^1.1.1",
            "util-deprecate": "^1.0.1"
          }
        }
      }
    },
    "zrender": {
      "version": "4.1.2",
      "resolved": "https://registry.npmjs.org/zrender/-/zrender-4.1.2.tgz",
package.json
@@ -4,7 +4,8 @@
  "private": true,
  "dependencies": {
    "@antv/data-set": "^0.11.4",
    "@antv/g2": "^4.1.0-beta.18",
    "@antv/g2": "^4.1.14",
    "@antv/util": "^2.0.13",
    "@babel/core": "7.5.5",
    "@svgr/webpack": "4.3.2",
    "@typescript-eslint/eslint-plugin": "1.13.0",
@@ -39,6 +40,7 @@
    "eslint-plugin-jsx-a11y": "6.2.3",
    "eslint-plugin-react": "7.14.3",
    "eslint-plugin-react-hooks": "^1.6.1",
    "exceljs": "^4.2.1",
    "file-loader": "3.0.1",
    "fs-extra": "7.0.1",
    "html-webpack-plugin": "4.0.0-beta.5",
@@ -52,6 +54,7 @@
    "jest-environment-jsdom-fourteen": "0.1.0",
    "jest-resolve": "24.8.0",
    "jest-watch-typeahead": "0.3.1",
    "js-table2excel": "^1.0.3",
    "jsbarcode": "^3.11.3",
    "jssha": "^3.2.0",
    "md5": "^2.2.1",
@@ -189,7 +192,7 @@
      ]
    ]
  },
  "homepage": "./build",
  "homepage": ".",
  "devDependencies": {
    "typescript": "^4.0.2"
  }
public/admin.html
@@ -24,10 +24,8 @@
              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/'
                }
              } else if (config.defaultApp) {
                appPort = 'pc/index.html'
              }
              window.location.replace(url + appPort)
            }
public/options.json
@@ -1,13 +1,15 @@
{
  "appId": "20201221213901540C6BC419CE41E47FABA62",
  "appkey": "2020122121373692319E639E61B0E46A6BC2C",
  "mainSystemApi": "http://cloud.positecgroup.com:8080/webapi/dostar",
  "mainSystemApi": "https://cloud.positecgroup.com/webapi/dostars",
  "systemType": "",
  "externalDatabase": "false",
  "externalDatabase": "mkdata_kress_test",
  "lineColor": "",
  "filter": "false",
  "defaultApp": "",
  "defaultLang": "zh-CN",
  "host": "http://qingqiumarket.cn",
  "service": "mkwms/"
  "WXAppID": "",
  "debugger": false,
  "host": "http://bms-test.kresstools.cn",
  "service": "oc/"
}
src/api/cacheutils.js
@@ -5,7 +5,7 @@
  /**
   * @description 打开websql
   */
  static openWebSql () {
  static openWebSql (sysType) {
    let service = window.GLOB.service ? '-' + window.GLOB.service.replace('/', '') : ''
    try {
      window.GLOB.WebSql = openDatabase(`mkdb${service}`, '1', 'mk-pc-database', 50 * 1024 * 1024)
@@ -22,6 +22,15 @@
          // eslint-disable-next-line
          throw 'CREATE TABLE ERROR'
        })
        if (sysType === 'local' && window.GLOB.systemType === '') {
          tx.executeSql('CREATE TABLE IF NOT EXISTS FUNCS (func_code varchar(50), key_sql text, CDefine1 varchar(50), CDefine2 varchar(50), CDefine3 varchar(50))', [], () => {
          }, () => {
            // eslint-disable-next-line
            throw 'CREATE TABLE ERROR'
          })
        }
      })
      // window.GLOB.WebSql.transaction(tx => {
      //   tx.executeSql('DROP TABLE VERSIONS')
@@ -42,16 +51,13 @@
    }
    return new Promise((resolve, reject) => {
      window.GLOB.WebSql.transaction(tx => {
        tx.executeSql('SELECT * FROM VERSIONS', [], (tx, results) => {
        tx.executeSql("SELECT * FROM VERSIONS where CDefine1='LongParam'", [], (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: ''})
          } else {
            resolve(results.rows[0])
          }
        }, (tx, results) => {
          window.GLOB.WebSql = null
@@ -107,7 +113,7 @@
  static updateWebSqlTime (curTime) {
    if (!window.GLOB.WebSql || !curTime) return
    window.GLOB.WebSql.transaction(tx => {
      tx.executeSql(`UPDATE VERSIONS SET createDate='${curTime}'`, [], () => {}, () => {
      tx.executeSql(`UPDATE VERSIONS SET createDate='${curTime}' where CDefine1='LongParam'`, [], () => {}, () => {
        window.GLOB.WebSql = null
      })
    })
@@ -119,7 +125,7 @@
  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}'`, [], () => {}, () => {
      tx.executeSql(`UPDATE VERSIONS SET version='${version}', createDate='${curTime}' where CDefine1='LongParam'`, [], () => {}, () => {
        window.GLOB.WebSql = null
      })
    })
@@ -131,7 +137,7 @@
  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], () => {}, () => {
      tx.executeSql('INSERT INTO VERSIONS (version, createDate, CDefine1) VALUES (?, ?, ?)', [version, curTime, 'LongParam'], () => {}, () => {
        window.GLOB.WebSql = null
      })
    })
@@ -182,7 +188,7 @@
  /**
   * @description 打开IndexedDB
   */
  static openIndexDB () {
  static openIndexDB (sysType) {
    let service = window.GLOB.service ? '-' + window.GLOB.service.replace('/', '') : ''
    try {
      let request = window.indexedDB.open(`mkdb${service}`, 1)
@@ -202,6 +208,9 @@
          objectStore.createIndex('menuid', 'menuid', { unique: false })
          objectStore.createIndex('userid', 'userid', { unique: false })
        }
        if (window.GLOB.systemType === '' && sysType === 'local' && !window.GLOB.IndexDB.objectStoreNames.contains('funcs')) {
          window.GLOB.IndexDB.createObjectStore('funcs', { keyPath: 'id' })
        }
      }
    } catch (e) {
      console.warn('IndexedDB 初始化失败!')
src/api/index.js
@@ -17,9 +17,9 @@
  1578479100252lfbp29v1kafk4s4q4ig,1577971621421tg4v0i1ur8873k7e0ob,1577929944419lgc5h3hepum765e2k7u,1588493493409k9guqp067d31lu7blsv`
if (window.openDatabase) {
  CacheUtils.openWebSql()
  CacheUtils.openWebSql(options.sysType)
} else if (window.indexedDB) {
  CacheUtils.openIndexDB()
  CacheUtils.openIndexDB(options.sysType)
}
axios.defaults.crossDomain = true
@@ -255,15 +255,31 @@
      login_id_address: ipAddress
    }
    param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    let sys_datetime = sessionStorage.getItem('sys_datetime')
    let app_datetime = sessionStorage.getItem('app_datetime')
    if (sys_datetime && app_datetime) {
      let seconds = Math.floor((new Date().getTime() - app_datetime) / 1000)
      param.timestamp = moment(sys_datetime, 'YYYY-MM-DD HH:mm:ss').add(seconds, 'seconds').format('YYYY-MM-DD HH:mm:ss')
    }
    // 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: 'mk_' 时
    // param.Type = '公钥'
    // let shaObj = new jsSHA('SHA-1', 'TEXT')
    // 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'
@@ -297,14 +313,26 @@
      return Promise.reject()
    }
    let curTime = moment().format('YYYY-MM-DD HH:mm:ss') + '.000'
    let sys_datetime = sessionStorage.getItem('sys_datetime')
    let app_datetime = sessionStorage.getItem('app_datetime')
    if (sys_datetime && app_datetime) {
      let seconds = Math.floor((new Date().getTime() - app_datetime) / 1000)
      curTime = moment(sys_datetime, 'YYYY-MM-DD HH:mm:ss').add(seconds, 'seconds').format('YYYY-MM-DD HH:mm:ss') + '.000'
    }
    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 modifydate = msg.createDate || curTime
          if (modifydate.indexOf('Invalid date') > -1) {
            modifydate = curTime
          }
          let param = {
            func: 's_get_app_version',
            modifydate: msg.createDate || curTime,
            modifydate
          }
  
          this.getSystemConfig(param).then(res => {
@@ -348,10 +376,13 @@
      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 modifydate = msg.createDate || curTime
          if (modifydate.indexOf('Invalid date') > -1) {
            modifydate = curTime
          }
          let param = {
            func: 's_get_app_version',
            modifydate: msg.createDate || curTime,
            modifydate
          }
          this.getSystemConfig(param).then(res => {
@@ -437,6 +468,30 @@
    return axios({
      url,
      method: 'post',
      data: param
    })
  }
  /**
   * @description 获取或修改系统配置,增加appkey
   */
  getSystemFuncs (time) {
    let param = {
      func: 's_get_func_base_sso',
      update_date: time,
      userid: sessionStorage.getItem('UserID') || '',
      lang: sessionStorage.getItem('lang') || '',
      SessionUid: localStorage.getItem('SessionUid') || '',
      LoginUID: sessionStorage.getItem('LoginUID') || '',
      appkey: window.GLOB.appkey
    }
    let url = window.GLOB.mainSystemApi
    param = this.encryptParam(param)
    return axios({
      url: `${url}/${param.func}`,
      method: 'post',
      data: param
    })
@@ -785,22 +840,17 @@
    param = this.encryptParam(param)
    let url = '/webapi/SaveBase64Image'
    if (param.rduri) {
      let url = param.rduri.replace(/webapi(.*)$/, 'webapi/SaveBase64Image')
      url = param.rduri.replace(/webapi(.*)$/, 'webapi/SaveBase64Image')
      delete param.rduri
      return axios({
        url,
        method: 'post',
        data: param
      })
    } else {
      return axios({
        url: '/webapi/SaveBase64Image',
        method: 'post',
        data: param
      })
    }
    return axios({
      url,
      method: 'post',
      data: param
    })
  }
  /**
src/assets/css/main.scss
@@ -135,15 +135,15 @@
  background: rgba(0, 0, 0, 0);
}
// 重置模态框滚动条
.ant-modal-wrap::-webkit-scrollbar {
.ant-modal-wrap::-webkit-scrollbar, .ant-drawer-wrapper-body::-webkit-scrollbar {
  width: 7px;
}
.ant-modal-wrap::-webkit-scrollbar-thumb {
.ant-modal-wrap::-webkit-scrollbar-thumb, .ant-drawer-wrapper-body::-webkit-scrollbar-thumb {
  border-radius: 5px;
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
  background: rgba(0, 0, 0, 0.13);
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.1);
  background: rgba(0, 0, 0, 0.1);
}
.ant-modal-wrap::-webkit-scrollbar-track {
.ant-modal-wrap::-webkit-scrollbar-track, .ant-drawer-wrapper-body::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
  border-radius: 3px;
  border: 1px solid rgba(0, 0, 0, 0.07);
@@ -386,4 +386,21 @@
      padding: 5px 20px;
    }
  }
}
.ant-input[disabled] {
  background-color: #fafafa!important;
}
.ant-btn[disabled] {
  background-color: transparent!important;
  border-color: rgba(0, 0, 0, 0.35)!important;
  color: rgba(0, 0, 0, 0.35)!important;
}
.ant-btn.ant-btn-link {
  border-width: 0;
}
.button-list {
  .ant-btn.ant-btn-link {
    border-width: 1px;
  }
}
src/assets/css/viewstyle.scss
@@ -33,11 +33,8 @@
      button.vercode {
        color: $color6;
      }
      .ant-tabs {
        .ant-tabs-tab.ant-tabs-tab-active {
          color: $color6;
        }
        .ant-tabs-tab:hover {
      .login-way-wrap {
        .login-way.active, .login-way:hover {
          color: $color6;
        }
      }
@@ -208,7 +205,7 @@
  .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 {
        .ant-btn:not(.ant-btn-primary):not(.ant-btn-link):active, .ant-btn:not(.ant-btn-primary):not(.ant-btn-link).active, .ant-btn:not(.ant-btn-primary):not(.ant-btn-link):hover, .ant-btn:not(.ant-btn-primary):not(.ant-btn-link):focus {
          color: $color7;
          border-color: $color7;
        }
@@ -216,6 +213,14 @@
          background-color: $color6;
          border-color: $color6;
        }
        .ant-btn-link {
          color: $color6;
        }
      }
    }
    .advanced-list {
      .advance-value {
        color: $color6;
      }
    }
  }
src/assets/img/rolemanage.jpg

src/assets/mobimg/dashboard.png
src/assets/mobimg/login.png
src/assets/mobimg/menubar.png
src/assets/mobimg/navtop-mob.png
src/assets/mobimg/nest.png
src/assets/mobimg/ratioboard.png
src/assets/mobimg/scatter.png
src/assets/mobimg/tree.png
src/components/editor/index.jsx
@@ -23,8 +23,8 @@
class NormalEditor extends Component {
  static propTpyes = {
    Item: PropTypes.object,     // 表单元素
    onChange: PropTypes.func,   // 表单更新
    config: PropTypes.object,
    onChange: PropTypes.func
  }
  state = {
@@ -33,16 +33,17 @@
  }
  UNSAFE_componentWillMount () {
    const { config, defaultValue } = this.props
    let initVal = null
    let encryption = 'false'
    if (this.props['data-__meta']) {
      initVal = this.props['data-__meta'].initialValue || null
    } else if (this.props.defaultValue) {
      initVal = this.props.defaultValue || null
    if (config && config.initval) {
      initVal = config.initval
    } else if (defaultValue) {
      initVal = defaultValue
    }
    if (this.props.Item && this.props.Item.encryption === 'true') {
    if (config && config.encryption === 'true') {
      encryption = 'true'
      if (initVal) {
        try {
@@ -92,6 +93,7 @@
    form.append('fileExt', params.file.fileType)
    form.append('shardingCnt', _param.chunks)
    form.append('shardingNo', _param.chunk)
    form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
    Api.getLargeFileUpload(form).then(res => {
      if (res.status) {
src/components/header/index.jsx
@@ -143,7 +143,7 @@
        edition_type: 'A',
        pro_sys: window.GLOB.systemType === 'production' ? 'Y' : ''
      }).then(result => {
        let _permAction = {} // 按钮权限
        let _permAction = {loaded: true} // 按钮权限
        if (result && result.status) {
          if (result.UserRoles_Menu) {
@@ -214,7 +214,7 @@
        this.props.modifyMenuTree(menulist)
        this.props.modifyMainMenu(mainMenu)
        this.props.initMenuPermission(thdMenuList)
        this.props.initMenuPermission([...thdMenuList, {MenuID: 'home_page_id', EasyCode: '', MenuName: 'home', type: 'CustomPage'}])
        resolve(_menu)
      })
@@ -371,7 +371,7 @@
    // 修改编辑状态
    let UserID = sessionStorage.getItem('CloudUserID')
    let LoginUID = sessionStorage.getItem('CloudLoginUID')
    if (!UserID || !LoginUID) {
      this.setState({
        loginVisible: true
@@ -408,12 +408,18 @@
          sessionStorage.setItem('dataM', res.dataM ? 'true' : '')
          sessionStorage.setItem('isEditState', 'true')
          this.setSystemFuncs()
          this.props.modifyMenuTree([])
          this.props.modifyMainMenu(null)
          this.props.modifyTabview([])
          this.props.history.replace('/design')
        } else {
          if (res.message.indexOf('密码错误') > -1) {
            const input = document.getElementById('password')
            input && input.select()
          }
          this.setState({
            loginLoading: false
          })
@@ -427,6 +433,107 @@
    })
  }
  setSystemFuncs = () => {
    if (!window.GLOB.WebSql && !window.GLOB.IndexDB) {
      return
    }
    this.getfuncTime().then(res => {
      Api.getSystemFuncs(res.createDate).then(result => {
        if (!result.status) {
          notification.error({
            top: 92,
            message: result.message,
            duration: 10
          })
          return
        } else if (result.func_detail && result.func_detail.length > 0) {
          this.writeFuncs(result.func_detail)
        }
      })
    })
  }
  writeFuncs = (funcs) => {
    let timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
    let sys_datetime = sessionStorage.getItem('sys_datetime')
    let app_datetime = sessionStorage.getItem('app_datetime')
    if (sys_datetime && app_datetime) {
      let seconds = Math.floor((new Date().getTime() - app_datetime) / 1000)
      timestamp = moment(sys_datetime, 'YYYY-MM-DD HH:mm:ss').add(seconds, 'seconds').format('YYYY-MM-DD HH:mm:ss')
    }
    if (window.GLOB.WebSql) {
      window.GLOB.WebSql.transaction(tx => {
        tx.executeSql('DELETE FROM FUNCS')
        funcs.forEach(item => {
          if (!item.key_sql) return
          tx.executeSql('INSERT INTO FUNCS (func_code, key_sql) VALUES (?, ?)', [item.func_code, item.key_sql])
        })
        tx.executeSql(`UPDATE VERSIONS SET createDate='${timestamp}' where CDefine1='funcs'`)
      })
    } else {
      let objectStore = window.GLOB.IndexDB.transaction(['funcs'], 'readwrite').objectStore('funcs')
      objectStore.clear()
      funcs.forEach(item => {
        if (!item.key_sql) return
        item.id = item.func_code
        objectStore.add(item)
      })
      let funcStore = window.GLOB.IndexDB.transaction(['version'], 'readwrite').objectStore('version')
      funcStore.put({id: 'funcs', version: '1.0', createDate: timestamp})
    }
  }
  getfuncTime = () => {
    return new Promise((resolve, reject) => {
      if (window.GLOB.WebSql) {
        window.GLOB.WebSql.transaction(tx => {
          tx.executeSql("SELECT * FROM VERSIONS where CDefine1='funcs'", [], (tx, results) => {
            let rows = results.rows
            if (rows.length === 0) {
              tx.executeSql('DELETE FROM FUNCS')
              tx.executeSql('INSERT INTO VERSIONS (version, createDate, CDefine1) VALUES (?, ?, ?)', ['1.0', '1970-01-01 14:59:09.000', 'funcs'])
              resolve({createDate: '1970-01-01 14:59:09.000'})
            } else {
              resolve(rows[0])
            }
          }, (tx, results) => {
            reject()
            console.warn(results)
          })
        })
      } else {
        let objectStore = window.GLOB.IndexDB.transaction(['version'], 'readwrite').objectStore('version')
        let request = objectStore.get('funcs')
        request.onerror = (event) => {
          console.warn(event)
          reject()
        }
        request.onsuccess = () => {
          if (request.result) {
            resolve(request.result)
          } else {
            let add = objectStore.add({id: 'funcs', version: '1.0', createDate: '1970-01-01 14:59:09.000'})
            add.onerror = () => {
              reject()
            }
            add.onsuccess = () => {
              resolve({id: 'funcs', version: '1.0', createDate: '1970-01-01 14:59:09.000'})
            }
          }
        }
      }
    })
  }
  changeSystem = (system) => {
    let url = system.LinkUrl1
src/components/mkIcon/index.jsx
New file
@@ -0,0 +1,70 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Icon, Row, Col, Button } from 'antd'
import { minkeIconSystem } from '@/utils/option.js'
import './index.scss'
class MkIcon extends Component {
  static propTpyes = {
    onChange: PropTypes.func
  }
  state = {
    selectIcon: '',
    allowClear: false,
    icons: [...minkeIconSystem.direction, ...minkeIconSystem.edit, ...minkeIconSystem.normal, ...minkeIconSystem.trademark, ...minkeIconSystem.data, ...minkeIconSystem.hint],
    visible: false
  }
  UNSAFE_componentWillMount () {
    let val = ''
    if (this.props['data-__meta']) {
      val = this.props['data-__meta'].initialValue || ''
    }
    this.setState({selectIcon: val, allowClear: this.props.allowClear === true})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  checkIcon = (val) => {
    this.setState({selectIcon: val, visible: false})
    this.props.onChange(val)
  }
  render() {
    const { selectIcon, visible, icons, allowClear } = this.state
    return (
      <div className="mk-icon-box">
        {selectIcon ? <Icon type={selectIcon}/> : <Icon style={{opacity: 0}} type="plus"/>}
        <Icon className="trigger" onClick={() => this.setState({visible: true})} type="swap"/>
        {allowClear ? <Icon className="close" onClick={() => this.checkIcon('')} type="close"/> : null}
        <Modal
          wrapClassName="popview-modal mk-icon-wrap"
          title={'图标选择'}
          visible={visible}
          width={800}
          maskClosable={false}
          onCancel={() => { this.setState({ visible: false }) }}
          footer={[
            <Button key="close" onClick={() => { this.setState({ visible: false }) }}>关闭</Button>
          ]}
          destroyOnClose
        >
          <Row>
            {icons.map(icon => <Col className={icon === selectIcon ? 'active' : ''} key={icon} span={4}>
              <Icon onClick={() => this.checkIcon(icon)} type={icon} />
            </Col>)}
          </Row>
        </Modal>
      </div>
    )
  }
}
export default MkIcon
src/components/mkIcon/index.scss
New file
@@ -0,0 +1,53 @@
.mk-icon-box {
  display: block;
  height: 32px;
  border: 1px solid #d9d9d9;
  border-radius: 4px;
  line-height: 32px;
  padding: 0px 0px 0px 10px;
  .anticon.trigger {
    float: right;
    line-height: 32px;
    padding: 0 10px;
    border-left: 1px solid #d9d9d9;
  }
  .close.anticon {
    float: right;
    margin-top: 6px;
    margin-right: 5px;
    background: #efefef;
    border-radius: 50%;
    color: #757575;
    padding: 3px;
    font-size: 10px;
    opacity: 0;
    transition: opacity 0.3s;
  }
}
.mk-icon-box:hover {
  border-color: #1890ff;
  .close.anticon {
    opacity: 1;
  }
}
.mk-icon-box::after {
  content: ' ';
  display: block;
  clear: both;
}
.mk-icon-wrap {
  .ant-col {
    text-align: center;
    line-height: 55px;
    .anticon {
      font-size: 30px;
      cursor: pointer;
    }
  }
  .active.ant-col {
    .anticon {
      color: #1890ff;
    }
  }
}
src/components/paste/index.jsx
New file
@@ -0,0 +1,72 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Icon, Modal, notification } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const PasteForm = asyncComponent(() => import('@/templates/zshare/pasteform'))
class PasteController extends Component {
  static propTpyes = {
    options: PropTypes.array,
    updateConfig: PropTypes.func
  }
  state = {
    visible: false
  }
  handleMenuClick = () => {
    this.setState({visible: true})
  }
  pasteSubmit = () => {
    const { options } = this.props
    this.pasteFormRef.handleConfirm().then(res => {
      if (!options.includes(res.copyType)) {
        notification.warning({ top: 92, message: '配置信息格式错误!', duration: 5 })
        return
      }
      this.props.updateConfig(res, (result) => {
        if (result.status) {
          notification.success({
            top: 92,
            message: '粘贴成功!',
            duration: 2
          })
          this.setState({visible: false})
        } else {
          notification.success({
            top: 92,
            message: result.message,
            duration: 2
          })
        }
      })
    })
  }
  render() {
    const { visible } = this.state
    return (
      <div style={{display: 'inline-block'}}>
        <Icon type="snippets" style={{color: 'purple'}} onClick={() => {this.setState({visible: true})}} />
        <Modal
          title="粘贴"
          visible={visible}
          width={600}
          maskClosable={false}
          onOk={this.pasteSubmit}
          onCancel={() => {this.setState({visible: false})}}
          destroyOnClose
        >
          <PasteForm wrappedComponentRef={(inst) => this.pasteFormRef = inst} inputSubmit={this.pasteSubmit}/>
        </Modal>
      </div>
    )
  }
}
export default PasteController
src/components/paste/index.scss
copy from src/tabviews/zshare/mutilform/customSwitch/index.scss copy to src/components/paste/index.scss
src/components/tabview/index.jsx
@@ -137,6 +137,8 @@
      return (<TabManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
    } else if (view.type === 'RolePermission') {
      return (<RoleManage MenuNo={view.MenuNo} MenuID={view.MenuID} MenuName={view.MenuName} key={view.MenuID}/>)
    } else if (view.type === 'appRolePermission') {
      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') {
src/index.js
@@ -69,9 +69,9 @@
sessionStorage.setItem('role_id', sessionStorage.getItem('localRole_id') || '')
sessionStorage.setItem('dataM', sessionStorage.getItem('localDataM') || '')
// 新系统文件置于admin中 ../options.json
// 新系统文件置于admin中 ../options.json , { cache: 'no-cache'}
fetch('./options.json')
fetch('../options.json')
  .then(response => response.json())
  .catch(() => {
    document.getElementById('root').innerHTML = '<div style="text-align: center; font-size: 30px; margin-top: 40vh;">系统配置信息获取失败,请联系管理员!</div>'
src/locales/zh-CN/model.js
@@ -125,7 +125,7 @@
  'model.form.link': '联动菜单',
  'model.form.linkform': '关联表单',
  'model.form.dateday': '日期(天)',
  'model.form.datetime': '日期(秒)',
  'model.form.datetime': '日期(分/秒)',
  'model.form.dateweek': '日期(周)',
  'model.form.datemonth': '日期(月)',
  'model.form.daterange': '日期(区间)',
src/menu/bgcontroller/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Form } from 'antd'
import { Form, Select } from 'antd'
import zhCN from '@/locales/zh-CN/mob.js'
import enUS from '@/locales/en-US/mob.js'
@@ -10,6 +10,7 @@
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
const { Option } = Select
class MobController extends Component {
  static propTpyes = {
@@ -21,6 +22,8 @@
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    backgroundColor: '',
    backgroundImage: '',
    backgroundSize: '',
    backgroundRepeat: '',
  }
  UNSAFE_componentWillMount () {
@@ -35,7 +38,9 @@
    this.setState({
      backgroundColor: config.style.backgroundColor,
      backgroundImage: bgImg
      backgroundImage: bgImg,
      backgroundSize: config.style.backgroundSize || '100%',
      backgroundRepeat: config.style.backgroundRepeat || 'repeat',
    })
  }
@@ -72,8 +77,30 @@
    this.props.updateConfig(config)
  }
  backgroundSizeChange = (val) => {
    this.setState({
      backgroundSize: val
    })
    let config = fromJS(this.props.config).toJS()
    config.style.backgroundSize = val
    this.props.updateConfig(config)
  }
  backgroundRepeatChange = (val) => {
    this.setState({
      backgroundRepeat: val
    })
    let config = fromJS(this.props.config).toJS()
    config.style.backgroundRepeat = val
    this.props.updateConfig(config)
  }
  render () {
    const { backgroundColor, backgroundImage } = this.state
    const { backgroundColor, backgroundImage, backgroundSize, backgroundRepeat } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -94,6 +121,23 @@
          <Form.Item colon={false} label="图片">
            <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
          </Form.Item>
          <Form.Item colon={false} label="比例">
            <Select defaultValue={backgroundSize} onChange={this.backgroundSizeChange}>
              <Option value="100%">100%</Option>
              <Option value="100% 100%">100% 100%</Option>
              <Option value="auto 100%">auto 100%</Option>
              <Option value="contain">contain</Option>
              <Option value="cover">cover</Option>
            </Select>
          </Form.Item>
          <Form.Item colon={false} label="重复">
            <Select defaultValue={backgroundRepeat} onChange={this.backgroundRepeatChange}>
              <Option value="repeat">repeat</Option>
              <Option value="no-repeat">no-repeat</Option>
              <Option value="repeat-x">repeat-x</Option>
              <Option value="repeat-y">repeat-y</Option>
            </Select>
          </Form.Item>
        </Form>
      </div>
    )
src/menu/components/card/balcony/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 } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import 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 CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/components/paste'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
class BalconyEditComponent 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: { interType: 'system' },
        wrap: { name: card.name, width: card.width || 24, linkType: 'static', position: 'relative', datatype: 'static' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '0px', marginBottom: '0px' },
        columns: [],
        scripts: [],
        elements: [],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
        _card.elements = _card.elements.map(elem => {
          elem.uuid = Utils.getuuid()
          return elem
        })
      }
      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)
  }
  updateCard = (elements) => {
    const { card } = this.state
    let _card = {...card, elements: elements}
    this.setState({
      card: _card,
    })
    this.props.updateConfig(_card)
  }
  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)
  }
  addElement = () => {
    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', [card.uuid, card.uuid], newcard)
  }
  addButton = () => {
    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', [card.uuid, card.uuid], newcard)
  }
  pasteComponent = (res, resolve) => {
    const { card } = this.state
    let type = res.copyType
    delete res.copyType
    res.uuid = Utils.getuuid()
    res.focus = true
    if (type === 'customCardElement') {
      MKEmitter.emit('cardAddElement', [card.uuid, card.uuid], res)
    } else {
      res.eleType = 'button'
      MKEmitter.emit('cardAddElement', [card.uuid, card.uuid], res)
    }
    resolve({status: true})
  }
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-balcony-edit-box" style={_style} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加元素" onClick={this.addElement} type="plus" />
            <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="balcony" card={card}/>
            <PasteComponent options={['action', 'customCardElement']} updateConfig={this.pasteComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <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}
            {card.wrap.datatype === 'static' ? <Icon style={{color: '#eeeeee', cursor: 'not-allowed'}} type="setting"/> : null}
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <CardCellComponent cards={card} cardCell={card} elements={card.elements} updateElement={this.updateCard}/>
      </div>
    )
  }
}
export default BalconyEditComponent
src/menu/components/card/balcony/index.scss
New file
@@ -0,0 +1,77 @@
.menu-balcony-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-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;
    }
  }
}
.menu-balcony-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-balcony-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/card/balcony/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 BalconyWrapSetting extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { config } = this.props
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title="卡片设置"
          visible={visible}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default BalconyWrapSetting
src/menu/components/card/balcony/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/card/balcony/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,415 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select, Cascader } from 'antd'
import MenuUtils from '@/utils/utils-custom.js'
import StyleInput from '@/menu/components/share/styleInput'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,
    config: PropTypes.object,
    wrap: PropTypes.object,
    inputSubmit: PropTypes.func
  }
  state = {
    roleList: [],
    modules: [],
    supmodules: [],
    appType: sessionStorage.getItem('appType'),
    linkType: this.props.wrap.linkType,
    position: this.props.wrap.position,
  }
  UNSAFE_componentWillMount () {
    let roleList = sessionStorage.getItem('sysRoles')
    if (roleList) {
      try {
        roleList = JSON.parse(roleList)
      } catch {
        roleList = []
      }
    } else {
      roleList = []
    }
    let menu = fromJS(window.GLOB.customMenu).toJS()
    let modules = MenuUtils.getLinkModules(menu.components)
    if (!modules) {
      modules = []
    }
    let _menu = fromJS(window.GLOB.customMenu).toJS()
    let supmodules = MenuUtils.getSupModules(_menu.components, '')
    if (!supmodules) {
      supmodules = []
    }
    this.setState({roleList, modules, supmodules})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  chose = (val) => {
    let values = {}
    if (val === 'top') {
      values = {
        top: '0px',
        left: '50%',
        right: '',
        bottom: '',
        transform: 'translateX(-50%)'
      }
    } else if (val === 'top-left') {
      values = {
        top: '0px',
        left: '0px',
        right: '',
        bottom: '',
        transform: ''
      }
    } else if (val === 'top-right') {
      values = {
        top: '0px',
        left: '',
        right: '0px',
        bottom: '',
        transform: ''
      }
    } else if (val === 'left-middle') {
      values = {
        top: '50%',
        left: '0px',
        right: '',
        bottom: '',
        transform: 'translateY(-50%)'
      }
    } else if (val === 'right-middle') {
      values = {
        top: '50%',
        left: '',
        right: '0px',
        bottom: '',
        transform: 'translateY(-50%)'
      }
    } else if (val === 'bottom-left') {
      values = {
        top: '',
        left: '0px',
        right: '',
        bottom: '0px',
        transform: ''
      }
    } else if (val === 'bottom-right') {
      values = {
        top: '',
        left: '',
        right: '0px',
        bottom: '0px',
        transform: ''
      }
    } else if (val === 'bottom') {
      values = {
        top: '',
        left: '50%',
        right: '',
        bottom: '0px',
        transform: 'translateX(-50%)'
      }
    } else if (val === 'middle') {
      values = {
        top: '50%',
        left: '50%',
        right: '',
        bottom: '',
        transform: 'translate(-50%, -50%)'
      }
    }
    this.props.form.setFieldsValue(values)
  }
  render() {
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList, modules, supmodules, linkType, position } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择静态值,无需配置数据源。">
                  <Icon type="question-circle" />
                  数据来源
                </Tooltip>
              }>
                {getFieldDecorator('datatype', {
                  initialValue: wrap.datatype
                })(
                  <Radio.Group>
                    <Radio value="dynamic">动态</Radio>
                    <Radio value="static">静态</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="组件与其他组件之间的控制类型,独立表示与其他没有关联。">
                  <Icon type="question-circle" />
                  受控类型
                </Tooltip>
              }>
                {getFieldDecorator('linkType', {
                  initialValue: wrap.linkType || 'static'
                })(
                  <Radio.Group onChange={(e) => this.setState({linkType: e.target.value})}>
                    <Radio value="static">独立</Radio>
                    <Radio value="sync">同步</Radio>
                    <Radio value="sup">上级</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {linkType === 'sup' ? <Col span={12}>
              <Form.Item label="上级组件">
                {getFieldDecorator('supModule', {
                  initialValue: wrap.supModule,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '上级组件!'
                    }
                  ]
                })(
                  <Cascader options={supmodules} expandTrigger="hover" placeholder="" />
                )}
              </Form.Item>
            </Col> : null}
            {linkType === 'sup' ? <Col span={12}>
              <Form.Item label="显示控制">
                {getFieldDecorator('supControl', {
                  initialValue: wrap.supControl || 'show'
                })(
                  <Radio.Group>
                    <Radio key="hidden" value="hidden"> 选行显示 </Radio>
                    <Radio key="show" value="show"> 始终显示 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {linkType === 'sync' ? <Col span={12}>
              <Form.Item label="同步组件">
                {getFieldDecorator('syncModule', {
                  initialValue: wrap.syncModule,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '同步组件!'
                    }
                  ]
                })(
                  <Cascader options={modules} expandTrigger="hover" placeholder="" />
                )}
              </Form.Item>
            </Col> : null}
            {linkType === 'sync' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="当同步组件可多选时,设置全选有效。">
                  <Icon type="question-circle" />
                  全选
                </Tooltip>
              }>
                {getFieldDecorator('checkAll', {
                  initialValue: wrap.checkAll || 'hidden'
                })(
                  <Radio.Group>
                    <Radio key="hidden" value="hidden"> 隐藏 </Radio>
                    <Radio key="show" value="show"> 显示 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="使用固定定位时,请在测试环境中查看定位效果。">
                  <Icon type="question-circle" />
                  位置
                </Tooltip>
              }>
                {getFieldDecorator('position', {
                  initialValue: wrap.position || 'relative'
                })(
                  <Radio.Group onChange={(e) => this.setState({position: e.target.value})}>
                    <Radio value="relative">相对定位</Radio>
                    <Radio value="fixed">固定定位</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="快捷选择">
                <Select onSelect={this.chose}>
                  <Select.Option key='1' value={'top'}>上</Select.Option>
                  <Select.Option key='2' value={'top-left'}>左上</Select.Option>
                  <Select.Option key='3' value={'top-right'}>右上</Select.Option>
                  <Select.Option key='4' value={'left-middle'}>左中</Select.Option>
                  <Select.Option key='5' value={'right-middle'}>右中</Select.Option>
                  <Select.Option key='6' value={'bottom-left'}>左下</Select.Option>
                  <Select.Option key='7' value={'bottom-right'}>右下</Select.Option>
                  <Select.Option key='8' value={'bottom'}>下</Select.Option>
                  <Select.Option key='9' value={'middle'}>中间</Select.Option>
                </Select>
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="距上">
                {getFieldDecorator('top', {
                  initialValue: wrap.top || ''
                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="距右">
                {getFieldDecorator('right', {
                  initialValue: wrap.right || ''
                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="距下">
                {getFieldDecorator('bottom', {
                  initialValue: wrap.bottom || ''
                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="距左">
                {getFieldDecorator('left', {
                  initialValue: wrap.left || ''
                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="实际宽度">
                {getFieldDecorator('realwidth', {
                  initialValue: wrap.realwidth || ''
                })(<StyleInput options={['px', 'vh', 'vw', '%']} />)}
              </Form.Item>
            </Col> : null}
            {position === 'fixed' ? <Col span={12}>
              <Form.Item label="变换">
                {getFieldDecorator('transform', {
                  initialValue: wrap.transform || ''
                })(
                  <Select>
                    <Select.Option key='1' value={''}>无</Select.Option>
                    <Select.Option key='2' value={'translateY(-50%)'}>上移50%</Select.Option>
                    <Select.Option key='3' value={'translateY(50%)'}>下移50%</Select.Option>
                    <Select.Option key='4' value={'translateX(-50%)'}>左移50%</Select.Option>
                    <Select.Option key='5' value={'translateX(50%)'}>右移50%</Select.Option>
                    <Select.Option key='6' value={'translate(-50%, -50%)'}>左上移50%</Select.Option>
                    <Select.Option key='7' value={'translate(-50%, 50%)'}>左下移50%</Select.Option>
                    <Select.Option key='8' value={'translate(50%, -50%)'}>右上移50%</Select.Option>
                    <Select.Option key='9' value={'translate(50%, 50%)'}>右下移50%</Select.Option>
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
                  initialValue: wrap.blacklist || []
                })(
                  <Select
                    showSearch
                    mode="multiple"
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {roleList.map(option =>
                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/components/card/balcony/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,36 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
  .css {
    padding-top: 10px;
    .css-class {
      position: absolute;
      right: 13px;
      top: -15px;
      z-index: 1;
      button {
        height: 25px;
      }
    }
    .ant-form-item {
      margin-bottom: 0;
    }
    .ant-form-item-label {
      width: 16%;
    }
    .ant-form-item-control-wrapper {
      width: 84%;
      .code-mirror-wrap .code-mirror-area .CodeMirror {
        height: 100px;
        min-height: 100px;
      }
    }
  }
}
src/menu/components/card/cardcellcomponent/dragaction/action.jsx
@@ -1,9 +1,11 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Popover, Button } from 'antd'
import { resetStyle } from '@/utils/utils-custom.js'
import './index.scss'
const Card = ({ id, card, moveCard, findCard, editCard, delCard, profileCard, changeStyle, doubleClickCard }) => {
const Card = ({ id, card, moveCard, findCard, editCard, copyCard, delCard, profileCard, changeStyle, doubleClickCard }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'action', id, originalIndex },
@@ -25,12 +27,6 @@
    },
  })
  let _style = {opacity: isDragging ? 0 : 1}
  if (card.style) {
    _style = {...card.style, opacity: isDragging ? 0 : 1}
  }
  let hasProfile = false
  if (['pop', 'prompt', 'exec'].includes(card.OpenType)) {
    hasProfile = true
@@ -41,25 +37,27 @@
  }
  let btnElement = null
  let _style = resetStyle(card.style)
  if (card.show === 'icon') {
    btnElement = (<Button style={card.btnstyle} type="link"><Icon type={card.icon}/></Button>)
    btnElement = (<Button style={_style} type="link"><Icon type={card.icon}/></Button>)
  } else if (card.show === 'link') {
    btnElement = (<Button style={card.btnstyle} type="link">{card.label}{card.icon ? <Icon type={card.icon}/> : null}</Button>)
    btnElement = (<Button style={_style} type="link">{card.icon ? <Icon type={card.icon}/> : null}{card.label}</Button>)
  } else {
    btnElement = (<Button icon={card.icon} style={card.btnstyle}> {card.label} </Button>)
    btnElement = (<Button style={_style}> {card.label}{card.icon ? <Icon type={card.icon}/> : null} </Button>)
  }
  return (
    <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={() => editCard(id)} />
        <Icon className="copy" title="复制" type="copy" onClick={() => copyCard(id)} />
        <Icon className="close" title="删除" type="close" onClick={() => delCard(id)} />
        <Icon className="style" title="调整样式" onClick={() => changeStyle(id)} type="font-colors" />
        {hasProfile ? <Icon className="profile" title="setting" type="profile" onClick={() => profileCard(id)} /> : null}
      </div>
    } trigger="hover">
      <div ref={node => drag(drop(node))} className={'ant-col card-button-cell ant-col-' + card.width} onDoubleClick={() => doubleClickCard(id)}>
        <div style={_style}>
      <div ref={node => drag(drop(node))} className={'ant-col card-button-cell ant-col-' + card.width} onDoubleClick={(e) => {e.stopPropagation(); doubleClickCard(id)}}>
        <div style={{opacity: isDragging ? 0 : 1}}>
          {btnElement}
        </div>
      </div>
src/menu/components/card/cardcellcomponent/dragaction/card.jsx
@@ -11,13 +11,18 @@
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import './index.scss'
const BarCode = asyncComponent(() => import('@/components/barcode'))
const QrCode = asyncComponent(() => import('@/components/qrcode'))
const Video = asyncComponent(() => import('@/components/video'))
const MarkColumn = asyncIconComponent(() => import('@/menu/components/share/markcomponent'))
const PicRadio = {
  '4:3': '75%', '3:2': '66.67%', '16:9': '56.25%', '2:1': '50%', '3:1': '33.33%', '4:1': '25%',
  '5:1': '20%', '6:1': '16.67%', '7:1': '14.29%', '8:1': '12.5%', '9:1': '11.11%',
  '10:1': '10%', '3:4': '133.33%', '2:3': '150%', '9:16': '177.78%'
}
const Card = ({ id, parent, fields, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, updateMarks, doubleClickCard }) => {
  const originalIndex = findCard(id).index
@@ -45,6 +50,17 @@
  
  if (card.style) {
    _style = {...card.style, opacity: isDragging ? 0 : 1}
    _style = resetStyle(_style)
  }
  if (card.eleType === 'picture' && card.maxWidth) {
    _style.maxWidth = card.maxWidth
    let left = _style.marginLeft && _style.marginLeft !== '0px' ? _style.marginLeft : 'auto'
    let right = _style.marginRight && _style.marginRight !== '0px' ? _style.marginRight : 'auto'
    _style.margin = (_style.marginTop || 0) + ' ' + right + ' ' + (_style.marginBottom || 0) + ' ' + left
    delete _style.marginLeft
    delete _style.marginRight
    delete _style.marginTop
    delete _style.marginBottom
  }
  const getContent = () => {
@@ -85,12 +101,8 @@
        _imagestyle.borderRadius = card.style.borderRadius
      }
      if (card.lenWidRadio === '16:9') {
        _imagestyle.paddingTop = '56.25%'
      } else if (card.lenWidRadio === '3:2') {
        _imagestyle.paddingTop = '66.67%'
      } else if (card.lenWidRadio === '4:3') {
        _imagestyle.paddingTop = '75%'
      if (PicRadio[card.lenWidRadio]) {
        _imagestyle.paddingTop = PicRadio[card.lenWidRadio]
      } else {
        _imagestyle.paddingTop = '100%'
      }
@@ -146,7 +158,7 @@
        <Icon className="copy" title="复制" type="copy" onClick={() => copyCard(id)} />
        <Icon className="close" title="删除" type="close" onClick={() => delCard(id)} />
        <Icon className="style" title="调整样式" onClick={() => changeStyle(id)} type="font-colors" />
        {['text', 'number', 'slider'].includes(card.eleType) ? <MarkColumn columns={fields} type={card.eleType} marks={card.marks} onSubmit={(vals) => updateMarks({...card, marks: vals})} /> : null }
        {['text', 'number', 'slider', 'sequence'].includes(card.eleType) ? <MarkColumn columns={fields} type={card.eleType} marks={card.marks} onSubmit={(vals) => updateMarks({...card, marks: vals})} /> : null }
      </div>
    } trigger="hover">
      <div ref={node => drag(drop(node))} className={'ant-col card-cell ant-col-' + card.width}>
src/menu/components/card/cardcellcomponent/dragaction/index.jsx
@@ -38,7 +38,11 @@
    let copycard = fromJS(card).toJS()
    let _cards = fromJS(cards).toJS()
    copycard.copyType = 'customCardElement'
    if (card.eleType === 'button') {
      copycard.copyType = 'action'
    } else {
      copycard.copyType = 'customCardElement'
    }
    copycard.focus = true
    let _val = fromJS(copycard).toJS()
@@ -112,6 +116,7 @@
              key={card.uuid}
              card={card}
              parent={parent}
              copyCard={copyCard}
              moveCard={moveCard}
              editCard={editCard}
              changeStyle={changeStyle}
src/menu/components/card/cardcellcomponent/elementform/index.jsx
@@ -15,7 +15,7 @@
  sequence: ['eleType', 'width'],
  text: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix', 'link'],
  number: ['eleType', 'datatype', 'format', 'width', 'height', 'prefix', 'postfix'],
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'link'],
  picture: ['eleType', 'datatype', 'width', 'lenWidRadio', 'maxWidth', 'link'],
  video: ['eleType', 'datatype', 'width', 'aspectRatio', 'autoPlay', 'loop'],
  icon: ['eleType', 'icon', 'datatype', 'width'],
  slider: ['eleType', 'datatype', 'width', 'color', 'maxValue'],
src/menu/components/card/cardcellcomponent/formconfig.jsx
@@ -4,11 +4,11 @@
const Formdict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
/**
 * @description 获取按钮表单配置信息
 * @param {*} card           编辑按钮
 * @param {*} type           按钮类型,用于区分可选的打开方式
 * @description 获取元素配置信息
 * @param {*} card
 * @param {*} type
 */
export function getCardCellForm (card, type) {
export function getCardCellForm (card, type, subtype, cardCell) {
  let _options = [
    { value: 'text', text: '文本'},
    { value: 'number', text: '数值'},
@@ -22,7 +22,7 @@
    { value: 'currentDate', text: '当前时间'},
  ]
  if (type === 'table') {
  if (type === 'table' || (type === 'card' && subtype === 'datacard')) {
    _options.push({value: 'sequence', text: '序号'})
  }
  let appMenus = []
@@ -40,6 +40,17 @@
    } else {
      appMenus = []
    }
  }
  let dataTypes = [
    { value: 'dynamic', text: '动态' },
    { value: 'static', text: '静态' }
  ]
  if (cardCell.$cardType === 'extendCard') {
    card.datatype = 'static'
    dataTypes = [
      { value: 'static', text: '静态' }
    ]
  }
  let forms = [
@@ -98,10 +109,7 @@
      label: '数据类型',
      initVal: card.datatype || 'static',
      required: true,
      options: [
        { value: 'dynamic', text: '动态' },
        { value: 'static', text: '静态' }
      ]
      options: dataTypes
    },
    {
      type: 'select',
@@ -293,10 +301,32 @@
      required: true,
      options: [
        { value: '1:1', text: '1:1' },
        { value: '3:2', text: '3:2' },
        { value: '4:3', text: '4:3' },
        { value: '16:9', text: '16:9' }
        { value: '3:2', text: '3:2' },
        { value: '16:9', text: '16:9' },
        { value: '2:1', text: '2:1' },
        { value: '3:1', text: '3:1' },
        { value: '4:1', text: '4:1' },
        { value: '5:1', text: '5:1' },
        { value: '6:1', text: '6:1' },
        { value: '7:1', text: '7:1' },
        { value: '8:1', text: '8:1' },
        { value: '9:1', text: '9:1' },
        { value: '10:1', text: '10:1' },
        { value: '3:4', text: '3:4' },
        { value: '2:3', text: '2:3' },
        { value: '9:16', text: '9:16' },
      ]
    },
    {
      type: 'number',
      key: 'maxWidth',
      min: 10,
      max: 2000,
      label: '最大宽度',
      initVal: card.maxWidth || '',
      tooltip: '图片宽度的最大值。',
      required: false,
    },
    {
      type: 'select',
@@ -332,7 +362,7 @@
      forbid: !isApp,
      options: [
        { value: '', text: '无' },
        { value: 'page', text: '菜单' },
        // { value: 'page', text: '菜单' },
        { value: 'linkpage', text: '关联菜单' },
        { value: 'custom', text: '链接' }
      ]
@@ -358,15 +388,15 @@
        { value: 'self', text: '当前页面' }
      ]
    },
    {
      type: 'select',
      key: 'copyMenuId',
      label: '复制菜单',
      initVal: card.copyMenuId || '',
      required: false,
      forbid: !isApp,
      options: appMenus
    },
    // {
    //   type: 'select',
    //   key: 'copyMenuId',
    //   label: '复制菜单',
    //   initVal: card.copyMenuId || '',
    //   required: false,
    //   forbid: !isApp,
    //   options: appMenus
    // },
    {
      type: 'radio',
      key: 'joint',
src/menu/components/card/cardcellcomponent/index.jsx
@@ -34,6 +34,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,          // 编辑中元素
    formlist: null,      // 表单信息
    elements: null,      // 按钮组
@@ -47,16 +48,25 @@
   */
  UNSAFE_componentWillMount () {
    const { elements } = this.props
    let _elements = fromJS(elements).toJS()
    this.setState({
      elements: fromJS(elements).toJS()
      elements: _elements.map(item => {
        if (item.btnstyle) { // 兼容
          item.style = item.style || {}
          item.style = {...item.style, ...item.btnstyle}
          delete item.btnstyle
        }
        return item
      })
    })
  }
  componentDidMount () {
    MKEmitter.addListener('cardAddElement', this.cardAddElement)
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('submitModal', this.handleSave)
    MKEmitter.addListener('cardAddElement', this.cardAddElement)
    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
  }
@@ -83,9 +93,9 @@
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('cardAddElement', this.cardAddElement)
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitModal', this.handleSave)
    MKEmitter.removeListener('cardAddElement', this.cardAddElement)
    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
  }
@@ -126,12 +136,10 @@
    const { cards, cardCell } = this.props
    let _style = element.style ? fromJS(element.style).toJS() : {}
    let options = ['font', 'border', 'padding', 'margin', 'backgroundColor']
    let options = ['font', 'border', 'padding', 'margin', 'background']
    if (element.eleType === 'button') {
      if (element.btnstyle) {
        _style = {..._style, ...element.btnstyle}
      }
      options.push('width', 'float')
    } else if (element.eleType === 'picture') {
      options = ['border', 'margin']
    } else if (element.eleType === 'slider') {
@@ -187,6 +195,17 @@
      if (line) {
        _card.innerHeight = fontSize * lineHeight * line
      }
    } else if (_card.eleType === 'sequence') {
      _card.style = style
      let fontSize = 14
      let lineHeight = 1.5
      if (_card.style.fontSize) {
        fontSize = parseInt(_card.style.fontSize)
      }
      _card.innerHeight = fontSize * lineHeight
    } else if (_card.eleType === 'barcode') {
      _card.style = style
@@ -198,47 +217,7 @@
      _card.innerHeight = _card.barHeight + (_card.displayValue === 'true' ? fontSize + 2 : 0)
    } else if (_card.eleType === 'button') { // 拆分style
      let _style = fromJS(style).toJS()
      _card.style = {}
      if (_style.marginTop) {
        _card.style.marginTop = _style.marginTop
        delete _style.marginTop
      }
      if (_style.marginBottom) {
        _card.style.marginBottom = _style.marginBottom
        delete _style.marginBottom
      }
      if (_style.marginLeft) {
        _card.style.marginLeft = _style.marginLeft
        delete _style.marginLeft
      }
      if (_style.marginRight) {
        _card.style.marginRight = _style.marginRight
        delete _style.marginRight
      }
      if (_style.paddingTop) {
        _card.style.paddingTop = _style.paddingTop
        delete _style.paddingTop
      }
      if (_style.paddingBottom) {
        _card.style.paddingBottom = _style.paddingBottom
        delete _style.paddingBottom
      }
      if (_style.paddingLeft) {
        _card.style.paddingLeft = _style.paddingLeft
        delete _style.paddingLeft
      }
      if (_style.paddingRight) {
        _card.style.paddingRight = _style.paddingRight
        delete _style.paddingRight
      }
      if (_style.textAlign) {
        _card.style.textAlign = _style.textAlign
        delete _style.textAlign
      }
      _card.btnstyle = _style
      _card.style = style
    } else {
      _card.style = style
    }
@@ -259,7 +238,7 @@
   * @description 元素编辑,获取元素表单信息
   */
  handleElement = (card) => {
    const { cards } = this.props
    const { cards, cardCell } = this.props
    if (card.eleType === 'button') {
      this.handleAction(card)
@@ -267,7 +246,7 @@
      this.setState({
        visible: true,
        card: card,
        formlist: getCardCellForm(card, cards.type)
        formlist: getCardCellForm(card, cards.type, cards.subtype, cardCell)
      })
    }
  }
@@ -310,7 +289,7 @@
    this.setState({
      actvisible: true,
      card: card,
      formlist: getActionForm(card, functip, cards.setting, usefulFields, 'card', menulist, modules)
      formlist: getActionForm(card, functip, cards, usefulFields, 'card', menulist, modules)
    })
  }
@@ -404,18 +383,18 @@
        if (cell.uuid === res.uuid) {
          res = {...cell, ...res}
          delete res.focus
          let btnstyle = {}
          let style = {}
          if (res.class !== cell.class || res.show !== cell.show || !res.btnstyle) {
          if (res.class !== cell.class || res.show !== cell.show || !res.style) {
            if (res.show === 'link' || res.show === 'icon') {
              btnstyle.color = color[res.class]
              btnstyle.backgroundColor = 'transparent'
              style.color = color[res.class]
              style.backgroundColor = 'transparent'
            } else {
              btnstyle.color = '#ffffff'
              btnstyle.backgroundColor = color[res.class]
              style.color = '#ffffff'
              style.backgroundColor = color[res.class]
            }
          }
          res.btnstyle = {...res.btnstyle, ...btnstyle}
          res.style = {...res.style, ...style}
          return res
        }
@@ -436,17 +415,14 @@
   */
  deleteElement = (card) => {
    const { cards, cardCell, side } = this.props
    const { dict, elements } = this.state
    const { dict, elements, appType } = this.state
    let _this = this
    confirm({
      content: dict['model.confirm'] + dict['model.delete'] + '元素吗?',
      onOk() {
        let _elements = elements.filter(item => item.uuid !== card.uuid)
        if (card.eleType === 'button') {
          MKEmitter.emit('delButtons', [card.uuid])
        }
        if (card.OpenType === 'popview' || card.verify || card.modal) {
          card.$parentId = cardCell.uuid
          card.$side = side || ''
@@ -459,6 +435,11 @@
        }, () => {
          _this.props.updateElement(_elements)
        })
        if (card.eleType !== 'button') return
        if (appType === 'mob' || (appType === 'pc' && card.OpenType !== 'popview')) return
        MKEmitter.emit('delButtons', [card.uuid])
      },
      onCancel() {}
    })
@@ -500,15 +481,16 @@
  handleSubConfig = (item) => {
    const { cards } = this.props
    const { appType } = this.state
    let btn = fromJS(item).toJS()
    if ((sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') !== 'false')) return
    if ((sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') === 'true')) return
    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' },
            setting: { title: btn.label, width: appType === 'mob' ? 100 : 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
            tables: [],
            groups: [],
            fields: []
@@ -647,7 +629,7 @@
        >
          <ActionForm
            dict={dict}
            type="card"
            type={cards.type === 'balcony' ? '' : 'card'}
            card={card}
            formlist={this.state.formlist}
            inputSubmit={this.handleActionSubmit}
src/menu/components/card/cardcellcomponent/index.scss
@@ -16,16 +16,23 @@
    cursor: pointer;
  }
  .ant-btn {
    padding: 0;
  }
  .card-button-cell {
    float: left;
    button {
      width: 100%;
      background-size: cover;
      background-position: center center;
      height: auto;
      min-height: 32px;
      text-align: center;
      span {
        font-style: inherit;
        text-decoration: inherit;
        font-weight: inherit;
      }
    }
  }
src/menu/components/card/cardcomponent/index.jsx
@@ -8,13 +8,14 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import { resetStyle } from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const CardCellComponent = asyncComponent(() => import('../cardcellcomponent'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('./pastecomponent'))
class CardBoxComponent extends Component {
  static propTpyes = {
@@ -88,7 +89,7 @@
    this.props.updateElement(_card)
  }
  updateCard = (elements) => {
  updateCard = (elements, type) => {
    const { card, side } = this.state
    let _card = {}
@@ -99,9 +100,16 @@
      _card = {...card, elements: elements}
    }
    this.setState({
      card: _card
    })
    if (type === 'paste') {
      this.setState({
        card: _card,
        elements: fromJS(elements).toJS()
      })
    } else {
      this.setState({
        card: _card
      })
    }
    this.props.updateElement(_card)
  }
@@ -213,6 +221,14 @@
    }
  }
  doubleClickCard = () => {
    const { card } = this.state
    if (card.setting.click === 'menu' && card.setting.menu) {
      MKEmitter.emit('changeEditMenu', {MenuID: card.setting.menu})
    }
  }
  render() {
    const { cards, offset } = this.props
    const { card, elements, side, settingVisible, dict } = this.state
@@ -233,10 +249,11 @@
        marginTop: card.style.marginTop
      }
    }
    _style = resetStyle(_style)
    return (
      <Col span={card.setting.width || 6} offset={offset || 0}>
        <div className="card-item" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <div className="card-item" style={_style} onClick={this.clickComponent} onDoubleClick={(e) => {e.stopPropagation(); this.doubleClickCard()}} id={card.uuid}>
          <CardCellComponent cards={cards} cardCell={card} side={side} elements={elements} updateElement={this.updateCard}/>
          <div className="card-control">
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
@@ -245,16 +262,17 @@
                <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}/>
                <PasteComponent elements={elements} options={['action', 'customCardElement']} updateConfig={(list) => this.updateCard(list, 'paste')} />
                <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={
                <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')} />
                    <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}
                </Popover>
                {cards.subtype === 'propcard' || card.$cardType === 'extendCard' ? <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} /> : null}
                {card.setting.type === 'multi' ? <Switch size="small" onClick={this.changeSide} defaultChecked /> : null}
              </div>
            } trigger="hover">
@@ -266,7 +284,7 @@
          wrapClassName="popview-modal"
          title={'卡片设置'}
          visible={settingVisible}
          width={700}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.settingSubmit}
src/menu/components/card/cardcomponent/pastecomponent/index.jsx
New file
@@ -0,0 +1,77 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Icon, Modal, notification } from 'antd'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const PasteForm = asyncComponent(() => import('@/templates/zshare/pasteform'))
class PasteController extends Component {
  static propTpyes = {
    config: PropTypes.object,        // 组件配置
    updateConfig: PropTypes.func
  }
  state = {
    visible: false
  }
  handleMenuClick = () => {
    this.setState({visible: true})
  }
  pasteSubmit = () => {
    const { options, elements } = this.props
    this.pasteFormRef.handleConfirm().then(res => {
      if (!options.includes(res.copyType)) {
        notification.warning({ top: 92, message: '配置信息格式错误!', duration: 5 })
        return
      }
      let _uuid = Utils.getuuid()
      if (res.copyType === 'action' && res.OpenType === 'popview') {
        let _cell = fromJS(res).toJS()
        _cell.$originUuid = res.uuid
        _cell.uuid = _uuid
        MKEmitter.emit('copyButtons', [_cell])
      }
      res.uuid = _uuid
      this.props.updateConfig([...elements, res])
      this.setState({visible: false})
      notification.success({
        top: 92,
        message: '粘贴成功!',
        duration: 2
      })
    })
  }
  render() {
    const { visible } = this.state
    return (
      <div style={{display: 'inline-block'}}>
        <Icon type="snippets" style={{color: 'purple'}} onClick={() => {this.setState({visible: true})}} />
        <Modal
          title="粘贴"
          visible={visible}
          width={600}
          maskClosable={false}
          onOk={this.pasteSubmit}
          onCancel={() => {this.setState({visible: false})}}
          destroyOnClose
        >
          <PasteForm wrappedComponentRef={(inst) => this.pasteFormRef = inst} inputSubmit={this.pasteSubmit}/>
        </Modal>
      </div>
    )
  }
}
export default PasteController
src/menu/components/card/cardcomponent/pastecomponent/index.scss
copy from src/tabviews/zshare/mutilform/customSwitch/index.scss copy to src/menu/components/card/cardcomponent/pastecomponent/index.scss
src/menu/components/card/cardcomponent/settingform/index.jsx
@@ -17,15 +17,15 @@
  state = {
    type: this.props.setting.type || 'simple',
    click: this.props.setting.click || '',
    isApp: sessionStorage.getItem('appType') === 'pc',
    appType: sessionStorage.getItem('appType'),
    menulist: []
  }
  UNSAFE_componentWillMount() {
    const { isApp } = this.state
    const { appType } = this.state
    let menulist = null
    if (isApp) {
    if (appType) {
      menulist = sessionStorage.getItem('appMenus')
    } else {
      menulist = sessionStorage.getItem('fstMenuList')
@@ -67,7 +67,7 @@
  render() {
    const { setting, cards } = this.props
    const { getFieldDecorator } = this.props.form
    const { menulist, click, isApp } = this.state
    const { menulist, click, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -81,7 +81,7 @@
    }
    return (
      <div className="model-menu-setting-form">
      <div className="model-menu-card-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
@@ -102,7 +102,7 @@
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col>
            <Col span={12}>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择复式卡时,可配置鼠标悬浮时的显示信息。">
                  <Icon type="question-circle" />
@@ -118,7 +118,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            {this.state.type === 'multi' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="复式卡片鼠标悬浮信息的动画效果。">
@@ -155,19 +155,25 @@
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="点击事件">
              <Form.Item label={
                <Tooltip placement="topLeft" title="当选择触发按钮时,只有当卡片中只存在一个按钮时有效。">
                  <Icon type="question-circle" />
                  点击事件
                </Tooltip>
              }>
                {getFieldDecorator('click', {
                  initialValue: click
                })(
                  <Radio.Group onChange={(e) => this.setState({click: e.target.value})}>
                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({click: e.target.value})}>
                    <Radio value="">无</Radio>
                    <Radio value="menu">菜单</Radio>
                    <Radio value="link">链接</Radio>
                    <Radio value="button">按钮</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {!isApp && click === 'menu' ? <Col span={12}>
            {!appType && click === 'menu' ? <Col span={12}>
              <Form.Item label="菜单">
                {getFieldDecorator('menu', {
                  initialValue: setting.menu || [],
@@ -182,7 +188,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {isApp && click === 'menu' ? <Col span={12}>
            {appType && click === 'menu' ? <Col span={12}>
              <Form.Item label="关联菜单">
                {getFieldDecorator('menu', {
                  initialValue: setting.menu || '',
@@ -217,7 +223,7 @@
                })( <TextArea rows={2}/> )}
              </Form.Item>
            </Col> : null}
            {isApp ? <Col span={12}>
            {appType === 'pc' && click !== '' && click !== 'button' ? <Col span={12}>
              <Form.Item label="打开方式">
                {getFieldDecorator('open', {
                  initialValue: setting.open || 'blank'
@@ -229,7 +235,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {click !== '' ? <Col span={12}>
            {click !== '' && click !== 'button' ? <Col span={12}>
              <Form.Item label="参数拼接">
                {getFieldDecorator('joint', {
                  initialValue: setting.joint || 'true'
src/menu/components/card/cardcomponent/settingform/index.scss
@@ -1,4 +1,4 @@
.model-menu-setting-form {
.model-menu-card-setting-form {
  position: relative;
  .anticon-question-circle {
@@ -16,4 +16,7 @@
      width: 84%;
    }
  }
  .ant-radio-wrapper {
    margin-right: 3px;
  }
}
src/menu/components/card/data-card/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -15,6 +15,7 @@
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const CardComponent = asyncComponent(() => import('../cardcomponent'))
const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
@@ -34,14 +35,15 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    appType: sessionStorage.getItem('appType'),
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    const { appType } = this.state
    if (card.isNew) {
      let ismob = sessionStorage.getItem('appType') === 'mob'
      let _card = {
        uuid: card.uuid,
        type: card.type,
@@ -56,7 +58,7 @@
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, width: card.width || 24, title: '', pagestyle: 'page', switch: 'false' },
        wrap: { name: card.name, width: card.width || 24, title: '', pagestyle: 'page', switch: 'false', cardType: '' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
@@ -66,7 +68,7 @@
        btnlog: [],
        subcards: [{
          uuid: Utils.getuuid(),
          setting: { width: ismob ? 24 : 6, type: 'simple'},
          setting: { width: appType === 'mob' ? 24 : 6, type: 'simple'},
          style: {
            borderWidth: '1px', borderColor: '#e8e8e8',
            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
@@ -192,6 +194,7 @@
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    const { appType } = this.state
    let card = fromJS(this.state.card).toJS()
    let _this = this
@@ -200,25 +203,31 @@
      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)
          }
        })
        cell.backElements && cell.backElements.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)
        if (appType === 'mob') return
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        cell.backElements && cell.backElements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -285,27 +294,27 @@
    newcard.execError = 'never'
    newcard.verify = null
    newcard.show = 'button'
    newcard.btnstyle = {marginRight: '15px'}
    newcard.style = {marginRight: '15px'}
    // 注册事件-添加按钮
    MKEmitter.emit('addButton', card.uuid, newcard)
  }
  setSubConfig = (item) => {
    const { card } = this.state
    const { card, appType } = this.state
    let btn = fromJS(item).toJS()
    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' },
          setting: { title: btn.label, width: appType === 'mob' ? 100 : 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
          tables: [],
          groups: [],
          fields: []
        }
      }
      MKEmitter.emit('changeModal', card, btn)
    } else if (btn.OpenType === 'popview') {
    } else if (btn.OpenType === 'popview' && appType !== 'mob') {
      MKEmitter.emit('changePopview', card, btn)
    }
  }
@@ -374,6 +383,54 @@
    }
  }
  addCard = () => {
    let card = fromJS(this.state.card).toJS()
    let height = card.subcards[0].style.height
    if (height === 'auto') {
      height = '100px'
    }
    let newcard = {
      uuid: Utils.getuuid(),
      $cardType: 'extendCard',
      setting: { width: 6, type: 'simple', click: 'button'},
      style: {
        height,
        borderWidth: '1px', borderColor: '#e8e8e8',
        paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
        marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
      },
      backStyle: {},
      elements: [],
      backElements: []
    }
    card.subcards.push(newcard)
    this.setState({card})
    this.props.updateConfig(card)
  }
  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})
    this.props.updateConfig(card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
@@ -382,7 +439,7 @@
  }
  render() {
    const { card } = this.state
    const { card, appType } = this.state
    let offset = 0
    if (card.wrap.cardFloat && card.wrap.cardFloat !== 'left') {
@@ -395,17 +452,19 @@
        offset = Math.floor(offset / 2)
      }
    }
    let _style = resetStyle(card.style)
    return (
      <div className="menu-data-card-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className={'menu-data-card-edit-box ' + appType} style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader defaultshow="hidden" config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" />
            {appType !== 'mob' ? <Icon className="plus" title="添加卡片" onClick={this.addCard} type="plus" /> : null}
            {appType !== 'mob' ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null}
            <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="datacard" card={card}/>
            <PasteComponent config={card} options={['action', 'search', 'form']} updateConfig={this.updateComponent} />
            <PasteComponent config={card} options={['action', 'search', 'form', 'cardcell']} updateConfig={this.updateComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <LogComponent btnlog={card.btnlog || []} handlelog={this.handleLog} />
            <UserComponent config={card}/>
@@ -415,10 +474,11 @@
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <ActionComponent config={card} setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
        {card.subcards.map((subcard, index) => (<CardComponent key={subcard.uuid} offset={!index ? offset : 0} cards={card} card={subcard} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
        <ActionComponent config={card} type="datacard" setSubConfig={this.setSubConfig} updateaction={this.updateComponent}/>
        {card.subcards.map((subcard, index) => (<CardComponent key={subcard.uuid} offset={!index ? offset : 0} cards={card} card={subcard} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
        <div style={{clear: 'both'}}></div>
        {card.wrap.pagestyle !== 'switch' && card.setting.laypage === 'true' ? <Pagination total={85} size="small" showTotal={total => `共 ${total} 条`} pageSize={20} defaultCurrent={1}/> : null}
        {card.wrap.pagestyle === 'page' && card.setting.laypage === 'true' && appType !== 'mob' ? <Pagination total={85} size="small" showTotal={total => `共 ${total} 条`} pageSize={20} defaultCurrent={1}/> : null}
        {card.wrap.pagestyle === 'page' && card.setting.laypage === 'true' && appType === 'mob' ? <MobPagination /> : null}
      </div>
    )
  }
src/menu/components/card/data-card/index.scss
@@ -75,12 +75,31 @@
      line-height: 55px;
    }
  }
  .normal-pagination {
    .am-button::before {
      display: none;
    }
    .am-button {
      border: none;
      font-size: 16px;
    }
  }
}
.menu-data-card-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-data-card-edit-box.mob {
  .model-menu-action-list {
    position: absolute;
    top: 5px;
    right: 0px;
    .page-card {
      line-height: 40px;
    }
  }
}
.menu-data-card-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
src/menu/components/card/data-card/wrapsetting/index.jsx
@@ -8,7 +8,7 @@
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
class CardWrapSetting extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
@@ -80,4 +80,4 @@
  }
}
export default DataSource
export default CardWrapSetting
src/menu/components/card/data-card/wrapsetting/settingform/index.jsx
@@ -6,14 +6,17 @@
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
    dict: PropTypes.object,
    config: PropTypes.object,
    wrap: PropTypes.object,
    inputSubmit: PropTypes.func
  }
  state = {
    roleList: []
    roleList: [],
    appType: sessionStorage.getItem('appType'),
    cardType: this.props.wrap.cardType,
    MenuType: ''
  }
  UNSAFE_componentWillMount () {
@@ -28,7 +31,13 @@
      roleList = []
    }
    this.setState({roleList})
    let MenuType = ''
    if (window.GLOB.customMenu && window.GLOB.customMenu.parentId === 'BillPrintTemp') {
      MenuType = 'billPrint'
    }
    this.setState({roleList, MenuType})
  }
  handleConfirm = () => {
@@ -55,7 +64,7 @@
  render() {
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const { roleList, MenuType, appType, cardType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -132,7 +141,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'datacard' ? <Col span={12}>
            {config.subtype === 'datacard' || (config.subtype === 'tablecard' && appType === 'mob') ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="数据源中选择分页时有效。">
                  <Icon type="question-circle" />
@@ -144,7 +153,8 @@
                })(
                  <Radio.Group>
                    <Radio value="page">页码</Radio>
                    <Radio value="switch">左右切换</Radio>
                    {appType !== 'mob' ? <Radio value="switch">左右切换</Radio> : null}
                    {appType === 'mob' ? <Radio value="slide">滑动加载</Radio> : null}
                  </Radio.Group>
                )}
              </Form.Item>
@@ -154,10 +164,22 @@
                {getFieldDecorator('cardType', {
                  initialValue: wrap.cardType || ''
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({cardType: e.target.value})}>
                    <Radio key="" value=""> 不可选 </Radio>
                    <Radio key="radio" value={'radio'}> 单选 </Radio>
                    {config.subtype !== 'propcard' ? <Radio key="checkbox" value={'checkbox'}> 多选 </Radio> : null}
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'datacard' && appType === 'mob' && cardType === 'checkbox' ? <Col span={12}>
              <Form.Item label="全选">
                {getFieldDecorator('checkAll', {
                  initialValue: wrap.checkAll || 'hidden'
                })(
                  <Radio.Group>
                    <Radio key="hidden" value="hidden"> 隐藏 </Radio>
                    <Radio key="show" value="show"> 显示 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
@@ -180,7 +202,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype !== 'tablecard' ? <Col span={12}>
            {config.subtype !== 'tablecard' && appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="鼠标悬浮于卡片上方时,卡片放大1.05倍。">
                  <Icon type="question-circle" />
@@ -197,19 +219,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'tablecard' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="表格高度,超出时滚动,高度为空时根据内容自适应。">
                  <Icon type="question-circle" />
                  高度
                </Tooltip>
              }>
                {getFieldDecorator('height', {
                  initialValue: wrap.height
                })(<InputNumber min={100} max={2000} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col> : null}
            {config.subtype === 'propcard' ? <Col span={12}>
            {config.subtype === 'propcard' && MenuType === 'billPrint' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="选择类型为《页眉/页脚》时,打印的每页里都会带有该组件。">
                  <Icon type="question-circle" />
src/menu/components/card/prop-card/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -33,15 +33,16 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,
    back: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    const { appType } = this.state
    if (card.isNew) {
      let ismob = sessionStorage.getItem('appType') === 'mob'
      let _card = {
        uuid: card.uuid,
        type: card.type,
@@ -63,7 +64,7 @@
        scripts: [],
        subcards: [{
          uuid: Utils.getuuid(),
          setting: { width: ismob ? 24 : 6, type: 'simple'},
          setting: { width: appType === 'mob' ? 24 : 6, type: 'simple'},
          style: {
            borderWidth: '1px', borderColor: '#e8e8e8',
            paddingTop: '15px', paddingBottom: '15px', paddingLeft: '15px', paddingRight: '15px',
@@ -195,6 +196,7 @@
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    const { appType } = this.state
    let card = fromJS(this.state.card).toJS()
    let _this = this
@@ -202,19 +204,6 @@
      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)
          }
        })
        cell.backElements && cell.backElements.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)
@@ -222,6 +211,25 @@
        _this.setState({card})
        _this.props.updateConfig(card)
        if (appType === 'mob') return
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        cell.backElements && cell.backElements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -373,8 +381,10 @@
      }
    }
    let _style = resetStyle(card.style)
    return (
      <div className="menu-prop-card-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-prop-card-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader defaultshow="hidden" config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
src/menu/components/card/table-card/cardcomponent/index.jsx
@@ -8,7 +8,7 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import { resetStyle } from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -163,10 +163,11 @@
  render() {
    const { cards } = this.props
    const { card, elements, settingVisible, dict } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="ant-col ant-col-24">
        <div className="card-item" style={card.style}>
        <div className="card-item" style={_style}>
          <CardCellComponent cards={cards} cardCell={card} elements={elements} updateElement={this.updateCard}/>
          <div className="card-control">
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
@@ -176,6 +177,14 @@
                <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" />
                <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
                  <div className="mk-popover-control">
                    <Icon className="plus" title="前移" type="arrow-left" onClick={() => this.props.move(card, 'left')} />
                    <Icon className="close" title="后移" type="arrow-right" onClick={() => this.props.move(card, 'right')} />
                  </div>
                } trigger="hover" getPopupContainer={() => document.getElementById(card.uuid + 'swap')}>
                  <Icon type="swap" id={card.uuid + 'swap'}/>
                </Popover>
                <Icon className="close" title="删除卡片" type="delete" onClick={() => this.props.deleteElement(card)} />
              </div>
            } trigger="hover">
src/menu/components/card/table-card/cardcomponent/settingform/index.jsx
@@ -90,6 +90,7 @@
                        {option.label}
                      </Select.Option>
                    )}
                    <Select.Option key={'index'} value={'$Index'}>序号(前端)</Select.Option>
                  </Select>
                )}
              </Form.Item>
src/menu/components/card/table-card/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -15,6 +15,7 @@
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('../data-card/wrapsetting'))
const CardComponent = asyncComponent(() => import('./cardcomponent'))
const MobPagination = asyncIconComponent(() => import('@/menu/components/share/mobPagination'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
@@ -33,6 +34,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    appType: sessionStorage.getItem('appType'),
    back: false
  }
@@ -171,6 +173,7 @@
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    const { appType } = this.state
    let card = fromJS(this.state.card).toJS()
    let _this = this
@@ -178,15 +181,6 @@
      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)
@@ -194,6 +188,20 @@
        _this.setState({card})
        _this.props.updateConfig(card)
        if (appType === 'mob') return
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -202,7 +210,7 @@
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
    MKEmitter.emit('changeStyle', [card.uuid], ['height', 'background', 'border', 'padding', 'margin'], card.style)
  }
  getStyle = (comIds, style) => {
@@ -314,6 +322,26 @@
    }
  }
  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})
    this.props.updateConfig(card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
@@ -322,15 +350,16 @@
  }
  render() {
    const { card } = this.state
    const { card, appType } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-table-card-edit-box" style={{...card.style, height: card.wrap.height}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-table-card-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加卡片" onClick={this.addCard} type="plus" />
            <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" />
            {appType !== 'mob' ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null}
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="tablecard" card={card}/>
            <PasteComponent config={card} options={['cardcell', 'search', 'form']} updateConfig={this.updateComponent} />
@@ -343,10 +372,11 @@
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <div style={{minHeight: card.wrap.height - 90}}>
          {card.subcards.map(subcard => (<CardComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
        <div style={{minHeight: 'calc(100% - 90px)'}}>
          {card.subcards.map(subcard => (<CardComponent key={subcard.uuid} cards={card} card={subcard} updateElement={this.updateCard} move={this.move} deleteElement={this.deleteCard}/>))}
        </div>
        {card.setting.laypage === 'true' ? <Pagination size="small" total={50} /> : null}
        {card.setting.laypage === 'true' && card.wrap.pagestyle !== 'slide' && appType !== 'mob' ? <Pagination size="small" total={50} /> : null}
        {card.setting.laypage === 'true' && card.wrap.pagestyle !== 'slide' && appType === 'mob' ? <MobPagination /> : null}
      </div>
    )
  }
src/menu/components/carousel/cardcomponent/index.jsx
@@ -8,7 +8,7 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import { resetStyle } from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -176,6 +176,7 @@
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = cards.style.height
    _style = resetStyle(_style)
    return (
      <div className="card-item" style={_style} onClick={this.clickComponent} id={card.uuid}>
src/menu/components/carousel/data-card/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -30,6 +30,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,
    back: false
  }
@@ -163,6 +164,7 @@
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    const { appType } = this.state
    let card = fromJS(this.state.card).toJS()
    let _this = this
@@ -171,20 +173,26 @@
      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)
        if (appType === 'mob') return
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -251,9 +259,10 @@
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-data-carousel-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-data-carousel-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent}/>
src/menu/components/carousel/data-card/wrapsetting/settingform/index.jsx
@@ -13,6 +13,7 @@
  }
  state = {
    appType: sessionStorage.getItem('appType'),
    roleList: []
  }
@@ -55,7 +56,7 @@
  render() {
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const { roleList, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -161,7 +162,19 @@
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
            {appType === 'mob' ? <Col span={12}>
              <Form.Item label="垂直显示">
                {getFieldDecorator('vertical', {
                  initialValue: wrap.vertical || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">是</Radio>
                    <Radio value="false">否</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label="指示点位置">
                {getFieldDecorator('dotPosition', {
                  initialValue: wrap.dotPosition || 'bottom'
@@ -174,8 +187,8 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
            </Col> : null}
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label="动画效果">
                {getFieldDecorator('effect', {
                  initialValue: wrap.effect || 'scrollx'
@@ -186,7 +199,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
src/menu/components/carousel/prop-card/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -31,6 +31,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,
    back: false
  }
@@ -180,6 +181,7 @@
   * @description 单个卡片信息更新
   */
  deleteCard = (cell) => {
    const { appType } = this.state
    let card = fromJS(this.state.card).toJS()
    let _this = this
@@ -187,14 +189,6 @@
      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)
@@ -202,6 +196,20 @@
        _this.setState({card})
        _this.props.updateConfig(card)
        if (appType === 'mob') return
        let uuids = []
        cell.elements && cell.elements.forEach(c => {
          if (c.eleType !== 'button' || (appType === 'pc' && c.OpenType !== 'popview')) return
          uuids.push(c.uuid)
        })
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -328,9 +336,10 @@
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-prop-carousel-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-prop-carousel-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加卡片" onClick={this.addCard} type="plus" />
src/menu/components/chart/antv-bar/chartcompile/formconfig.jsx
@@ -8,31 +8,34 @@
 * @param {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let isApp = sessionStorage.getItem('appType') === 'pc'
  let menulist = null
  // let appType = sessionStorage.getItem('appType')
  // let menulist = null
  if (isApp) {
    menulist = sessionStorage.getItem('appMenus')
  } else {
    menulist = sessionStorage.getItem('fstMenuList')
  }
  // if (appType === 'pc') {
  //   menulist = sessionStorage.getItem('appMenus')
  //   if (Array.isArray(card.linkmenu)) {
  //     card.linkmenu = ''
  //   }
  // } 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 = []
    }
  } else {
    menulist = []
  }
  // if (menulist) {
  //   try {
  //     menulist = JSON.parse(menulist)
  //     if (appType === 'pc') {
  //       menulist = menulist.map(item => {
  //         item.value = item.MenuID
  //         item.text = item.MenuName
  //         return item
  //       })
  //     }
  //   } catch {
  //     menulist = []
  //   }
  // } else {
  //   menulist = []
  // }
  let roleList = sessionStorage.getItem('sysRoles')
  if (roleList) {
@@ -91,38 +94,38 @@
      required: false,
      options: roleList
    },
    {
      type: 'cascader',
      key: 'linkmenu',
      label: '关联菜单',
      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: '当前窗口' }
      ]
    }
    // {
    //   type: 'cascader',
    //   key: 'linkmenu',
    //   label: '关联菜单',
    //   initVal: card.linkmenu || [],
    //   tooltip: '在使用柱形图且未启用自定义设置时有效。',
    //   required: false,
    //   forbid: appType === 'pc' || appType === 'mob',
    //   options: menulist
    // },
    // {
    //   type: 'select',
    //   key: 'linkmenu',
    //   label: '关联菜单',
    //   initVal: card.linkmenu || '',
    //   tooltip: '双击柱状图,会打开关联的菜单。',
    //   required: false,
    //   forbid: appType !== 'pc',
    //   options: menulist
    // },
    // {
    //   type: 'radio',
    //   key: 'open',
    //   label: '打开方式',
    //   initVal: card.open || 'blank',
    //   required: false,
    //   forbid: appType !== 'pc',
    //   options: [
    //     { value: 'blank', text: '新窗口' },
    //     { value: 'self', text: '当前窗口' }
    //   ]
    // }
  ]
}
@@ -132,6 +135,7 @@
 * @param {Array}  columns    // 显示列
 */
export function getOptionForm (card, columns) {
  let appType = sessionStorage.getItem('appType')
  let shapes = []
  if (card.chartType === 'line') {
@@ -159,28 +163,48 @@
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  let labelOptions = [{
    value: 'false',
    text: '隐藏'
  }, {
    value: 'true',
    text: '显示'
  }]
  if (card.chartType === 'bar') {
    labelOptions[1].text = '外部'
    labelOptions.push(...[{
      value: 'top',
      text: '顶部'
    }, {
      value: 'middle',
      text: '中间'
    }, {
      value: 'bottom',
      text: '底部'
    }])
  }
  return [
    {
      type: 'radio',
      key: 'datatype',
      label: '数据类型',
      initVal: card.datatype || 'query',
      tooltip: '统计图表适用于表格不分页,且数据需要转换',
      tooltip: '统计图表适用于展示数据类型为动态值。',
      required: false,
      options: [
        { value: 'query', text: Formdict['header.form.query'] },
        { value: 'statistics', text: Formdict['header.form.statistics'] }
      ]
    },
    {
    }, {
      type: 'select',
      key: 'Xaxis',
      label: 'X-轴',
      initVal: card.Xaxis || '',
      required: true,
      options: xfields
    },
    {
    }, {
      type: 'select',
      key: 'InfoType',
      label: '类型',
@@ -188,8 +212,7 @@
      hidden: card.datatype !== 'statistics',
      required: true,
      options: xfields
    },
    {
    }, {
      type: 'select',
      key: 'InfoValue',
      label: '值',
@@ -197,8 +220,7 @@
      hidden: card.datatype !== 'statistics',
      required: true,
      options: yfields
    },
    {
    }, {
      type: 'select',
      key: 'legend',
      label: '图例位置',
@@ -219,8 +241,7 @@
        { field: 'left-bottom', label: '左下' },
        { field: 'hidden', label: '隐藏' }
      ]
    },
    {
    }, {
      type: 'select',
      key: 'Yaxis',
      label: 'Y-轴',
@@ -229,16 +250,14 @@
      hidden: card.datatype === 'statistics',
      required: true,
      options: yfields
    },
    {
    }, {
      type: 'select',
      key: 'shape',
      label: '形状',
      initVal: card.shape || (shapes[0] && shapes[0].field),
      required: false,
      options: shapes
    },
    {
    }, {
      type: 'radio',
      key: 'tooltip',
      label: '悬浮提示',
@@ -251,8 +270,7 @@
        value: 'false',
        text: '隐藏'
      }]
    },
    {
    }, {
      type: 'radio',
      key: 'point',
      label: '点图',
@@ -266,8 +284,7 @@
        value: 'false',
        text: '隐藏'
      }]
    },
    {
    }, {
      type: 'radio',
      key: 'transpose',
      label: '变换',
@@ -281,8 +298,7 @@
        value: 'false',
        text: Formdict['model.false']
      }]
    },
    {
    }, {
      type: 'radio',
      key: 'show',
      label: '格式化',
@@ -295,20 +311,41 @@
        value: 'percent',
        text: '百分比'
      }]
    },
    {
      type: 'radio',
    }, {
      type: labelOptions.length > 20 ? 'select' : 'radio',
      key: 'label',
      label: '标注值',
      label: '标签',
      initVal: card.label || 'false',
      required: false,
      options: labelOptions
    }, {
      type: 'radio',
      key: 'labelColor',
      label: '标签颜色',
      initVal: card.labelColor || 'system',
      tooltip: '使用系统色时,使用色系选项设置的系统颜色,使用自定义为颜色设置中定义的图形颜色。',
      required: false,
      options: [{
        value: 'true',
        text: '显示'
        value: 'system',
        text: '系统'
      }, {
        value: 'false',
        text: '隐藏'
        value: 'custom',
        text: '自定义'
      }]
    // }, {
    //   type: 'radio',
    //   key: 'offset',
    //   label: '标注位置',
    //   initVal: card.offset || 'outer',
    //   required: false,
    //   options: [{
    //     value: 'outer',
    //     text: '外部'
    //   }, {
    //     value: 'inner',
    //     text: '内部'
    //   }],
    //   forbid: card.chartType !== 'bar'
    }, {
      type: 'radio',
      key: 'adjust',
@@ -322,6 +359,21 @@
      }, {
        value: 'stack',
        text: '堆叠'
      }]
    }, {
      type: 'radio',
      key: 'area',
      label: '面积图',
      initVal: card.area || 'false',
      // tooltip: '仅在形状为smooth时有效。',
      required: false,
      forbid: ['bar'].includes(card.chartType),
      options: [{
        value: 'true',
        text: '显示'
      }, {
        value: 'false',
        text: '不显示'
      }]
    }, {
      type: 'radio',
@@ -353,16 +405,32 @@
        text: '极坐标'
      }]
    }, {
      type: 'number',
      key: 'InfoDefNumber',
      label: '展示数',
      tooltip: '默认显示类型数量',
      min: 1,
      max: 50,
      decimal: 0,
      initVal: card.InfoDefNumber || 5,
      hidden: card.datatype !== 'statistics',
      required: true
      type: 'radio',
      key: 'grid',
      label: '网格线',
      initVal: card.grid || 'show',
      required: false,
      options: [{
        value: 'show',
        text: '显示'
      }, {
        value: 'hidden',
        text: '隐藏'
      }]
    }, {
      type: 'radio',
      key: 'y_line',
      label: 'y轴边线',
      initVal: card.y_line || 'hidden',
      tooltip: '图形左侧或右侧的边线。',
      required: false,
      options: [{
        value: 'show',
        text: '显示'
      }, {
        value: 'hidden',
        text: '隐藏'
      }]
    }, {
      type: 'number',
      key: 'barSize',
@@ -375,19 +443,87 @@
      forbid: !['bar'].includes(card.chartType),
      required: false
    }, {
      type: 'number',
      key: 'barRadius',
      label: '柱形圆角',
      tooltip: '柱形图上端圆角。',
      min: 0,
      max: 200,
      decimal: 0,
      initVal: card.barRadius || 0,
      forbid: !['bar'].includes(card.chartType),
      required: false
    }, {
      type: 'number',
      key: 'min',
      label: '最小值',
      tooltip: 'y轴最小值,为空时自适应。',
      initVal: card.min,
      required: false
    }, {
      type: 'number',
      key: 'max',
      label: '最大值',
      tooltip: 'y轴最大值,为空时自适应。',
      initVal: card.max,
      required: false
    }, {
      type: 'color',
      key: 'color',
      label: '色系',
      initVal: card.color || 'rgba(0, 0, 0, 0.85)',
      tooltip: '坐标轴及示例等提示文字使用的颜色。',
      initVal: card.color || 'rgba(0, 0, 0, 0.65)',
      tooltip: '坐标轴提示文字及示例的颜色。',
      required: false
    }, {
      type: 'color',
      key: 'lineColor',
      label: '轴线颜色',
      initVal: card.lineColor,
      tooltip: '坐标轴线的颜色,包括x轴、y轴及网格线。',
      allowClear: true,
      required: false
    }, {
      type: 'color',
      key: 'selectColor',
      label: '选中颜色',
      initVal: card.selectColor || '',
      tooltip: '选中柱形图的颜色,在交互效果《元素选中(多选)》和《元素选中(单选)》中有效,自定义设置中无效。',
      forbid: !['bar'].includes(card.chartType),
      allowClear: true,
      required: false
    }, {
      type: 'number',
      key: 'rotate',
      label: '旋转',
      tooltip: '坐标轴标注文本的旋转角度。',
      min: 0,
      max: 360,
      decimal: 0,
      initVal: card.rotate,
      forbid: appType !== 'mob',
      required: false
    }, {
      type: 'select',
      key: 'interaction',
      label: '交互效果',
      initVal: card.interaction || [],
      multi: true,
      required: false,
      options: [{
        value: 'black',
        text: '黑色'
      }, {
        value: 'white',
        text: '白色'
      }]
      forbid: appType === 'mob',
      options: [
        { value: 'element-active', label: '元素聚焦' },
        { value: 'element-selected', label: '元素选中(多选)' },
        { value: 'element-single-selected', label: '元素选中(单选)' },
        { value: 'active-region', label: '背景框' },
        { value: 'view-zoom', label: '视图缩放' },
        { value: 'element-highlight', label: '元素高亮' },
        { value: 'element-highlight-by-color', label: '同色元素高亮' },
        { value: 'element-highlight-by-x', label: '同X轴元素高亮' },
        { value: 'legend-filter', label: '图例过滤' },
        { value: 'legend-active', label: '图例聚焦' },
        { value: 'legend-highlight', label: '图例高亮' },
        { value: 'brush', label: '选框过滤' },
      ]
    }
  ]
}
src/menu/components/chart/antv-bar/chartcompile/index.jsx
@@ -24,6 +24,7 @@
  state = {
    view: 'normal',
    ramp: 'false',
    visible: false,
    datatype: '',
    plot: null,
@@ -48,6 +49,34 @@
        }
      },
    ],
    rampColorColumns: [
      {
        title: '指标',
        dataIndex: 'label',
        editable: false,
        width: '20%'
      },
      {
        title: '颜色1',
        dataIndex: 'color',
        inputType: 'color',
        editable: true,
        width: '30%',
        render: (text, record) => {
          return (<div style={{width: '80px', height: '23px', background: text}}></div>)
        }
      },
      {
        title: '颜色2',
        dataIndex: 'color1',
        inputType: 'color',
        editable: true,
        width: '30%',
        render: (text, record) => {
          return (<div style={{width: '80px', height: '23px', background: text}}></div>)
        }
      },
    ],
    statColorColumns: [
      {
        title: '指标',
@@ -67,19 +96,48 @@
        }
      },
    ],
    rampStatColorColumns: [
      {
        title: '指标',
        dataIndex: 'type',
        inputType: 'input',
        editable: true,
        width: '20%'
      },
      {
        title: '颜色1',
        dataIndex: 'color',
        inputType: 'color',
        editable: true,
        width: '30%',
        render: (text, record) => {
          return (<div style={{width: '80px', height: '23px', background: text}}></div>)
        }
      },
      {
        title: '颜色2',
        dataIndex: 'color1',
        inputType: 'color',
        editable: true,
        width: '30%',
        render: (text, record) => {
          return (<div style={{width: '80px', height: '23px', background: text}}></div>)
        }
      },
    ],
    cusColumns: [
      {
        title: '指标',
        dataIndex: 'name',
        editable: false,
        width: '20%'
        width: '14%'
      },
      {
        title: '形状',
        dataIndex: 'shape',
        inputType: 'cascader',
        editable: true,
        width: '20%',
        width: '12%',
        render: (text, record) => {
          return text.join(' / ').replace('line', '折线').replace('bar', '柱形')
        },
@@ -117,7 +175,7 @@
        dataIndex: 'axis',
        inputType: 'select',
        editable: true,
        width: '20%',
        width: '12%',
        options: [
          { value: 'true', text: '显示'},
          { value: 'false', text: '隐藏'}
@@ -132,7 +190,7 @@
        dataIndex: 'label',
        inputType: 'select',
        editable: true,
        width: '20%',
        width: '12%',
        options: [
          { value: 'true', text: '显示'},
          { value: 'false', text: '隐藏'}
@@ -141,6 +199,37 @@
          let trans = {'true': '显示', 'false': '隐藏'}
          return trans[text] || '隐藏'
        }
      },
      {
        title: '标题',
        dataIndex: 'title',
        inputType: 'select',
        editable: true,
        width: '12%',
        options: [
          { value: 'true', text: '显示'},
          { value: 'false', text: '隐藏'}
        ],
        render: (text, record) => {
          let trans = {'true': '显示', 'false': '隐藏'}
          return trans[text] || '显示'
        }
      },
      {
        title: '最小值',
        dataIndex: 'min',
        inputType: 'number',
        editable: true,
        required: false,
        width: '12%'
      },
      {
        title: '最大值',
        dataIndex: 'max',
        inputType: 'number',
        editable: true,
        required: false,
        width: '12%'
      },
    ]
  }
@@ -163,6 +252,7 @@
    this.setState({
      visible: true,
      view: 'normal',
      ramp: config.plot.ramp || 'false',
      datatype: config.plot.datatype || 'query',
      fieldName: fieldName,
      plot: fromJS(config.plot).toJS(),
@@ -181,7 +271,7 @@
        formlist: formlist.map(item => {
          if (['Yaxis'].includes(item.key)) {
            item.hidden = val === 'statistics'
          } else if (['InfoType', 'InfoValue', 'InfoDefNumber'].includes(item.key)) {
          } else if (['InfoType', 'InfoValue'].includes(item.key)) {
            item.hidden = val !== 'statistics'
          }
          return item
@@ -291,7 +381,7 @@
                  }
                ]
              })(
                <Radio.Group disabled={item.readonly} onChange={(e) => this.radioChange(e, item.key)}>
                <Radio.Group style={{whiteSpace: 'nowrap'}} disabled={item.readonly} onChange={(e) => this.radioChange(e, item.key)}>
                  {item.options.map(option => {
                    return (
                      <Radio key={option.value} value={option.value}>{option.text}</Radio>
@@ -314,7 +404,7 @@
              {getFieldDecorator(item.key, {
                initialValue: item.initVal
              })(
                <ColorSketch />
                <ColorSketch allowClear={item.allowClear} />
              )}
            </Form.Item>
          </Col>
@@ -329,6 +419,35 @@
    let val = e.target.value
    this.setState({plot: {...plot, enabled: val}})
  }
  mutilBarChange = (e) => {
    const { plot } = this.state
    let val = e.target.value
    this.setState({plot: {...plot, mutilBar: val}})
  }
  rampChange = (e) => {
    const { plot } = this.state
    let val = e.target.value
    let colors = plot.colors || []
    if (val === 'true') {
      colors = colors.map(item => {
        item.color1 = item.color1 || item.color
        return item
      })
    }
    this.setState({plot: {...plot, colors, ramp: val}, ramp: val})
  }
  rampDirectionChange = (e) => {
    const { plot } = this.state
    let val = e.target.value
    this.setState({plot: {...plot, rampDirection: val}})
  }
  onSubmit = () => {
@@ -411,6 +530,7 @@
                name: labels[item] || item,
                axis: i === 0 ? 'true' : 'false',
                label: 'false',
                title: 'true',
                shape: _plot.chartType === 'bar' && i === 0 ? ['bar', 'rect'] : ['line', 'smooth']
              }
            })
@@ -425,7 +545,8 @@
                  uuid: Utils.getuuid(),
                  type: item,
                  label: labels[item] || item,
                  color: chartColors[i % limit]
                  color: chartColors[i % limit],
                  color1: chartColors[i % limit]
                }
              })
            }
@@ -459,7 +580,8 @@
    plot.colors.push({
      uuid: Utils.getuuid(),
      type: `指标${plot.colors.length}`,
      color: 'rgb(91, 143, 249)'
      color: 'rgb(91, 143, 249)',
      color1: 'rgb(91, 143, 249)'
    })
    this.setState({plot})
@@ -478,7 +600,7 @@
  }
  render() {
    const { view, visible, datatype, plot, colorColumns, statColorColumns, cusColumns, baseFormlist } = this.state
    const { view, visible, datatype, plot, ramp, colorColumns, rampColorColumns, statColorColumns, rampStatColorColumns, cusColumns, baseFormlist } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -497,7 +619,7 @@
          wrapClassName="popview-modal menu-chart-edit-modal"
          title="图表编辑"
          visible={visible}
          width={850}
          width={950}
          maskClosable={false}
          onOk={this.onSubmit}
          onCancel={() => { this.setState({ visible: false }) }}
@@ -514,9 +636,29 @@
            </TabPane>
            {plot ? <TabPane tab="颜色设置" key="color">
              <div>
                <Col span={8} style={{height: '40px', top: '-5px', zIndex: 1}}>
                  <Form {...formItemLayout}>
                    <Form.Item label="渐变色" style={{marginBottom: 10}}>
                      <Radio.Group value={plot.ramp || 'false'} onChange={this.rampChange}>
                        <Radio value="false">不使用</Radio>
                        <Radio value="true">使用</Radio>
                      </Radio.Group>
                    </Form.Item>
                  </Form>
                </Col>
                {plot.chartType === 'line' ? <Col span={8} style={{height: '40px', top: '-5px', zIndex: 1}}>
                  <Form {...formItemLayout}>
                    <Form.Item label="渐变方向" style={{marginBottom: 10}}>
                      <Radio.Group value={plot.rampDirection || 'horizontal'} onChange={this.rampDirectionChange}>
                        <Radio value="horizontal">水平</Radio>
                        <Radio value="vertical">垂直</Radio>
                      </Radio.Group>
                    </Form.Item>
                  </Form>
                </Col> : null}
                {datatype === 'statistics' ? <Button className="color-add mk-green" onClick={this.addColor}>{this.props.dict['model.add']}</Button> : null}
                {datatype === 'statistics' ? <EditTable actions={['edit', 'move', 'del']} data={plot.colors || []} columns={statColorColumns} onChange={this.changeColor}/> : null}
                {datatype !== 'statistics' ? <EditTable actions={['edit']} data={plot.colors || []} columns={colorColumns} onChange={this.changeColor}/> : null}
                {datatype === 'statistics' ? <EditTable actions={['edit', 'move', 'del']} data={plot.colors || []} columns={ramp ==='true' ? rampStatColorColumns : statColorColumns} onChange={this.changeColor}/> : null}
                {datatype !== 'statistics' ? <EditTable actions={['edit']} data={plot.colors || []} columns={ramp ==='true' ? rampColorColumns : colorColumns} onChange={this.changeColor}/> : null}
              </div>
            </TabPane> : null}
            {plot ? <TabPane tab="自定义设置" disabled={datatype === 'statistics'} key="custom">
@@ -530,7 +672,18 @@
                  </Form.Item>
                </Form>
              </Col>
              <Col style={{fontSize: '12px', color: '#757575', paddingLeft: '10px'}} span={24}>注:使用自定义设置时,显示的坐标轴第一个在左侧,第二个在右侧,多余的不生效;柱形图只可以添加一个(设置多个时,第一个生效)。</Col>
              <Col span={12}>
                <Form {...formItemLayout}>
                  <Form.Item label="多柱排列" style={{marginBottom: 10}}>
                    <Radio.Group value={plot.mutilBar || 'dodge'} onChange={this.mutilBarChange}>
                      <Radio value="dodge">分组</Radio>
                      <Radio value="stack">堆叠</Radio>
                      <Radio value="overlap">重叠</Radio>
                    </Radio.Group>
                  </Form.Item>
                </Form>
              </Col>
              <Col style={{fontSize: '12px', color: '#757575', paddingLeft: '10px'}} span={24}>注:使用自定义设置时,显示的坐标轴第一个在左侧,第二个在右侧,多余的不生效。</Col>
              <EditTable actions={['edit', 'move']} data={plot.customs || []} columns={cusColumns} onChange={this.changeCustom}/>
            </TabPane> : null}
          </Tabs>
src/menu/components/chart/antv-bar/chartcompile/index.scss
@@ -20,6 +20,9 @@
        .ant-input-number {
          width: 100%;
        }
        .ant-radio-wrapper {
          margin-right: 5px;
        }
        .ant-tabs-nav-wrap {
          text-align: center;
        }
src/menu/components/chart/antv-bar/index.jsx
@@ -8,7 +8,7 @@
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import { chartColors } from '@/utils/option.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -35,6 +35,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    appType: sessionStorage.getItem('appType'),
    eventListener: null
  }
@@ -50,6 +51,7 @@
        width: card.width || 24,
        height: 400,
        barSize: 35,
        color: 'rgba(0, 0, 0, 0.65)',
        name: card.name
      }
@@ -73,7 +75,7 @@
        parentId: card.parentId || '',
        format: 'array',   // 组件属性 - 数据格式
        pageable: false,   // 组件属性 - 是否可分页
        switchable: false, // 组件属性 - 数据是否可切换
        switchable: card.type === 'bar' ? true : false, // 组件属性 - 数据是否可切换
        dataName: card.dataName || '',
        width: _plot.width,
        name: _plot.name,
@@ -121,9 +123,11 @@
  }
  componentDidMount () {
    this.viewrender()
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    setTimeout(() => {
      this.viewrender()
    }, 1000)
  }
  shouldComponentUpdate (nextProps, nextState) {
@@ -150,7 +154,8 @@
        _element.innerHTML = ''
      }
      setTimeout(this.viewrender, 100)
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(this.viewrender, 100)
    }
  }
@@ -193,10 +198,14 @@
   */
  linerender = () => {
    const { card } = this.state
    let plot = {...card.plot, height: card.plot.height - 80} // 去除title所占空间
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
    let plot = {...card.plot} // 去除title所占空间
    let color = plot.color || 'rgba(0, 0, 0, 0.65)'
    let X_axis = plot.Xaxis || 'x'
    let Y_axis = plot.Yaxis || ['y']
    if (card.plot.title || card.search.length > 0) {
      plot.height = card.plot.height - 70
    }
    let data = this.getdata(X_axis, Y_axis)
@@ -235,8 +244,29 @@
  
      chart.data(dv.rows)
      chart.axis(X_axis, { label: { style: { fill: color } }, line: { style: { fill: color } } })
      chart.axis('value', { grid: { style: { fill: color } }, label: { style: { fill: color } } })
      // chart.axis(X_axis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } })
      // chart.axis('value', { grid: { line: { style: { stroke: color } }}, label: { style: { fill: color } } })
      let xc = {label: { style: { fill: color } }}
      let yc = {label: { style: { fill: color } }}
      if (plot.grid === 'hidden') {
        yc.grid = null
      }
      if (plot.y_line === 'show') {
        yc.line = {style: { stroke: '#D1D2CE' }}
      }
      if (plot.lineColor) {
        xc.tickLine = {style: { stroke: plot.lineColor }}
        xc.line = { style: { stroke: plot.lineColor } }
        if (yc.grid !== null) {
          yc.grid = { line: { style: { stroke: plot.lineColor } }}
        }
        if (yc.line) {
          yc.line = { style: { stroke: plot.lineColor } }
        }
      }
      chart.axis(X_axis, xc)
      chart.axis('value', yc)
  
      if (plot.coordinate !== 'polar') {
        chart.scale(X_axis, {
@@ -245,7 +275,7 @@
      }
      chart.scale('value', {
        nice: true,
        range: [0, 0.93]
        range: [0, 0.9]
      })
  
      if (!plot.legend || plot.legend === 'hidden') {
@@ -280,11 +310,23 @@
      let colorIndex = 0
      if (plot.colors && plot.colors.length > 0) {
        plot.colors.forEach(item => {
          if (!colors.has(transfield[item.type])) {
            colors.set(transfield[item.type], item.color)
        if (plot.ramp === 'true') {
          let _s = 'l(0) '
          if (plot.rampDirection === 'vertical') {
            _s = 'l(90) '
          }
        })
          plot.colors.forEach(item => {
            if (!colors.has(transfield[item.type])) {
              colors.set(transfield[item.type], `${_s}0:${item.color} 1:${item.color1}` )
            }
          })
        } else {
          plot.colors.forEach(item => {
            if (!colors.has(transfield[item.type])) {
              colors.set(transfield[item.type], item.color)
            }
          })
        }
      }
  
      let _chart = chart
@@ -304,25 +346,33 @@
      if (plot.colors && plot.colors.length > 0) {
        let limit = chartColors.length
        _chart.color('key', (key) => {
          if (colors.get(key)) {
          if (colors.has(key)) {
            if (plot.area === 'true' && plot.rampDirection === 'vertical') {
              return colors.get(key).replace(/l\(9?0\) 0:|\s1:.*/ig, '')
            }
            return colors.get(key)
          } else {
            colors.set(key, chartColors[colorIndex % limit])
            colorIndex++
            return chartColors[(colorIndex - 1) % limit]
          }
        })
      } else {
        _chart.color('key')
      }
      if (plot.label === 'true') {
        _chart.label('value', (value) => {
      if (plot.label !== 'false') {
        _chart.label('value*key', (value, key) => {
          if (plot.show === 'percent') {
            value = value + '%'
          }
          let _color = color
          if (plot.labelColor === 'custom' && colors.has(key)) {
            _color = colors.get(key)
          }
          return {
            content: value,
            style: {
              fill: color
              fill: _color
            }
          }
        })
@@ -336,6 +386,32 @@
          .size(3)
          .shape('circle')
      }
      if (plot.area === 'true') {
        let area = chart.area().position(`${X_axis}*value`).tooltip(false)
        if (plot.shape === 'smooth') {
          area.shape('smooth')
        }
        if (plot.colors && plot.colors.length > 0) {
          let limit = chartColors.length
          area.color('key', (key) => {
            if (colors.has(key)) {
              return colors.get(key)
            } else {
              colorIndex++
              return chartColors[(colorIndex - 1) % limit]
            }
          })
        } else {
          area.color('key')
        }
      }
      if (plot.interaction && plot.interaction.length) {
        plot.interaction.forEach(t => {
          chart.interaction(t)
        })
      }
      chart.render()
    } else {
      this.customrender(data)
@@ -347,11 +423,16 @@
   */
  customrender = (data) => {
    let card = fromJS(this.state.card).toJS()
    let plot = {...card.plot, height: card.plot.height - 80} // 去除title所占空间
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
    let plot = {...card.plot} // 去除title所占空间
    let color = plot.color || 'rgba(0, 0, 0, 0.65)'
    let fields = []
    let legends = []
    let transfield = {}
    let Bar_axis = []
    if (card.plot.title || card.search.length > 0) {
      plot.height = card.plot.height - 70
    }
    card.columns.forEach(col => {
      if (col.field) {
@@ -364,41 +445,90 @@
    let limit = chartColors.length
    if (plot.colors && plot.colors.length > 0) {
      plot.colors.forEach(item => {
        if (!colors.has(item.type)) {
          colors.set(item.type, item.color)
        }
      })
      if (plot.ramp === 'true') {
        let bars = {}
        plot.customs.forEach(item => {
          if (!item.shape || !item.shape[0] || item.shape[0] === 'bar') {
            bars[item.type] = true
          }
        })
        plot.colors.forEach(item => {
          if (!colors.has(transfield[item.type])) {
            if (bars[item.type]) {
              colors.set(transfield[item.type], `l(90) 0:${item.color} 1:${item.color1}` )
            } else {
              colors.set(transfield[item.type], `l(0) 0:${item.color} 1:${item.color1}` )
            }
          }
        })
      } else {
        plot.colors.forEach(item => {
          if (!colors.has(transfield[item.type])) {
            colors.set(transfield[item.type], item.color)
          }
        })
      }
    }
    let axisIndex = 0
    let hasBar = false
    plot.$paddingLeft = 30
    plot.$paddingRight = 30
    plot.customs.forEach(item => {
      item.name = transfield[item.type] || item.type
      item.chartType = item.shape ? (item.shape[0] || 'bar') : 'bar'
      item.shape = item.shape ? (item.shape[1] || '') : ''
      if (colors.get(item.type)) {
        item.color = colors.get(item.type)
      if (colors.has(item.name)) {
        item.color = colors.get(item.name)
      } else {
        item.color = chartColors[colorIndex % limit]
        colorIndex++
      }
      if (item.chartType === 'bar' && !hasBar) {
      if (item.chartType === 'bar') {
        Bar_axis.push(item.type)
        hasBar = true
      } else if (item.chartType === 'bar') {
        item.chartType = 'line'
        item.shape = 'smooth'
      }
      if (item.axis === 'true' && axisIndex < 2) {
        if (axisIndex === 0) {
          item.axis = { grid: {style: { fill: color }}, title: { style: { fill: color } }, label: {style: { fill: color }} }
          // item.axis = { grid: {line: { style: { stroke: color } }}, title: { style: { fill: color } }, label: {style: { fill: color }} }
          item.axis = { label: {style: { fill: color }} }
          if (item.title !== 'false') {
            item.axis.title = { style: { fill: color } }
            plot.$paddingLeft = 50
          }
          if (plot.grid === 'hidden') {
            item.axis.grid = null
          }
          if (plot.y_line === 'show') {
            item.axis.line = {style: { stroke: '#D1D2CE' }}
          }
          if (plot.lineColor) {
            if (item.axis.grid !== null) {
              item.axis.grid = { line: { style: { stroke: plot.lineColor } }}
            }
            if (item.axis.line) {
              item.axis.line = { style: { stroke: plot.lineColor } }
            }
          }
          fields.unshift(item)
        } else {
          item.axis = { grid: null, title: {style: { fill: color }}, label: {style: { fill: color }} }
          item.axis = { grid: null, label: {style: { fill: color }} }
          if (item.title !== 'false') {
            item.axis.title = { style: { fill: color } }
            plot.$paddingRight = 60
          }
          if (plot.y_line === 'show') {
            item.axis.line = {style: { stroke: '#D1D2CE' }}
          }
          if (plot.lineColor && item.axis.line) {
            item.axis.line = { style: { stroke: plot.lineColor } }
          }
          fields.splice(1, 0, item)
        }
        axisIndex++
@@ -426,15 +556,29 @@
      }
    })
    let padding = [10, 30, 30, 30]
    if (plot.mutilBar === 'overlap') {
      Bar_axis = []
    }
    if (!Bar_axis.length) {
      padding = [10, plot.$paddingRight, 30, plot.$paddingLeft]
    }
    const chart = new Chart({
      container: card.uuid + 'canvas',
      autoFit: true,
      height: plot.height || 400
      height: plot.height || 400,
    })
    chart.data(dv.rows)
    // chart.axis(plot.Xaxis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } })
    chart.axis(plot.Xaxis, { label: { style: { fill: color } }, line: { style: { fill: color } } })
    let xc = {label: { style: { fill: color } }}
    if (plot.lineColor) {
      xc.tickLine = {style: { stroke: plot.lineColor }}
      xc.line = { style: { stroke: plot.lineColor } }
    }
    chart.axis(plot.Xaxis, xc)
    if (!hasBar) {
      chart.scale(plot.Xaxis, {
@@ -465,16 +609,211 @@
      nice: true
    })
    fields.forEach(item => {
      chart.axis(item.name, item.axis)
      chart.scale(item.name, {
        nice: true,
        range: [0, 0.93]
    let lablecfg = {
      position: 'top',
      offset: 2,
      style: {
        fill: '#fff'
      }
    }
    if (plot.label === 'top') {
      lablecfg.offset = -5
      lablecfg.style.textBaseline = 'top'
    } else if (plot.label === 'middle') {
      lablecfg.position = 'middle'
      lablecfg.offset = 0
    } else if (plot.label === 'bottom') {
      lablecfg.position = 'bottom'
      lablecfg.offset = 0
    } else if (plot.label === 'true') {
      lablecfg.style.fill = color
    }
    if (Bar_axis.length) {
      const view1 = chart.createView({
        region: {
          start: { x: 0, y: 0 },
          end: { x: 1, y: 1 }
        },
        padding
      })
      const dst = new DataSet()
      const dvt = dst.createView().source(data)
      dvt.transform({
        type: 'fold',
        fields: [...Bar_axis],
        key: 'key',
        value: 'value'
      })
      dvt.transform({
        type: 'map',
        callback(row) {
          row.key = transfield[row.key] || row.key
          return row
        },
      })
      if (item.chartType === 'bar') {
        let _chart = chart
      view1.data(dvt.rows)
      view1.scale('value', {
        nice: true,
        range: [0, 0.9]
      })
      let yc = {label: { style: { fill: color } }}
      if (plot.grid === 'hidden') {
        yc.grid = null
      }
      if (plot.y_line === 'show') {
        yc.line = {style: { stroke: '#D1D2CE' }}
      }
      if (plot.lineColor) {
        if (yc.grid !== null) {
          yc.grid = { line: { style: { stroke: plot.lineColor } }}
        }
        if (yc.line) {
          yc.line = { style: { stroke: plot.lineColor } }
        }
      }
      view1.axis('value', yc)
      view1.legend(false)
      if (plot.mutilBar !== 'stack') {
        let _chart = view1
          .interval()
          .position(`${plot.Xaxis}*value`)
          .adjust([
            {
              type: 'dodge',
              marginRatio: 0
            }
          ])
          .shape(plot.shape || 'rect')
          .tooltip(`${plot.Xaxis}*value*key`, (name, value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            return {
              name: key,
              value: value
            }
          })
        if (plot.colors && plot.colors.length > 0) {
          let limit = chartColors.length
          _chart.color('key', (key) => {
            if (colors.has(key)) {
              return colors.get(key)
            } else {
              colorIndex++
              return chartColors[(colorIndex - 1) % limit]
            }
          })
        } else {
          _chart.color('key')
        }
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            if (plot.label === 'true' && plot.labelColor === 'custom' && colors.has(key)) {
              lablecfg.style.fill = colors.get(key)
            }
            return {
              content: value,
              ...lablecfg
            }
          })
        }
        if (plot.barSize || plot.correction) {
          _chart.size(plot.barSize || 35)
        }
        if (plot.barRadius) {
          _chart.style({ radius: [plot.barRadius, plot.barRadius, 0, 0] })
        }
      } else if (plot.mutilBar === 'stack') {
        let _chart = view1
          .interval()
          .position(`${plot.Xaxis}*value`)
          .adjust('stack')
          .shape(plot.shape || 'rect')
          .tooltip(`${plot.Xaxis}*value*key`, (name, value, type) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            return {
              name: type,
              value: value
            }
          })
        if (plot.colors && plot.colors.length > 0) {
          let limit = chartColors.length
          _chart.color('key', (key) => {
            if (colors.has(key)) {
              return colors.get(key)
            } else {
              colorIndex++
              return chartColors[(colorIndex - 1) % limit]
            }
          })
        } else {
          _chart.color('key')
        }
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            if (plot.label === 'true' && plot.labelColor === 'custom' && colors.has(key)) {
              lablecfg.style.fill = colors.get(key)
            }
            return {
              content: value,
              ...lablecfg
            }
          })
        }
        if (plot.barSize || plot.correction) {
          _chart.size(plot.barSize || 35)
        }
        if (plot.barRadius) {
          _chart.style({ radius: [plot.barRadius, plot.barRadius, 0, 0] })
        }
      }
    }
    const view2 = chart.createView({
      region: {
        start: { x: 0, y: 0 },
        end: { x: 1, y: 1 }
      },
      padding
    })
    view2.data(dv.rows)
    view2.legend(false)
    fields.forEach(item => {
      if (item.chartType === 'bar' && !Bar_axis.length) {
        view2.axis(item.name, item.axis)
        view2.scale(item.name, {
          nice: true,
          range: [0, 0.9]
        })
        let _chart = view2
          .interval()
          .position(`${plot.Xaxis}*${item.name}`)
          .color(item.color)
@@ -492,21 +831,35 @@
        if (plot.barSize) {
          _chart.size(plot.barSize || 35)
        }
        if (item.label === 'true') {
        if (item.label !== 'false') {
          _chart.label(item.name, (value) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            if (plot.label === 'true' && plot.labelColor === 'custom') {
              lablecfg.style.fill = item.color
            }
            return {
              content: value,
              style: {
                fill: color
              }
              ...lablecfg
            }
          })
        }
        if (plot.barRadius) {
          _chart.style({ radius: [plot.barRadius, plot.barRadius, 0, 0] })
        }
      } else if (item.chartType === 'line') {
        let _chart = chart
        if (!Bar_axis.length) {
          view2.axis(item.name, item.axis)
        } else {
          view2.axis(item.name, { grid: null, title: null, label: null })
        }
        view2.scale(item.name, {
          nice: true,
          range: [0, 0.9]
        })
        let _chart = view2
          .line()
          .position(`${plot.Xaxis}*${item.name}`)
          .color(item.color)
@@ -521,15 +874,20 @@
            }
          })
        if (item.label === 'true') {
        if (item.label !== 'false') {
          _chart.label(item.name, (value) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            let _color = color
            if (plot.labelColor === 'custom') {
              _color = item.color
            }
            return {
              content: value,
              style: {
                fill: color
                fill: _color
              }
            }
          })
@@ -546,6 +904,14 @@
      }
    })
    if (plot.interaction && plot.interaction.length) {
      plot.interaction.forEach(t => {
        if (t === 'element-active' || t === 'element-highlight') {
          chart.interaction(t)
        }
      })
    }
    chart.render()
  }
@@ -554,10 +920,14 @@
   */
  barrender = () => {
    const { card } = this.state
    let plot = {...card.plot, height: card.plot.height - 80}
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
    let plot = {...card.plot}
    let color = plot.color || 'rgba(0, 0, 0, 0.65)'
    let X_axis = plot.Xaxis || 'x'
    let Y_axis = plot.Yaxis || ['y']
    if (card.plot.title || card.search.length > 0) {
      plot.height = card.plot.height - 70
    }
    let data = this.getdata(X_axis, Y_axis)
    
@@ -594,15 +964,36 @@
        autoFit: true,
        height: plot.height || 400
      })
      chart.data(dv.rows)
      chart.axis(X_axis, { label: { style: { fill: color } }, line: { style: { fill: color } } })
      chart.axis('value', { grid: { style: { fill: color } }, label: { style: { fill: color } } })
      // chart.axis(X_axis, { label: { style: { fill: color } }, tickLine: {style: { stroke: color }}, line: { style: { stroke: color } } })
      // chart.axis('value', { grid: { line: { style: { stroke: color } }}, label: { style: { fill: color } } })
      let xc = {label: { style: { fill: color } }}
      let yc = {label: { style: { fill: color } }}
      if (plot.grid === 'hidden') {
        yc.grid = null
      }
      if (plot.y_line === 'show') {
        yc.line = {style: { stroke: '#D1D2CE' }}
      }
      if (plot.lineColor) {
        xc.tickLine = {style: { stroke: plot.lineColor }}
        xc.line = { style: { stroke: plot.lineColor } }
        if (yc.grid !== null) {
          yc.grid = { line: { style: { stroke: plot.lineColor } }}
        }
        if (yc.line) {
          yc.line = { style: { stroke: plot.lineColor } }
        }
      }
      chart.axis(X_axis, xc)
      chart.axis('value', yc)
  
      chart.scale('value', {
        nice: true,
        range: [0, 0.93]
        range: [0, 0.9]
      })
  
      if (!plot.legend || plot.legend === 'hidden') {
@@ -622,10 +1013,6 @@
        })
      }
  
      if (plot.transpose === 'true') {
        chart.coordinate().transpose()
      }
      if (plot.coordinate === 'polar') {
        chart.coordinate('polar', {
          innerRadius: 0.1,
@@ -635,13 +1022,60 @@
      let colors = new Map()
      let colorIndex = 0
      let lablecfg = {
        position: 'top',
        offset: 2,
        style: {
          fill: '#fff'
        }
      }
      if (plot.label === 'top') {
        lablecfg.offset = -5
        lablecfg.style.textBaseline = 'top'
      } else if (plot.label === 'middle') {
        lablecfg.position = 'middle'
        lablecfg.offset = 0
      } else if (plot.label === 'bottom') {
        lablecfg.position = 'bottom'
        lablecfg.offset = 0
      } else if (plot.label === 'true') {
        lablecfg.style.fill = color
      }
      if (plot.transpose === 'true') {
        chart.coordinate().transpose()
        if (plot.label === 'top') {
          delete lablecfg.style.textBaseline
          lablecfg.position = 'right'
          lablecfg.offset = -3
          lablecfg.style.textAlign = 'end'
        } else if (plot.label === 'middle') {
          lablecfg.position = 'middle'
          lablecfg.offset = 0
        } else if (plot.label === 'bottom') {
          lablecfg.position = 'left'
          lablecfg.offset = 2
        } else if (plot.label === 'true') {
          lablecfg.position = 'right'
          lablecfg.offset = 2
        }
      }
      if (plot.colors && plot.colors.length > 0) {
        plot.colors.forEach(item => {
          if (!colors.has(transfield[item.type])) {
            colors.set(transfield[item.type], item.color)
          }
        })
        if (plot.ramp === 'true') {
          plot.colors.forEach(item => {
            if (!colors.has(transfield[item.type])) {
              colors.set(transfield[item.type], `l(90) 0:${item.color} 1:${item.color1}` )
            }
          })
        } else {
          plot.colors.forEach(item => {
            if (!colors.has(transfield[item.type])) {
              colors.set(transfield[item.type], item.color)
            }
          })
        }
      }
  
      if (plot.adjust !== 'stack') {
@@ -668,32 +1102,47 @@
        if (plot.colors && plot.colors.length > 0) {
          let limit = chartColors.length
          _chart.color('key', (key) => {
            if (colors.get(key)) {
            if (colors.has(key)) {
              return colors.get(key)
            } else {
              colors.set(key, chartColors[colorIndex % limit])
              colorIndex++
              return chartColors[(colorIndex - 1) % limit]
            }
          })
        } else {
          _chart.color('key')
        }
        if (plot.label === 'true') {
          _chart.label('value', (value) => {
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            if (plot.label === 'true' && plot.labelColor === 'custom' && colors.has(key)) {
              lablecfg.style.fill = colors.get(key)
            }
            return {
              content: value,
              style: {
                fill: color
              }
              ...lablecfg
            }
          })
        }
        if (plot.barSize || plot.correction) {
          _chart.size(plot.barSize || 35)
        }
        if (plot.selectColor) {
          _chart.state({
            selected: {
              style: {
                fill: plot.selectColor,
              }
            }
          })
        }
        if (plot.barRadius) {
          _chart.style({ radius: [plot.barRadius, plot.barRadius, 0, 0] })
        }
      } else if (plot.adjust === 'stack') {
        let _chart = chart
@@ -714,26 +1163,28 @@
        if (plot.colors && plot.colors.length > 0) {
          let limit = chartColors.length
          _chart.color('key', (key) => {
            if (colors.get(key)) {
            if (colors.has(key)) {
              return colors.get(key)
            } else {
              colors.set(key, chartColors[colorIndex % limit])
              colorIndex++
              return chartColors[(colorIndex - 1) % limit]
            }
          })
        } else {
          _chart.color('key')
        }
        if (plot.label === 'true') {
          _chart.label('value', (value) => {
        if (plot.label !== 'false') {
          _chart.label('value*key', (value, key) => {
            if (plot.show === 'percent') {
              value = value + '%'
            }
            if (plot.label === 'true' && plot.labelColor === 'custom' && colors.has(key)) {
              lablecfg.style.fill = colors.get(key)
            }
            return {
              content: value,
              style: {
                fill: color
              }
              ...lablecfg
            }
          })
        }
@@ -741,6 +1192,24 @@
        if (plot.barSize || plot.correction) {
          _chart.size(plot.barSize || 35)
        }
        if (plot.selectColor) {
          _chart.state({
            selected: {
              style: {
                fill: plot.selectColor,
              }
            }
          })
        }
        if (plot.barRadius) {
          _chart.style({ radius: [plot.barRadius, plot.barRadius, 0, 0] })
        }
      }
      if (plot.interaction && plot.interaction.length) {
        plot.interaction.forEach(t => {
          chart.interaction(t)
        })
      }
  
      chart.render()
@@ -751,13 +1220,15 @@
  updateComponent = (component) => {
    const card = fromJS(this.state.card).toJS()
    let refresh = false
    if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) {
      let _element = document.getElementById(card.uuid + 'canvas')
      if (_element) {
        _element.innerHTML = ''
      }
      refresh = true
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(() => {
        this.viewrender()
      }, 150)
    }
    component.width = component.plot.width
@@ -765,12 +1236,6 @@
    
    this.setState({
      card: component
    }, () => {
      if (refresh) {
        setTimeout(() => {
          this.viewrender()
        }, 100)
      }
    })
    this.props.updateConfig(component)
  }
@@ -875,15 +1340,15 @@
  }
  render() {
    const { card } = this.state
    const { card, appType } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-line-chart-edit-box" style={{...card.style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
      <div className="menu-line-chart-edit-box" style={{..._style, height: card.plot.height || 400}} 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.addSearch} type="plus-circle" />
            <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
            {appType !== 'mob' ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null}
            {appType !== 'mob' ? <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" /> : null}
            <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/>
            <CopyComponent type="line" card={card}/>
            <PasteComponent config={card} options={['action', 'search', 'form']} updateConfig={this.updateComponent} />
@@ -897,12 +1362,13 @@
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <ActionComponent
        {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
        <div className="canvas" id={card.uuid + 'canvas'}></div>
        {appType !== 'mob' ? <ActionComponent
          type="chart"
          config={card}
          updateaction={this.updateComponent}
        />
        <div className="canvas" id={card.uuid + 'canvas'}></div>
        /> : null}
      </div>
    )
  }
src/menu/components/chart/antv-bar/index.scss
@@ -8,9 +8,12 @@
  
  .canvas {
    margin: 0px;
    // padding: 20px 15px 15px;
    padding: 15px;
    padding: 15px 10px 10px;
    letter-spacing: 0px;
    height: 100%;
  }
  .normal-header + .canvas {
    height: calc(100% - 45px);
  }
  .chart-header {
@@ -47,6 +50,7 @@
  .model-menu-action-list {
    position: absolute;
    right: 0px;
    top: 30px;
    z-index: 4;
    font-size: 16px;
  
@@ -58,6 +62,9 @@
      float: right;
    }
  }
  .normal-header + .canvas + .model-menu-action-list {
    top: 45px;
  }
}
.menu-line-chart-edit-box:hover {
  z-index: 1;
src/menu/components/chart/antv-dashboard/chartcompile/formconfig.jsx
New file
@@ -0,0 +1,233 @@
// 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 {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let roleList = sessionStorage.getItem('sysRoles')
  if (roleList) {
    try {
      roleList = JSON.parse(roleList)
    } catch {
      roleList = []
    }
  } else {
    roleList = []
  }
  return [
    {
      type: 'text',
      key: 'title',
      label: '标题',
      initVal: card.title,
      required: false
    },
    {
      type: 'text',
      key: 'name',
      label: '组件名称',
      initVal: card.name,
      tooltip: '用于组件间的区分。',
      required: true
    },
    {
      type: 'number',
      key: 'width',
      label: '宽度',
      initVal: card.width,
      tooltip: '栅格布局,每行等分为24列。',
      min: 1,
      max: 24,
      decimal: 0,
      required: true
    },
    {
      type: 'number',
      key: 'height',
      label: '高度',
      initVal: card.height,
      min: 100,
      max: 1000,
      decimal: 0,
      required: true
    },
    {
      type: 'select',
      key: 'blacklist',
      label: '黑名单',
      initVal: card.blacklist || [],
      multi: true,
      required: false,
      options: roleList
    }
  ]
}
/**
 * @description 获取图表视图配置表单
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getOptionForm (card, columns) {
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  return [
    {
      type: 'text',
      key: 'label',
      label: '指标名称',
      initVal: card.label || '',
      required: false
    },
    {
      type: 'select',
      key: 'valueField',
      label: '显示值',
      initVal: card.valueField || '',
      required: true,
      options: yfields
    },
    {
      type: 'number',
      key: 'maxValue',
      label: '最大值',
      initVal: card.maxValue || 100,
      tooltip: '仪表盘最大刻度值',
      min: 0,
      max: 999999,
      decimal: 1,
      required: true
    },
    {
      type: 'number',
      key: 'tickInterval',
      label: '间隔',
      initVal: card.tickInterval || 10,
      tooltip: '仪表盘刻度间隔值。',
      min: 0,
      max: 999999,
      decimal: 1,
      required: true
    },
    {
      type: 'radio',
      key: 'percent',
      label: '百分率',
      initVal: card.percent || 'true',
      required: false,
      options: [{
        value: 'true',
        text: '使用'
      }, {
        value: 'false',
        text: '不使用'
      }]
    },
    {
      type: 'color',
      key: 'tickColor',
      label: '刻度线',
      initVal: card.tickColor || '#CBCBCB',
      required: false
    },
    {
      type: 'color',
      key: 'labelColor',
      label: '指标颜色',
      initVal: card.labelColor || '#545454',
      required: false
    }
  ]
}
/**
 * @description 获取图表视图配置表单
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getRadioOptionForm (card, columns) {
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  return [
    {
      type: 'select',
      key: 'labelField',
      label: '指标',
      initVal: card.labelField || '',
      required: true,
      options: xfields
    },
    {
      type: 'select',
      key: 'valueField',
      label: '值',
      initVal: card.valueField || '',
      required: true,
      options: yfields
    },
    {
      type: 'number',
      key: 'maxValue',
      label: '最大值',
      initVal: card.maxValue || 100,
      min: 0,
      max: 999999,
      decimal: 1,
      required: true
    },
    {
      type: 'number',
      key: 'radius',
      label: '外环',
      initVal: card.radius || 75,
      tooltip: '图形所占区域的百分率。',
      min: 30,
      max: 100,
      decimal: 0,
      required: true
    },
    {
      type: 'number',
      key: 'fontSize',
      label: '字体大小',
      initVal: card.fontSize || 28,
      min: 12,
      max: 200,
      decimal: 0,
      required: true
    },
    {
      type: 'radio',
      key: 'percent',
      label: '百分率',
      initVal: card.percent || 'true',
      required: false,
      options: [{
        value: 'true',
        text: '使用'
      }, {
        value: 'false',
        text: '不使用'
      }]
    },
    {
      type: 'color',
      key: 'backColor',
      label: '背景色',
      initVal: card.backColor || '#ebedf0',
      required: false
    },
    {
      type: 'color',
      key: 'labelColor',
      label: '字体颜色',
      initVal: card.labelColor || '#8c8c8c',
      required: false
    }
  ]
}
src/menu/components/chart/antv-dashboard/chartcompile/index.jsx
New file
@@ -0,0 +1,333 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Modal, Form, Row, Col, Select, Icon, Radio, Tooltip, Input, InputNumber, Tabs, Button } from 'antd'
import Utils from '@/utils/utils.js'
import { getBaseForm, getOptionForm, getRadioOptionForm } from './formconfig'
import asyncComponent from '@/utils/asyncComponent'
import ColorSketch from '@/mob/colorsketch'
import './index.scss'
const { TabPane } = Tabs
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const NormalForm = asyncComponent(() => import('@/menu/components/share/normalform'))
class LineChartDrawerForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,
    plot: PropTypes.object,
    config: PropTypes.object,
    plotchange: PropTypes.func
  }
  state = {
    visible: false,
    plot: null,
    formlist: null,
    baseFormlist: null,
    view: 'normal',
    colorColumns: [
      {
        title: '指标',
        dataIndex: 'tick',
        inputType: this.props.config.subtype === 'ratioboard' ? 'text' : 'number',
        editable: true,
        width: '40%'
      },
      {
        title: '颜色',
        dataIndex: 'color',
        inputType: 'color',
        editable: true,
        width: '40%',
        render: (text, record) => {
          return (<div style={{width: '80px', height: '23px', background: text}}></div>)
        }
      },
    ]
  }
  showDrawer = () => {
    const { config } = this.props
    this.setState({
      visible: true,
      view: 'normal',
      plot: fromJS(config.plot).toJS(),
      baseFormlist: getBaseForm(config.plot),
      formlist: config.subtype === 'ratioboard' ? getRadioOptionForm(config.plot, config.columns) : getOptionForm(config.plot, config.columns)
    })
  }
  getFields() {
    const { formlist } = this.state
    const { getFieldDecorator } = this.props.form
    const fields = []
    if (!formlist) {
      return fields
    }
    formlist.forEach((item, index) => {
      if (item.hidden || item.forbid) return
      if (item.type === 'text') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.onSubmit}/>)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.onSubmit}/>)}
            </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 mode={item.multi ? 'multiple' : ''}>
                  {item.options.map((option, index) =>
                    <Select.Option key={index} value={option.field}>
                      {option.label}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Radio.Group 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 === 'color') {
        fields.push(
          <Col span={12} key={index} className="color-col">
            <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
              })(
                <ColorSketch />
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
  }
  onSubmit = () => {
    const { config } = this.props
    const { plot, view } = this.state
    if (view === 'normal') {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let _plot = {...plot, ...values}
          this.setState({
            plot: _plot,
            visible: false
          })
          this.props.plotchange({...config, plot: _plot})
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          visible: false
        })
        this.props.plotchange({...config, plot: _plot})
      })
    } else {
      this.setState({
        visible: false
      })
      this.props.plotchange({...config, plot: plot})
    }
  }
  changeTab = (tab) => {
    const { plot, view } = this.state
    if (view === 'normal') {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let _plot = {...plot, ...values}
          this.setState({
            plot: _plot,
            view: tab
          })
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          view: tab
        })
      })
    } else {
      this.setState({
        view: tab
      })
    }
  }
  addColor = () => {
    let plot = fromJS(this.state.plot).toJS()
    plot.colors = plot.colors || []
    plot.colors.push({
      uuid: Utils.getuuid(),
      tick: plot.maxValue || 1,
      color: '#1890ff'
    })
    this.setState({plot})
  }
  changeColor = (colors) => {
    const { plot } = this.state
    this.setState({plot: {...plot, colors}})
  }
  render() {
    const { visible, plot, colorColumns, view, baseFormlist } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 18 }
      }
    }
    return (
      <div className="line-chart-drawer-form">
        <Icon type="edit" title="编辑" onClick={this.showDrawer} />
        <Modal
          wrapClassName="popview-modal menu-chart-edit-modal"
          title="图表编辑"
          visible={visible}
          width={850}
          maskClosable={false}
          onOk={this.onSubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}>
            <TabPane tab="组件设置" key="base">
              <NormalForm dict={this.props.dict} formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/>
            </TabPane>
            <TabPane tab="图表设置" key="normal">
              <Form {...formItemLayout}>
                <Row gutter={16}>{this.getFields()}</Row>
              </Form>
            </TabPane>
            {plot ? <TabPane tab="颜色设置" key="color">
              <div>
                <Button className="color-add mk-green" onClick={this.addColor}>{this.props.dict['model.add']}</Button>
                <EditTable actions={['edit', 'move', 'del']} data={plot.colors || []} columns={colorColumns} onChange={this.changeColor}/>
              </div>
            </TabPane> : null}
          </Tabs>
        </Modal>
      </div>
    );
  }
}
export default Form.create()(LineChartDrawerForm)
src/menu/components/chart/antv-dashboard/chartcompile/index.scss
New file
@@ -0,0 +1,44 @@
.line-chart-drawer-form {
  display: inline-block;
  > .anticon-edit {
    color: #1890ff;
  }
}
.menu-chart-edit-modal {
  .ant-modal {
    top: 50px;
    .ant-modal-body {
      min-height: 50vh;
      max-height: calc(100vh - 190px);
      padding-top: 10px;
      .anticon-question-circle {
        color: #c49f47;
        position: relative;
        left: -3px;
      }
      .ant-input-number {
        width: 100%;
      }
      .ant-tabs-nav-wrap {
        text-align: center;
      }
      .color-sketch-block {
        position: relative;
        top: 5px;
        width: 240px;
      }
      .color-add {
        float: right;
        margin-bottom: 10px;
        position: relative;
        z-index: 1;
      }
      .color-col {
        .ant-form-item-control {
          height: 40px;
        }
      }
    }
  }
}
src/menu/components/chart/antv-dashboard/index.jsx
New file
@@ -0,0 +1,538 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover } from 'antd'
import { Chart, registerShape } from '@antv/g2'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.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 ChartCompileForm = asyncIconComponent(() => import('./chartcompile'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
registerShape('point', 'pointer', {
  draw(cfg, container) {
    const group = container.addGroup({});
    // 获取极坐标系下画布中心点
    const center = this.parsePoint({ x: 0, y: 0 });
    // 绘制指针
    group.addShape('line', {
      attrs: {
        x1: center.x,
        y1: center.y,
        x2: cfg.x,
        y2: cfg.y,
        stroke: cfg.color,
        lineWidth: 5,
        lineCap: 'round',
      },
    });
    group.addShape('circle', {
      attrs: {
        x: center.x,
        y: center.y,
        r: 9.75,
        stroke: cfg.color,
        lineWidth: 4.5,
        fill: '#fff',
      },
    });
    return group;
  },
})
registerShape('point', 'hidden', {
  draw(cfg, container) {
    return container.addGroup({})
  }
})
class antvDashboardChart extends Component {
  static propTpyes = {
    card: PropTypes.object,
    updateConfig: PropTypes.func,
    deletecomponent: PropTypes.func,
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    ismob: sessionStorage.getItem('appType') === 'mob',
    eventListener: null
  }
  UNSAFE_componentWillMount () {
    const { card, ismob } = this.props
    if (card.isNew) {
      let _plot = null
      if (card.subtype === 'ratioboard') {
        _plot = {
          width: card.width || 12,
          height: 400,
          labelField: '',
          valueField: '',
          name: card.name,
          maxValue: 100,
          radius: 75,
          fontSize: 28,
          percent: 'true',
          backColor: '#ebedf0',
          labelColor: '#8c8c8c'
        }
      } else {
        _plot = {
          width: card.width || 12,
          height: 400,
          label: '',
          valueField: '',
          name: card.name,
          maxValue: 100,
          tickInterval: 10,
          labelColor: '#545454',
          tickColor: '#CBCBCB',
          percent: 'true'
        }
      }
      if (ismob) {
        _plot.width = 24
      }
      let _card = {
        uuid: card.uuid,
        type: card.type,
        floor: card.floor,
        tabId: card.tabId || '',
        parentId: card.parentId || '',
        format: card.subtype === 'ratioboard' ? 'array' : 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        dataName: card.dataName || '',
        width: _plot.width,
        name: _plot.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        style: {
          borderWidth: '1px', borderColor: 'rgb(217, 217, 217)',
          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
        },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
        scripts: [],
        search: [],
        action: [],
        plot: _plot,
        btnlog: [],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.plot = config.plot
        _card.plot.name = card.name
        _card.style = config.style
        _card.headerStyle = config.headerStyle
      }
      this.props.updateConfig(_card)
      this.setState({
        card: _card
      })
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    MKEmitter.addListener('submitStyle', this.getStyle)
    setTimeout(() => {
      this.viewrender()
    }, 1000)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  handleTabsChange = (parentId) => {
    const { card } = this.state
    if (parentId === card.parentId) {
      let _element = document.getElementById(card.uuid + 'canvas')
      if (_element) {
        _element.innerHTML = ''
      }
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(this.viewrender, 100)
    }
  }
  viewrender = () => {
    const { card } = this.state
    if (card.subtype === 'ratioboard') {
      this.ratioboardrender()
    } else {
      this.dashboardrender()
    }
  }
  getratiodata = () => {
    const { card } = this.state
    let val = (Math.random() * card.plot.maxValue).toFixed(1)
    let data = [
      { type: '新用户', value: val, $percent: val / card.plot.maxValue, $color: '#1890ff' },
    ]
    if (card.plot.colors && card.plot.colors.length > 0) {
      data = []
      card.plot.colors.forEach(item => {
        let _val = (Math.random() * card.plot.maxValue).toFixed(1)
        data.push({
          type: item.tick,
          value: _val,
          $percent: _val / card.plot.maxValue,
          $color: item.color
        })
      })
    }
    return data
  }
  ratioboardrender = () => {
    const { card } = this.state
    const plot = card.plot
    const data = this.getratiodata()
    const chart = new Chart({
      container: card.uuid + 'dashboard',
      autoFit: true,
      height: plot.title ? plot.height - 45 : plot.height,
    })
    chart.data(data)
    chart.coordinate('polar', {
      startAngle: -Math.PI / 2,
      endAngle: 3 * Math.PI / 2,
      radius: (plot.radius || 75) / 100
    })
    chart.scale('$percent', {
      min: 0,
      max: 1,
      tickInterval: 1,
    })
    chart.axis(false)
    chart.facet('rect', {
      fields: ['type'],
      showTitle: false,
      eachView: function eachView(view, facet) {
        const data = facet.data[0]
        view.point().position('').shape('hidden')
        view.annotation().arc({
          top: false,
          start: [0, 1],
          end: [0.9999, 1],
          style: {
            stroke: plot.backColor,
            lineWidth: 10
          }
        })
        let _tick = data.$percent
        if (_tick >= 1) {
          _tick = 0.9999
        }
        view.annotation().arc({
          start: [0, 1],
          end: [_tick, 1],
          style: {
            stroke: data.$color,
            lineWidth: 10,
          }
        })
        // 仪表盘信息
        let text = ''
        if (plot.percent === 'true') {
          text = +(data.$percent * 100).toFixed(2) + '%'
        } else {
          text = data.value
        }
        view.annotation().text({
          position: ['50%', '45%'],
          content: data.type,
          style: {
            fontSize: plot.fontSize * 0.8,
            fill: plot.labelColor,
            fontWeight: 300,
            textAlign: 'center'
          },
          offsetX: 0
        })
        view.annotation().text({
          position: ['50%', '55%'],
          content: text,
          style: {
            fontSize: plot.fontSize,
            fill: plot.labelColor,
            fontWeight: 500,
            textAlign: 'center'
          },
          offsetX: 0,
          offsetY: 10
        })
      }
    })
    chart.render()
  }
  getdata = () => {
    const { card } = this.state
    const data = []
    const val = (Math.random() * card.plot.maxValue).toFixed(1)
    data.push({ value: +val })
    return data
  }
  dashboardrender = () => {
    const { card } = this.state
    const plot = card.plot
    const data = this.getdata()
    const chart = new Chart({
      container: card.uuid + 'dashboard',
      autoFit: true,
      height: plot.title ? plot.height - 45 : plot.height,
      padding: [0, 0, 0, 0],
    })
    chart.data(data)
    chart.scale('value', {
      min: 0,
      max: plot.maxValue,
      tickInterval: plot.tickInterval,
    })
    chart.coordinate('polar', {
      startAngle: (-9 / 8) * Math.PI,
      endAngle: (1 / 8) * Math.PI,
      radius: 0.75,
    })
    chart.axis('1', false)
    chart.axis('value', {
      line: null,
      label: {
        offset: -36,
        style: {
          fontSize: 18,
          fill: plot.tickColor,
          textAlign: 'center',
          textBaseline: 'middle',
        },
      },
      tickLine: {
        length: -24,
        style: {
          stroke: plot.tickColor
        }
      },
      grid: null,
    })
    chart.legend(false)
    chart.tooltip(false)
    chart
      .point()
      .position('value*1')
      .shape('pointer')
      .color('value', (val) => {
        let _color = '#1890FF'
        if (plot.colors && plot.colors.length > 0) {
          _color = plot.colors[plot.colors.length - 1].color || '#1890FF'
          plot.colors.some(item => {
            if (item.tick > val) {
              _color = item.color
              return true
            }
            return false
          })
        }
        return _color
      })
      .animate({
        appear: {
          animation: 'fade-in'
        }
      })
    // 绘制仪表盘背景
    chart.annotation().arc({
      top: false,
      start: [0, 1],
      end: [plot.maxValue, 1],
      style: {
        stroke: '#ebedf0',
        lineWidth: 18,
        lineDash: null,
      },
    })
    if (!plot.colors || plot.colors.length === 0) {
      chart.annotation().arc({
        start: [0, 1],
        end: [plot.maxValue, 1],
        style: {
          stroke: '#1890FF',
          lineWidth: 18,
          lineDash: null,
        },
      });
    } else {
      let start = 0
      plot.colors.forEach(item => {
        chart.annotation().arc({
          start: [start, 1],
          end: [item.tick, 1],
          style: {
            stroke: item.color,
            lineWidth: 18,
            lineDash: null,
          },
        })
        start = item.tick
      })
    }
    if (plot.label) {
      chart.annotation().text({
        position: ['50%', '85%'],
        content: plot.label,
        style: {
          fontSize: 20,
          fill: plot.labelColor,
          textAlign: 'center',
        },
      })
    }
    let val = data[0].value
    if (plot.percent !== 'false' && plot.maxValue) {
      val = +(val / plot.maxValue * 100).toFixed(2) + ' %'
    }
    chart.annotation().text({
      position: ['50%', '90%'],
      content: val,
      style: {
        fontSize: 36,
        fill: plot.labelColor,
        textAlign: 'center',
      },
      offsetY: 15,
    })
    chart.render()
  }
  updateComponent = (component) => {
    const card = fromJS(this.state.card).toJS()
    if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) {
      let _element = document.getElementById(card.uuid + 'dashboard')
      if (_element) {
        _element.innerHTML = ''
      }
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(() => {
        this.viewrender()
      }, 150)
    }
    component.width = component.plot.width
    component.name = component.plot.name
    this.setState({
      card: component
    })
    this.props.updateConfig(component)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid || comIds.length > 1) return
    let _card = {...card, style}
    this.updateComponent(_card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-dashboard-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/>
            <CopyComponent type="dashboard" card={card}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <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}/>
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.plot.title ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
        <div className="canvas" id={card.uuid + 'dashboard'}></div>
      </div>
    )
  }
}
export default antvDashboardChart
src/menu/components/chart/antv-dashboard/index.scss
New file
@@ -0,0 +1,50 @@
.menu-dashboard-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  .canvas {
    margin: 0px;
    padding: 15px;
    letter-spacing: 0px;
    height: 100%;
  }
  .normal-header + .canvas {
    height: calc(100% - 45px);
  }
  >.anticon-tool {
    position: absolute;
    right: 1px;
    top: 1px;
    z-index: 2;
    font-size: 16px;
    padding: 5px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.85);
    background: rgba(255, 255, 255, 0.55);
  }
  .model-menu-action-list {
    position: absolute;
    right: 0px;
    z-index: 4;
    padding-top: 10px;
    font-size: 16px;
    .ant-row .anticon-plus {
      float: right;
    }
    .page-card {
      float: right;
    }
  }
}
.menu-dashboard-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/chart/antv-pie/chartcompile/formconfig.jsx
@@ -8,7 +8,8 @@
 * @param {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let isApp = sessionStorage.getItem('appType') === 'pc'
  let appType = sessionStorage.getItem('appType')
  let isApp = appType === 'pc' || appType === 'mob'
  let menulist = null
  if (isApp) {
@@ -131,6 +132,7 @@
 * @param {Array}  columns    // 显示列
 */
export function getOptionForm (card, columns) {
  let appType = sessionStorage.getItem('appType')
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
@@ -144,13 +146,14 @@
      options: [
        { value: 'pie', text: '饼图' },
        { value: 'ring', text: '环图' },
        { value: 'nest', text: '嵌套' },
        { value: 'nightingale', text: '南丁格尔图' }
      ]
    },
    {
      type: 'select',
      key: 'Xaxis',
      label: 'X-轴',
      label: '名称',
      initVal: card.Xaxis || '',
      required: true,
      options: xfields
@@ -158,10 +161,20 @@
    {
      type: 'select',
      key: 'Yaxis',
      label: 'Y-轴',
      label: '值',
      initVal: card.Yaxis || '',
      required: true,
      options: yfields
    },
    {
      type: 'select',
      key: 'type',
      label: '类型',
      initVal: card.type || '',
      tooltip: '内环的分类字段。',
      required: true,
      options: xfields,
      hidden: card.shape !== 'nest',
    },
    {
      type: 'select',
@@ -183,7 +196,8 @@
        { field: 'left-top', label: '左上' },
        { field: 'left-bottom', label: '左下' },
        { field: 'hidden', label: '隐藏' }
      ]
      ],
      hidden: card.shape === 'nest',
    },
    {
      type: 'number',
@@ -239,7 +253,7 @@
    {
      type: 'radio',
      key: 'label',
      label: '标注值',
      label: '标签',
      initVal: card.label || 'false',
      required: false,
      options: [{
@@ -250,8 +264,11 @@
        text: '内侧'
      }, {
        value: 'outer',
        text: '外侧'
      }]
        text: '蜘蛛'
      }, {
        value: 'normal',
        text: '常规'
      }],
    }, {
      type: 'radio',
      key: 'repeat',
@@ -269,19 +286,51 @@
        text: '累加'
      }]
    }, {
      type: 'number',
      key: 'splitLine',
      label: '分隔线',
      initVal: card.splitLine || '',
      tooltip: '分隔线的宽度。',
      min: 0,
      max: 20,
      decimal: 0,
      required: false
    }, {
      type: 'color',
      key: 'splitColor',
      label: '分隔色',
      initVal: card.splitColor || '#ffffff',
      tooltip: '分隔线的颜色,存在分隔线时有效。',
      required: false
    }, {
      type: 'color',
      key: 'color',
      label: '色系',
      initVal: card.color || 'rgba(0, 0, 0, 0.85)',
      tooltip: '坐标轴及示例等提示文字使用的颜色。',
      required: false
    }, {
      type: 'select',
      key: 'interaction',
      label: '交互效果',
      initVal: card.interaction || [],
      multi: true,
      required: false,
      options: [{
        value: 'black',
        text: '黑色'
      }, {
        value: 'white',
        text: '白色'
      }]
      forbid: appType === 'mob',
      options: [
        { value: 'element-active', label: '元素聚焦' },
        { value: 'element-selected', label: '元素选中(多选)' },
        { value: 'element-single-selected', label: '元素选中(单选)' },
        // { value: 'active-region', label: '背景框' },
        // { value: 'view-zoom', label: '视图缩放' },
        { value: 'element-highlight', label: '元素高亮' },
        // { value: 'element-highlight-by-color', label: '同色元素高亮' },
        // { value: 'element-highlight-by-x', label: '同X轴元素高亮' },
        { value: 'legend-filter', label: '图例过滤' },
        { value: 'legend-active', label: '图例聚焦' },
        { value: 'legend-highlight', label: '图例高亮' },
        // { value: 'brush', label: '选框过滤' },
      ]
    }
  ]
}
src/menu/components/chart/antv-pie/chartcompile/index.jsx
@@ -70,6 +70,10 @@
        formlist: formlist.map(item => {
          if (item.key === 'innerRadius') {
            item.hidden = val === 'pie'
          } else if (item.key === 'type') {
            item.hidden = val !== 'nest'
          } else if (item.key === 'legend') {
            item.hidden = val === 'nest'
          }
          return item
        })
@@ -157,7 +161,7 @@
              })(
                <Select mode={item.multi ? 'multiple' : ''}>
                  {item.options.map((option, index) =>
                    <Select.Option key={index} value={option.field}>
                    <Select.Option key={index} value={option.field || option.value}>
                      {option.label}
                    </Select.Option>
                  )}
@@ -184,7 +188,7 @@
                  }
                ]
              })(
                <Radio.Group disabled={item.readonly} onChange={(e) => this.radioChange(e, item.key)}>
                <Radio.Group style={{whiteSpace: 'nowrap'}} disabled={item.readonly} onChange={(e) => this.radioChange(e, item.key)}>
                  {item.options.map(option => {
                    return (
                      <Radio key={option.value} value={option.value}>{option.text}</Radio>
src/menu/components/chart/antv-pie/index.jsx
@@ -1,14 +1,14 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover, notification } from 'antd'
import { Icon, Popover } from 'antd'
import { Chart } from '@antv/g2'
import DataSet from '@antv/data-set'
import DataSet, { DataView } from '@antv/data-set'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
@@ -18,7 +18,6 @@
const ChartCompileForm = asyncIconComponent(() => import('./chartcompile'))
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 NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
@@ -33,11 +32,12 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    ismob: sessionStorage.getItem('appType') === 'mob',
    eventListener: null
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    const { card, ismob } = this.props
    if (card.isNew) {
      let _plot = {
@@ -48,8 +48,15 @@
        name: card.name
      }
      if (ismob) {
        _plot.width = 24
      }
      if (card.subtype === 'ring') {
        _plot.innerRadius = 50
      } else if (card.subtype === 'nest') {
        _plot.innerRadius = 30
        _plot.radius = 80
      }
      let _card = {
@@ -105,9 +112,12 @@
  }
  componentDidMount () {
    this.pierender()
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    MKEmitter.addListener('submitStyle', this.getStyle)
    setTimeout(() => {
      this.viewrender()
    }, 1000)
  }
  shouldComponentUpdate (nextProps, nextState) {
@@ -134,7 +144,18 @@
        _element.innerHTML = ''
      }
      setTimeout(this.pierender, 100)
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(this.viewrender, 100)
    }
  }
  viewrender = () => {
    const { card } = this.state
    if (card.plot.shape === 'nest') {
      this.nestrender()
    } else {
      this.pierender()
    }
  }
@@ -145,13 +166,6 @@
      { label: '2003', value: 33.7 },
      { label: '2004', value: 30.7 },
      { label: '2005', value: 25.8 },
      { label: '2006', value: 31.7 },
      { label: '2007', value: 33 },
      { label: '2008', value: 46 },
      { label: '2009', value: 38.3 },
      { label: '2010', value: 28 },
      { label: '2011', value: 42.5 },
      { label: '2012', value: 30.3 }
    ]
    let data = xdata.map(item => {
@@ -164,13 +178,252 @@
    return data
  }
  pierender = () => {
    const { card } = this.state
    let plot = {...card.plot, height: card.plot.height - 80}
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
  getnestdata = (X_axis, Y_axis, type) => {
    const xdata = [
      { name: '狮子', type: '火象', value: 11 },
      { name: '白羊', type: '火象', value: 10 },
      { name: '水瓶', type: '风向', value: 14 },
      { name: '射手', type: '火象', value: 10 },
      { name: '双子', type: '风向', value: 7 },
      { name: '天平', type: '风向', value: 7 },
      { name: '摩羯', type: '土象', value: 14 },
      { name: '金牛', type: '土象', value: 3 },
      { name: '处女', type: '土象', value: 3 },
      { name: '天蝎', type: '水象', value: 11 },
      { name: '巨蟹', type: '水象', value: 5 },
      { name: '双鱼', type: '水象', value: 5 },
    ]
    let map = new Map()
    let sort = 1
    let data = xdata.map(item => {
      let _sort = sort
      if (map.has(item.type)) {
        _sort = map.get(item.type)
      } else {
        map.set(item.type, _sort)
        sort++
      }
      return {
        [X_axis]: item.name,
        [Y_axis]: item.value,
        [type]: item.type,
        $sort: _sort
      }
    })
    return data
  }
  nestrender = () => {
    const { card } = this.state
    const plot = card.plot
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
    let X_axis = plot.Xaxis || 'x'
    let Y_axis = plot.Yaxis || 'y'
    let type = plot.type || 'type'
    let height = plot.height || 400
    if (card.plot.title || card.search.length > 0) {
      height = height - 45
    }
    const _data = this.getnestdata(X_axis, Y_axis, type)
    const dvx = new DataView().source(_data)
    dvx.transform({
      type: 'sort-by',
      fields: ['$sort']
    })
    let data = dvx.rows
    // 通过 DataSet 计算百分比
    const dv = new DataView()
    dv.source(data).transform({
      type: 'percent',
      field: Y_axis,
      dimension: type,
      as: '$percent'
    })
    const dv1 = new DataView()
    dv1.source(data).transform({
      type: 'percent',
      field: Y_axis,
      dimension: X_axis,
      as: '$percent',
    })
    const chart = new Chart({
      container: card.uuid + 'canvas',
      autoFit: true,
      height: height,
      padding: 0,
    })
    chart.data(dv.rows)
    if (plot.show !== 'value') {
      chart.scale('percent', {
        formatter: (val) => {
          val = val * 100 + '%'
          return val
        }
      })
      Y_axis = '$percent'
    }
    let radius = plot.radius / 100
    let innerRadius = plot.innerRadius / 100
    chart.coordinate('theta', {
      innerRadius: innerRadius,
      radius: innerRadius + (radius - innerRadius) / 2,
    })
    if (plot.tooltip !== 'true') {
      chart.tooltip(false)
    } else {
      chart.tooltip({
        showTitle: false,
        showMarkers: false
      })
    }
    chart.legend(false)
    let chart1 = chart
      .interval()
      .adjust('stack')
      .position(Y_axis)
      .color(type)
      .tooltip(`${type}*${Y_axis}`, (type, percent) => {
        if (plot.show !== 'value') {
          percent = (percent * 100).toFixed(2) + '%'
        }
        return {
          name: type,
          value: percent,
        }
      })
    if (plot.splitLine) {
      chart1.style({
        lineWidth: plot.splitLine,
        stroke: plot.splitColor,
      })
    }
    if (plot.label !== 'false') {
      chart1.label(type, {
        offset: -10,
      })
    }
    const outterView = chart.createView()
    outterView.data(dv1.rows)
    if (plot.show !== 'value') {
      outterView.scale('percent', {
        formatter: (val) => {
          val = val * 100 + '%'
          return val
        }
      })
    }
    outterView.coordinate('theta', {
      innerRadius: (innerRadius + (radius - innerRadius) / 2) / radius,
      radius: radius
    })
    let chart2 = outterView
      .interval()
      .adjust('stack')
      .position(Y_axis)
      .color(X_axis)
      .tooltip(`${X_axis}*${Y_axis}`, (name, value) => {
        if (plot.show !== 'value') {
          value = (value * 100).toFixed(2) + '%'
        }
        return {
          name: name,
          value: value
        }
      })
    if (plot.splitLine) {
      chart2.style({
        lineWidth: plot.splitLine,
        stroke: plot.splitColor,
      })
    }
    if (plot.label !== 'false') {
      if (plot.label === 'inner') {
        chart2.label(Y_axis, {
          offset: -30,
          content: (data) => {
            let _val = ''
            if (plot.show !== 'value') {
              _val = `${(data[Y_axis] * 100).toFixed(2)}%`
            } else {
              _val = `${data[Y_axis]}`
            }
            return _val
          },
          style: {
            textAlign: 'center',
            fontSize: 16,
            shadowBlur: 2,
            shadowColor: 'rgba(0, 0, 0, .45)',
            fill: '#fff',
          }
        })
      } else {
        chart2.label(Y_axis, {
          layout: { type: plot.label === 'outer' ? 'pie-spider' : 'fixed-overlap' },
          labelHeight: 20,
          content: (data) => {
            let _val = ''
            if (plot.show !== 'value') {
              _val = `${(data[Y_axis] * 100).toFixed(2)}%`
            } else {
              _val = `${data[Y_axis]}`
            }
            return `${data[X_axis]}: ${_val}`
          },
          labelLine: {
            style: {
              lineWidth: 0.5,
            },
          },
          style: {
            fill: color
          }
        })
      }
    }
    if (plot.interaction && plot.interaction.length) {
      plot.interaction.forEach(t => {
        chart.interaction(t)
      })
    }
    chart.render()
  }
  pierender = () => {
    const { card } = this.state
    const plot = card.plot
    let color = plot.color || 'rgba(0, 0, 0, 0.85)'
    let X_axis = plot.Xaxis || 'x'
    let Y_axis = plot.Yaxis || 'y'
    let height = plot.height || 400
    if (card.plot.title || card.search.length > 0) {
      height = height - 45
    }
    let data = this.getdata(X_axis, Y_axis)
@@ -180,7 +433,7 @@
    const chart = new Chart({
      container: card.uuid + 'canvas',
      autoFit: true,
      height: plot.height || 400
      height: height
    })
    if (plot.shape !== 'nightingale' && plot.show !== 'value') {
@@ -245,6 +498,7 @@
      })
    }
    // 饼图或环图
    if (plot.shape !== 'nightingale') {
      let _chart = chart
        .interval()
@@ -260,24 +514,26 @@
            value: value
          }
        })
      if (plot.splitLine) {
        _chart.style({
          lineWidth: plot.splitLine,
          stroke: plot.splitColor,
        })
      }
      if (plot.label !== 'false') {
        if (plot.label === 'inner') {
          _chart.label(Y_axis, {
            offset: -30,
            content: (data) => {
              let _label = ''
              let _val = ''
              if (plot.show !== 'value') {
                _val = `${(data[Y_axis] * 100).toFixed(2)}%`
              } else {
                _val = `${data[Y_axis]}`
              }
              if (plot.label === 'inner') {
                _label = _val
              } else {
                _label = `${data[X_axis]}: ${_val}`
              }
              return _label
              return _val
            },
            style: {
              textAlign: 'center',
@@ -289,22 +545,17 @@
          })
        } else {
          _chart.label(Y_axis, {
            layout: { type: 'pie-spider' },
            layout: { type: plot.label === 'outer' ? 'pie-spider' : 'fixed-overlap' },
            labelHeight: 20,
            content: (data) => {
              let _label = ''
              let _val = ''
              if (plot.show !== 'value') {
                _val = `${(data[Y_axis] * 100).toFixed(2)}%`
              } else {
                _val = `${data[Y_axis]}`
              }
              if (plot.label === 'inner') {
                _label = _val
              } else {
                _label = `${data[X_axis]}: ${_val}`
              }
              return _label
              return `${data[X_axis]}: ${_val}`
            },
            labelLine: {
              style: {
@@ -317,10 +568,8 @@
          })
        }
      }
      chart.interaction('element-active')
    } else {
      chart.axis(false)
      chart.interaction('element-highlight')
      let _chart = chart
        .interval()
        .position(`${X_axis}*${Y_axis}`)
@@ -337,11 +586,19 @@
          }
          _chart.label(X_axis, _label)
          .style({
            lineWidth: 1,
            stroke: '#fff',
        }
        if (plot.splitLine) {
          _chart.style({
            lineWidth: plot.splitLine,
            stroke: plot.splitColor,
          })
        }
    }
    if (plot.interaction && plot.interaction.length) {
      plot.interaction.forEach(t => {
        chart.interaction(t)
      })
    }
    chart.render()
@@ -349,13 +606,16 @@
  updateComponent = (component) => {
    const card = fromJS(this.state.card).toJS()
    let refresh = false
    if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) {
      let _element = document.getElementById(card.uuid + 'canvas')
      if (_element) {
        _element.innerHTML = ''
      }
      refresh = true
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(() => {
        this.viewrender()
      }, 150)
    }
    component.width = component.plot.width
@@ -363,12 +623,6 @@
    
    this.setState({
      card: component
    }, () => {
      if (refresh) {
        setTimeout(() => {
          this.pierender()
        }, 100)
      }
    })
    this.props.updateConfig(component)
  }
@@ -410,32 +664,6 @@
    this.updateComponent(_card)
  }
  handleLog = (type, logs, item) => {
    let card = fromJS(this.state.card).toJS()
    if (type === 'revert') {
      card.action = card.action ? [...card.action, item] : [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()
@@ -444,19 +672,18 @@
  }
  render() {
    const { card } = this.state
    const { card, ismob } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-pie-chart-edit-box" style={{...card.style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
      <div className="menu-pie-chart-edit-box" style={{..._style, height: card.plot.height || 400}} 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.addSearch} type="plus-circle" />
            {!ismob ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null}
            <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/>
            <CopyComponent type="pie" card={card}/>
            <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)} />
@@ -465,6 +692,7 @@
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
        <div className="canvas" id={card.uuid + 'canvas'}></div>
      </div>
    )
src/menu/components/chart/antv-pie/index.scss
@@ -10,6 +10,10 @@
    margin: 0px;
    padding: 15px;
    letter-spacing: 0px;
    height: 100%;
  }
  .normal-header + .canvas {
    height: calc(100% - 45px);
  }
  >.anticon-tool {
src/menu/components/chart/antv-scatter/chartcompile/formconfig.jsx
New file
@@ -0,0 +1,174 @@
/**
 * @description 获取图表视图配置表单
 * @param {object} card       // 图表对象
 */
export function getBaseForm (card) {
  let roleList = sessionStorage.getItem('sysRoles')
  if (roleList) {
    try {
      roleList = JSON.parse(roleList)
    } catch {
      roleList = []
    }
  } else {
    roleList = []
  }
  return [
    {
      type: 'text',
      key: 'title',
      label: '标题',
      initVal: card.title,
      required: false
    },
    {
      type: 'text',
      key: 'name',
      label: '组件名称',
      initVal: card.name,
      tooltip: '用于组件间的区分。',
      required: true
    },
    {
      type: 'number',
      key: 'width',
      label: '宽度',
      initVal: card.width,
      tooltip: '栅格布局,每行等分为24列。',
      min: 1,
      max: 24,
      decimal: 0,
      required: true
    },
    {
      type: 'number',
      key: 'height',
      label: '高度',
      initVal: card.height,
      min: 100,
      max: 1000,
      decimal: 0,
      required: true
    },
    {
      type: 'select',
      key: 'blacklist',
      label: '黑名单',
      initVal: card.blacklist || [],
      multi: true,
      required: false,
      options: roleList
    }
  ]
}
/**
 * @description 获取图表视图配置表单
 * @param {object} card       // 图表对象
 * @param {Array}  columns    // 显示列
 */
export function getOptionForm (card, columns) {
  let appType = sessionStorage.getItem('appType')
  let xfields = columns.filter(item => /^Nvarchar/ig.test(item.datatype))
  let yfields = columns.filter(item => /^(Int|Decimal)/ig.test(item.datatype))
  return [
    {
      type: 'select',
      key: 'gender',
      label: '类型',
      initVal: card.gender || '',
      required: true,
      options: xfields
    },
    {
      type: 'select',
      key: 'Xaxis',
      label: 'X-轴',
      initVal: card.Xaxis || '',
      required: true,
      options: columns
    },
    {
      type: 'select',
      key: 'Yaxis',
      label: 'Y-轴',
      initVal: card.Yaxis || '',
      required: true,
      options: yfields
    },
    {
      type: 'radio',
      key: 'shape',
      label: '形状',
      initVal: card.shape || 'circle',
      required: false,
      options: [{
        value: 'circle',
        text: 'circle'
      }, {
        value: 'square',
        text: 'square'
      }]
    },
    {
      type: 'radio',
      key: 'tooltip',
      label: '悬浮提示',
      initVal: card.tooltip || 'true',
      required: false,
      options: [{
        value: 'true',
        text: '显示'
      }, {
        value: 'false',
        text: '隐藏'
      }]
    },
    {
      type: 'text',
      key: 'Xunit',
      label: 'X轴单位',
      initVal: card.Xunit || '',
      required: false
    },
    {
      type: 'text',
      key: 'Yunit',
      label: 'Y轴单位',
      initVal: card.Yunit || '',
      required: false
    },
    {
      type: 'color',
      key: 'color',
      label: '色系',
      initVal: card.color || 'rgba(0, 0, 0, 0.65)',
      tooltip: '坐标轴提示文字及示例的颜色。',
      required: false
    }, {
      type: 'select',
      key: 'interaction',
      label: '交互效果',
      initVal: card.interaction || [],
      multi: true,
      required: false,
      forbid: appType === 'mob',
      options: [
        { value: 'element-active', label: '元素聚焦' },
        { value: 'element-selected', label: '元素选中(多选)' },
        { value: 'element-single-selected', label: '元素选中(单选)' },
        // { value: 'active-region', label: '背景框' },
        { value: 'view-zoom', label: '视图缩放' },
        { value: 'element-highlight', label: '元素高亮' },
        { value: 'element-highlight-by-color', label: '同色元素高亮' },
        { value: 'element-highlight-by-x', label: '同X轴元素高亮' },
        { value: 'legend-filter', label: '图例过滤' },
        { value: 'legend-active', label: '图例聚焦' },
        { value: 'legend-highlight', label: '图例高亮' },
        { value: 'brush', label: '选框过滤' },
      ]
    }
  ]
}
src/menu/components/chart/antv-scatter/chartcompile/index.jsx
New file
@@ -0,0 +1,271 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Modal, Form, Row, Col, Select, Icon, Radio, Tooltip, Input, InputNumber, Tabs } from 'antd'
import { getBaseForm, getOptionForm } from './formconfig'
import asyncComponent from '@/utils/asyncComponent'
import ColorSketch from '@/mob/colorsketch'
import './index.scss'
const { TabPane } = Tabs
const NormalForm = asyncComponent(() => import('@/menu/components/share/normalform'))
class LineChartDrawerForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,
    plot: PropTypes.object,
    config: PropTypes.object,
    plotchange: PropTypes.func
  }
  state = {
    view: 'normal',
    visible: false,
    plot: null,
    formlist: null,
    baseFormlist: null
  }
  showDrawer = () => {
    const { config } = this.props
    this.setState({
      visible: true,
      view: 'normal',
      plot: fromJS(config.plot).toJS(),
      baseFormlist: getBaseForm(config.plot),
      formlist: getOptionForm(config.plot, config.columns)
    })
  }
  getFields() {
    const { formlist } = this.state
    const { getFieldDecorator } = this.props.form
    const fields = []
    if (!formlist) {
      return fields
    }
    formlist.forEach((item, index) => {
      if (item.hidden || item.forbid) return
      if (item.type === 'text') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<Input placeholder="" autoComplete="off" disabled={item.readonly} onPressEnter={this.onSubmit}/>)}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'number') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.input'] + item.label + '!'
                  }
                ]
              })(<InputNumber min={item.min} max={item.max} precision={item.decimal} onPressEnter={this.onSubmit}/>)}
            </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 mode={item.multi ? 'multiple' : ''}>
                  {item.options.map((option, index) =>
                    <Select.Option key={index} value={option.field || option.value}>
                      {option.label || option.text}
                    </Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'radio') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
              <Tooltip placement="topLeft" title={item.tooltip}>
                <Icon type="question-circle" />
                {item.label}
              </Tooltip> : item.label
            }>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <Radio.Group 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 === 'color') {
        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
              })(
                <ColorSketch />
              )}
            </Form.Item>
          </Col>
        )
      }
    })
    return fields
  }
  onSubmit = () => {
    const { config } = this.props
    const { plot, view } = this.state
    if (view === 'normal') {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          let _plot = {...plot, ...values}
          this.setState({
            plot: _plot,
            visible: false
          })
          this.props.plotchange({...config, plot: _plot})
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        let _plot = {...plot, ...res}
        this.setState({
          plot: _plot,
          visible: false
        })
        this.props.plotchange({...config, plot: _plot})
      })
    }
  }
  changeTab = (tab) => {
    const { plot, view } = this.state
    if (view === 'normal') {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          this.setState({
            plot: {...plot, ...values},
            view: tab
          })
        }
      })
    } else if (view === 'base') {
      this.baseRef.handleConfirm().then(res => {
        this.setState({
          plot: {...plot, ...res},
          view: tab
        })
      })
    }
  }
  render() {
    const { view, visible, baseFormlist } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 18 }
      }
    }
    return (
      <div className="line-chart-drawer-form">
        <Icon type="edit" title="编辑" onClick={this.showDrawer} />
        <Modal
          wrapClassName="popview-modal menu-chart-edit-modal"
          title="图表编辑"
          visible={visible}
          width={850}
          maskClosable={false}
          onOk={this.onSubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <Tabs activeKey={view} className="menu-chart-edit-box" onChange={this.changeTab}>
            <TabPane tab="组件设置" key="base">
              <NormalForm dict={this.props.dict} formlist={baseFormlist} inputSubmit={this.onSubmit} wrappedComponentRef={(inst) => this.baseRef = inst}/>
            </TabPane>
            <TabPane tab="图表设置" key="normal">
              <Form {...formItemLayout}>
                <Row gutter={16}>{this.getFields()}</Row>
              </Form>
            </TabPane>
          </Tabs>
        </Modal>
      </div>
    );
  }
}
export default Form.create()(LineChartDrawerForm)
src/menu/components/chart/antv-scatter/chartcompile/index.scss
New file
@@ -0,0 +1,41 @@
.line-chart-drawer-form {
  display: inline-block;
  > .anticon-edit {
    color: #1890ff;
  }
}
.menu-chart-edit-modal {
  .ant-modal {
    top: 50px;
    .ant-modal-body {
      max-height: calc(100vh - 190px);
      min-height: 50vh;
      padding-top: 10px;
      .menu-chart-edit-box {
        .anticon-question-circle {
          color: #c49f47;
          position: relative;
          left: -3px;
        }
        .ant-input-number {
          width: 100%;
        }
        .ant-tabs-nav-wrap {
          text-align: center;
        }
        .color-sketch-block {
          position: relative;
          top: 5px;
          width: 240px;
        }
        .color-add {
          float: right;
          margin-bottom: 10px;
          position: relative;
          z-index: 1;
        }
      }
    }
  }
}
src/menu/components/chart/antv-scatter/index.jsx
New file
@@ -0,0 +1,404 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover, notification } from 'antd'
import { Chart } from '@antv/g2'
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.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 ChartCompileForm = asyncIconComponent(() => import('./chartcompile'))
const LogComponent = asyncIconComponent(() => import('@/menu/components/share/logcomponent'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
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 antvScatterChart extends Component {
  static propTpyes = {
    card: PropTypes.object,
    updateConfig: PropTypes.func,
    deletecomponent: PropTypes.func,
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    card: null,
    appType: sessionStorage.getItem('appType'),
    eventListener: null
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    if (card.isNew) {
      let _plot = {
        width: card.width || 24,
        height: 400,
        gender: '',
        Xaxis: '',
        Xunit: '',
        Yaxis: '',
        Yunit: '',
        shape: 'circle',
        color: 'rgba(0, 0, 0, 0.65)',
        name: card.name
      }
      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: _plot.width,
        name: _plot.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        style: {
          borderWidth: '1px', borderColor: 'rgb(217, 217, 217)',
          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
        },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
        scripts: [],
        search: [],
        action: [],
        plot: _plot,
        btnlog: [],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.plot = config.plot
        _card.plot.name = card.name
        _card.style = config.style
        _card.headerStyle = config.headerStyle
        _card.action = config.action.map(col => {
          col.uuid = Utils.getuuid()
          return col
        })
        _card.search = config.search.map(col => {
          col.uuid = Utils.getuuid()
          return col
        })
      }
      this.setState({
        card: _card
      })
      this.props.updateConfig(_card)
    } else {
      this.setState({
        card: fromJS(card).toJS()
      })
    }
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    setTimeout(() => {
      this.ponitrender()
    }, 1000)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
  }
  handleTabsChange = (parentId) => {
    const { card } = this.state
    if (parentId === card.parentId) {
      let _element = document.getElementById(card.uuid + 'canvas')
      if (_element) {
        _element.innerHTML = ''
      }
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(this.ponitrender, 100)
    }
  }
  getdata = () => {
    let data = []
    for (let i = 0; i < 500; i++) {
      let item = {}
      let n = Math.random()
      let m = Math.random()
      if (n > 0.7) {
        n = n * n
      } else if (n < 0.3) {
        n = Math.sqrt(n)
      }
      if (m > 0.7) {
        m = m * m
      } else if (m < 0.3) {
        m = Math.sqrt(m)
      }
      if (i % 2 === 0) {
        item.gender = 'male'
        item.height = 160 + Math.floor(n * 35 * 10) / 10
        item.weight = 50 + Math.floor(m * 55 * 10) / 10
      } else {
        item.gender = 'female'
        item.height = 140 + Math.floor(n * 40 * 10) / 10
        item.weight = 41 + Math.floor(m * 45 * 10) / 10
      }
      data.push(item)
    }
    return data
  }
  /**
   * @description 散点图
   */
  ponitrender = () => {
    const { card } = this.state
    const plot = card.plot
    const data = this.getdata()
    let height = plot.height - 25
    if (card.plot.title || card.search.length > 0) {
      height = plot.height - 70
    }
    const chart = new Chart({
      container: card.uuid + 'canvas',
      autoFit: true,
      height: height
    })
    chart.data(data);
    chart.scale({
      height: { nice: true },
      weight: { nice: true },
    })
    // chart.axis('height', { label: { style: { fill: plot.color } }, tickLine: {style: { stroke: plot.color }}, line: { style: { stroke: plot.color } } })
    // chart.axis('weight', { grid: { line: { style: { stroke: plot.color } }}, label: { style: { fill: plot.color } } })
    chart.axis('height', { label: { style: { fill: plot.color } } })
    chart.axis('weight', { label: { style: { fill: plot.color } } })
    chart.legend({
      position: plot.legend || 'bottom',
      itemName: { style: { fill: plot.color } }
    })
    if (plot.tooltip !== 'true') {
      chart.tooltip(false)
    } else {
      chart.tooltip({
        showTitle: false,
        showCrosshairs: true,
        crosshairs: {
          type: 'xy',
        }
      })
    }
    chart
      .point()
      .position('height*weight')
      .color('gender')
      .shape(plot.shape)
      .tooltip('gender*height*weight', (gender, height, weight) => {
        return {
          name: gender,
          value: height + (plot.Xunit ? `(${plot.Xunit}), ` : ', ') + weight + (plot.Yunit ? `(${plot.Yunit})` : '')
        };
      })
      .style({
        fillOpacity: 0.85
      })
    if (plot.interaction && plot.interaction.length) {
      plot.interaction.forEach(t => {
        chart.interaction(t)
      })
    }
    chart.render()
  }
  updateComponent = (component) => {
    const card = fromJS(this.state.card).toJS()
    if (!is(fromJS(component.plot), fromJS(card.plot)) || !is(fromJS(component.style), fromJS(card.style))) {
      let _element = document.getElementById(card.uuid + 'canvas')
      if (_element) {
        _element.innerHTML = ''
      }
      this.$timer && clearTimeout(this.$timer)
      this.$timer = setTimeout(() => {
        this.ponitrender()
      }, 150)
    }
    component.width = component.plot.width
    component.name = component.plot.name
    this.setState({
      card: component
    })
    this.props.updateConfig(component)
  }
  addSearch = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = 'label'
    newcard.initval = ''
    newcard.type = 'select'
    newcard.resourceType = '0'
    newcard.options = []
    newcard.setAll = 'false'
    newcard.orderType = 'asc'
    newcard.display = 'dropdown'
    newcard.match = '='
    // 注册事件-添加搜索
    MKEmitter.emit('addSearch', card.uuid, newcard)
  }
  addButton = () => {
    const { card } = this.state
    let newcard = {}
    newcard.uuid = Utils.getuuid()
    newcard.focus = true
    newcard.label = '导出Excel'
    newcard.sqlType = ''
    newcard.Ot = 'requiredSgl'
    newcard.OpenType = 'excelOut'
    newcard.icon = 'download'
    newcard.class = 'dgreen'
    newcard.intertype = card.setting.interType
    newcard.innerFunc = card.setting.innerFunc || ''
    newcard.sysInterface = card.setting.sysInterface || ''
    newcard.outerFunc = card.setting.outerFunc || ''
    newcard.interface = card.setting.interface || ''
    newcard.execSuccess = 'grid'
    newcard.execError = 'never'
    newcard.popClose = 'never'
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'icon'
    // 注册事件-添加按钮
    MKEmitter.emit('addButton', card.uuid, newcard)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['background', 'border', 'padding', 'margin'], card.style)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid || comIds.length > 1) return
    let _card = {...card, style}
    this.updateComponent(_card)
  }
  handleLog = (type, logs, item) => {
    let card = fromJS(this.state.card).toJS()
    if (type === 'revert') {
      card.action = card.action ? [...card.action, item] : [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, appType } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-scatter-chart-edit-box" style={{..._style, height: card.plot.height || 400}} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            {appType !== 'mob' ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle"/> : null}
            {appType !== 'mob' ? <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" /> : null}
            <ChartCompileForm config={card} dict={this.state.dict} plotchange={this.updateComponent}/>
            <CopyComponent type="line" card={card}/>
            <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}/>
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.plot.title || card.search.length > 0 ? <NormalHeader config={card} updateComponent={this.updateComponent}/> : null}
        <div className="canvas" id={card.uuid + 'canvas'}></div>
        {appType !== 'mob' ? <ActionComponent type="chart" config={card} updateaction={this.updateComponent}/> : null}
      </div>
    )
  }
}
export default antvScatterChart
src/menu/components/chart/antv-scatter/index.scss
New file
@@ -0,0 +1,72 @@
.menu-scatter-chart-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  .canvas {
    margin: 0px;
    padding: 15px 10px 10px;
    letter-spacing: 0px;
    height: 100%;
  }
  .normal-header + .canvas {
    height: calc(100% - 45px);
  }
  .chart-header {
    position: relative;
    height: 45px;
    border-bottom: 1px solid #e8e8e8;
    overflow: hidden;
    padding-right: 35px;
    .chart-title {
      text-decoration: inherit;
      font-weight: inherit;
      font-style: inherit;
      float: left;
      line-height: 45px;
      margin-left: 10px;
      position: relative;
      z-index: 1;
    }
  }
  >.anticon-tool {
    position: absolute;
    right: 1px;
    top: 1px;
    z-index: 2;
    font-size: 16px;
    padding: 5px;
    cursor: pointer;
    color: rgba(0, 0, 0, 0.85);
    background: rgba(255, 255, 255, 0.55);
  }
  .model-menu-action-list {
    position: absolute;
    right: 0px;
    top: 30px;
    z-index: 4;
    font-size: 16px;
    .ant-row .anticon-plus {
      float: right;
    }
    .page-card {
      float: right;
    }
  }
  .normal-header + .canvas + .model-menu-action-list {
    top: 45px;
  }
}
.menu-scatter-chart-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/code/sandbox/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
@@ -152,8 +152,10 @@
  render() {
    const { card } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-editor-sand-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-editor-sand-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent} />
src/menu/components/form/formaction/actionform/index.jsx
@@ -49,11 +49,11 @@
    const { card } = this.props
    if (card.type === 'prev') {
      return ['type', 'label']
      return ['type', 'label', 'enable']
    } else if (card.type === 'next') {
      return ['type', 'label', 'enable']
    }
    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open'] // 选项列表
    let _options = ['type', 'label', 'intertype', 'syncComponent', 'linkmenu', 'open', 'enable'] // 选项列表
    
    if (_intertype === 'custom') {
      _options.push('procMode', 'interface', 'callbackType', 'cbTable', 'proInterface', 'method', 'cross')
src/menu/components/form/formaction/formconfig.jsx
@@ -10,7 +10,7 @@
 */
export function getActionForm (card, functip, tableName, usefulFields, modules) {
  const isApp = sessionStorage.getItem('appType') === 'pc'
  const appType = sessionStorage.getItem('appType')
  let _type = '提交'
  if (card.type === 'prev') {
    _type = '上一步'
@@ -19,7 +19,7 @@
  }
  let menulist = []
  if (isApp) {
  if (appType === 'pc' || appType === 'mob') {
    menulist = sessionStorage.getItem('appMenus')
    if (menulist) {
      try {
@@ -242,7 +242,7 @@
      readonly: false
    },
    {
      type: isApp ? 'select' : 'cascader',
      type: (appType === 'pc' || appType === 'mob') ? 'select' : 'cascader',
      key: 'linkmenu',
      label: '打开菜单',
      tooltip: '执行成功后需要打开的菜单。',
@@ -256,7 +256,7 @@
      label: '打开方式',
      initVal: card.open || 'blank',
      required: false,
      forbid: !isApp,
      forbid: appType !== 'pc',
      options: [{
        value: 'blank',
        text: '新窗口'
@@ -278,7 +278,7 @@
      type: 'radio',
      key: 'enable',
      label: '是否显示',
      initVal: card.enable || 'false',
      initVal: card.enable || 'true',
      required: false,
      options: [{
        value: 'true',
src/menu/components/form/formaction/index.jsx
@@ -7,6 +7,7 @@
import enUS from '@/locales/en-US/model.js'
import asyncComponent from '@/utils/asyncComponent'
import { getActionForm } from './formconfig'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import MenuUtils from '@/utils/utils-custom.js'
@@ -23,6 +24,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,          // 编辑中元素
    formlist: null,      // 表单信息
    visible: false,      // 模态框控制
@@ -172,6 +174,21 @@
    })
  }
  changeMenu = () => {
    const { appType } = this.state
    const { group } = this.props
    if (appType !== 'pc' && appType !== 'mob') return
    if (!group.subButton.linkmenu) return
    MKEmitter.emit('changeEditMenu', {
      MenuID: group.subButton.linkmenu,
      copyMenuId: '',
      MenuNo: '',
      MenuName: '',
    })
  }
  render() {
    const { group, config } = this.props
    const { visible, profVisible, card, dict } = this.state
@@ -184,7 +201,7 @@
            <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>
          <Button type="link" className={'prev ' + group.prevButton.enable} style={resetStyle(group.prevButton.style)}>{group.prevButton.label}</Button>
        </Popover> : null}
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
@@ -193,7 +210,7 @@
            <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>
          <Button type="link" className="submit mk-primary" onDoubleClick={this.changeMenu} style={resetStyle(group.subButton.style)}>{group.subButton.label}</Button>
        </Popover>
        {group.sort !== config.subcards.length ? <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
@@ -201,7 +218,7 @@
            <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>
          <Button type="link" className={'skip ' + group.nextButton.enable} style={resetStyle(group.nextButton.style)}>{group.nextButton.label}</Button>
        </Popover> : null}
        {/* 编辑按钮:复制、编辑 */}
        <Modal
src/menu/components/form/formaction/index.scss
@@ -6,6 +6,11 @@
  .prev {
    margin-right: 15px;
  }
  .prev.false {
    span {
      text-decoration: line-through;
    }
  }
  .submit {
    border: none;
  }
src/menu/components/form/normal-form/groupform/index.jsx
@@ -18,13 +18,22 @@
  UNSAFE_componentWillMount () {
    const { group } = this.props
    const { appType } = this.state
    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)
      }
    })
    if (appType === 'mob') {
      group.fields.forEach(f => {
        if (f.field && ['text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
          fields.push(f)
        }
      })
    } else {
      group.fields.forEach(f => {
        if (f.field && ['select', 'link', 'text', 'number'].includes(f.type) && f.hidden !== 'true' && f.readonly !== 'true') {
          fields.push(f)
        }
      })
    }
    this.setState({
      fields: fields
@@ -55,7 +64,7 @@
  render() {
    const { group, dict } = this.props
    const { getFieldDecorator } = this.props.form
    const { fields } = this.state
    const { fields, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -123,7 +132,7 @@
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
          {appType !== 'mob' ? <Col span={12}>
            <Form.Item label="表单排列">
              {getFieldDecorator('align', {
                initialValue: group.setting.align || 'left_right'
@@ -134,7 +143,7 @@
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          </Col> : null}
        </Row>
      </Form>
    )
src/menu/components/form/normal-form/index.jsx
@@ -8,16 +8,18 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import { resetStyle } from '@/utils/utils-custom.js'
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 ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
const WrapComponent = asyncIconComponent(() => import('@/menu/components/form/wrapsetting'))
const CardComponent = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
const MobCardComponent = asyncComponent(() => import('@/mob/components/formdragelement'))
const FormTitle = asyncComponent(() => import('../dragtitle'))
const GroupForm = asyncComponent(() => import('./groupform'))
const FormAction = asyncComponent(() => import('../formaction'))
@@ -37,6 +39,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,
    back: false,
    group: null,
@@ -66,7 +69,7 @@
        name: card.name,
        subtype: card.subtype,
        setting: { },
        wrap: { name: card.name, width: card.width || 24, datatype: 'static', color: '#1890ff' },
        wrap: { name: card.name, width: card.width || 24, datatype: 'static', groupLabel: 'show', color: '#1890ff' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
        columns: [],
        scripts: [],
@@ -76,8 +79,8 @@
          sort: 1,
          style: {},
          fields: [],
          prevButton: {label: '上一步', type: 'prev'},
          subButton: {label: '提交', type: 'submit', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
          prevButton: {label: '上一步', type: 'prev', enable: 'true'},
          subButton: {label: '提交', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
          nextButton: {label: '跳过', type: 'next', enable: 'false'}
        }]
      }
@@ -179,33 +182,6 @@
    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
@@ -235,8 +211,8 @@
      sort: card.subcards.length + 1,
      style: {},
      fields: [],
      prevButton: {label: '上一步', type: 'prev'},
      subButton: {label: '提交', type: 'submit', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
      prevButton: {label: '上一步', type: 'prev', enable: 'true'},
      subButton: {label: '提交', type: 'submit', enable: 'true', style: {backgroundColor: '#1890ff', color: '#ffffff', paddingLeft: '25px', paddingRight: '25px'}},
      nextButton: {label: '跳过', type: 'next', enable: 'false'}
    }
@@ -362,7 +338,7 @@
    })
  }
  handleList = (list) => {
  handleList = (list, newcard) => {
    let group = fromJS(this.state.group).toJS()
    let card = fromJS(this.state.card).toJS()
@@ -375,7 +351,11 @@
      return item
    })
    this.setState({card, group})
    this.setState({card, group}, () => {
      if (newcard) {
        this.handleForm(newcard)
      }
    })
    this.props.updateConfig(card)
  }
@@ -404,9 +384,13 @@
  }
  addForm = () => {
    const { appType } = this.state
    let group = fromJS(this.state.group).toJS()
    let lastItem = group.fields[group.fields.length - 1]
    let span = lastItem ? lastItem.span : 12
    let span = appType === 'mob' ? 24 : 12
    if (lastItem && lastItem.span) {
      span = lastItem.span
    }
    let newcard = {
      uuid: Utils.getuuid(),
@@ -439,13 +423,14 @@
    group.fields = group.fields.filter(item => !item.focus)
    this.setState({group, visible: false, editform: null})
    this.updateGroup(group)
  }
  /**
   * @description 表单编辑
   */
  handleForm = (_item) => {
    const { card, group } = this.state
    const { card, group, appType } = this.state
    let _form = fromJS(_item).toJS()
    let _inputfields = []
    let _tabfields = []
@@ -457,7 +442,11 @@
    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))
    if (appType === 'mob') {
      _tabfields = group.fields.filter(item => _form.field !== item.field && item.hidden !== 'true' && ['text', 'number'].includes(item.type))
    } else {
      _tabfields = group.fields.filter(item => _form.field !== item.field && item.hidden !== 'true' && ['text', 'number', 'select', 'link'].includes(item.type))
    }
    _tabfields.unshift({field: '', text: '原表单'})
    let uniq = new Map()
@@ -467,7 +456,8 @@
      if (_form.uuid === item.uuid) {
        index = i
      }
      if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
      if (!['select', 'link', 'radio', 'checkcard'].includes(item.type)) return
      if (item.field && !uniq.has(item.field)) {
        uniq.set(item.field, true)
@@ -505,7 +495,7 @@
      _form.linkSubField = _form.linkSubField.filter(item => fields.includes(item))
    }
    if (!_form.span && standardform && standardform.span) {
    if (appType !== 'mob' && !_form.span && standardform && standardform.span) {
      _form.span = standardform.span
      _form.labelwidth = standardform.labelwidth
    }
@@ -560,7 +550,7 @@
        return
      }
      if ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && res.resourceType === '1' && /\s/.test(res.dataSource)) {
      if (['select', 'multiselect', 'link', 'checkbox', 'radio', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
@@ -607,6 +597,47 @@
    })
  }
  pasteForm = (res) => {
    let _config = fromJS(this.state.group).toJS()
    let fieldrepet = false // 字段重复
    let labelrepet = false // 提示文字重复
    _config.fields.forEach(item => {
      if (res.field && item.field && item.field.toLowerCase() === res.field.toLowerCase()) {
        fieldrepet = true
      } else if (res.label && item.label === res.label) {
        labelrepet = true
      }
    })
    if (fieldrepet) {
      notification.warning({
        top: 92,
        message: '字段已存在!',
        duration: 10
      })
      return
    } else if (labelrepet) {
      notification.warning({
        top: 92,
        message: '名称已存在!',
        duration: 10
      })
      return
    }
    _config.fields.push(res)
    this.updateGroup(_config)
    this.handleForm(res)
    notification.success({
      top: 92,
      message: '粘贴成功!',
      duration: 2
    })
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
@@ -615,16 +646,16 @@
  }
  render() {
    const { card, dict, group } = this.state
    const { card, dict, group, appType } = this.state
    return (
      <div className="menu-normal-form-edit-box" style={{...card.style}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-normal-form-edit-box" style={resetStyle(card.style)} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加分组" onClick={this.addCard} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="propcard" card={card}/>
            <PasteComponent config={card} options={['cardcell']} updateConfig={this.updateComponent} />
            <PasteComponent config={card} options={['form']} updateConfig={this.pasteForm} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <UserComponent config={card}/>
            <Icon className="close" title="删除组件" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
@@ -646,12 +677,12 @@
          <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>
          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(1)}>1列</Button> : null}
          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(2)}>2列</Button> : null}
          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(3)}>3列</Button> : null}
          {appType !== 'mob' ? <Button className="mk-cols-change" onClick={() => this.changecols(4)}>4列</Button> : null}
          <div style={{clear: 'both'}}></div>
          <CardComponent
          {appType !== 'mob' ? <CardComponent
            list={group.fields}
            setting={group.setting}
            showField={this.state.showField}
@@ -659,7 +690,14 @@
            handleList={this.handleList}
            handleForm={this.handleForm}
            closeForm={this.closeForm}
          />
          /> : <MobCardComponent
            list={group.fields}
            setting={group.setting}
            showField={this.state.showField}
            handleList={this.handleList}
            handleForm={this.handleForm}
            closeForm={this.closeForm}
          />}
          <FormAction config={card} group={group} updateconfig={this.updateGroup}/>
        </div> : null}
        <Modal
src/menu/components/form/wrapsetting/settingform/index.jsx
@@ -149,6 +149,23 @@
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="加载时是否显示分组名称。">
                  <Icon type="question-circle" />
                  分组名称
                </Tooltip>
              }>
                {getFieldDecorator('groupLabel', {
                  initialValue: wrap.groupLabel || 'show'
                })(
                  <Radio.Group>
                    <Radio value="show">显示</Radio>
                    <Radio value="hidden">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="完成后的颜色">
                  <Icon type="question-circle" />
                  颜色控制
src/menu/components/form/wrapsetting/settingform/index.scss
@@ -9,6 +9,7 @@
    width: 100%;
  }
  .color-sketch-block {
    margin-top: 6px;
    position: relative;
    top: 6px;
  }
}
src/menu/components/group/groupcomponents/card.jsx
@@ -9,8 +9,15 @@
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 NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -50,6 +57,14 @@
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'dashboard') {
      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tree') {
      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'scatter') {
      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'card' && card.subtype === 'datacard') {
      return (<DataCard card={card} updateConfig={updateConfig} deletecomponent={delCard} />)
    } else if (card.type === 'card' && card.subtype === 'propcard') {
@@ -58,8 +73,14 @@
      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 === '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 === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
src/menu/components/group/groupcomponents/index.jsx
@@ -6,6 +6,7 @@
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'
@@ -38,49 +39,16 @@
  const deleteCard = (id) => {
    const { card } = findCard(id)
    let uuids = []
    if (card.action && card.action.length) {
      card.action.forEach(act => {
        if (!act.origin) {
          uuids.push(act.uuid)
        }
      })
    }
    if (card.type === 'card') {
      card.subcards.forEach(_card => {
        _card.elements && _card.elements.forEach(cell => {
          if (cell.eleType === 'button') {
            uuids.push(cell.uuid)
          }
        })
        _card.backElements && _card.backElements.forEach(cell => {
          if (cell.eleType === 'button') {
            uuids.push(cell.uuid)
          }
        })
      })
    } else if (card.type === 'table' && card.subtype === 'tablecard') {
      card.subcards.forEach(_card => {
        _card.elements && _card.elements.forEach(cell => {
          if (cell.eleType === 'button') {
            uuids.push(cell.uuid)
          }
        })
      })
    } else if (card.type === 'table' && card.subtype === 'normaltable') {
      card.cols && card.cols.forEach(col => {
        if (col.type !== 'action') return
        col.elements && col.elements.forEach(cell => {
          uuids.push(cell.uuid)
        })
      })
    }
    let uuids = MenuUtils.getDelButtonIds(card)
    confirm({
      title: `确定删除《${card.name}》吗?`,
      onOk() {
        MKEmitter.emit('delButtons', uuids)
        handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -99,11 +67,20 @@
      let name = ''
      let names = {
        bar: '柱状图',
        bbar: '柱状图',
        line: '折线图',
        tabs: '标签组',
        pie: '饼图',
        search: '搜索',
        table: '表格',
        group: '分组',
        editor: '富文本',
        code: '自定义',
        carousel: '轮播',
        form: '表单',
        dashboard: '仪表盘',
        scatter: '散点图',
        tree: '树形列表',
        card: '卡片'
      }
      let i = 1
src/menu/components/group/groupcomponents/index.scss
@@ -1,9 +1,6 @@
.group-shell-inner {
  margin: -8px;
  margin: 0px;
  >.ant-col {
    padding: 8px;
  }
  .anticon {
    cursor: unset;
  }
src/menu/components/group/groupsetting/settingform/index.jsx
@@ -13,6 +13,7 @@
  state = {
    roleList: [],
    appType: sessionStorage.getItem('appType'),
    print: this.props.setting.print || 'false'
  }
@@ -55,7 +56,7 @@
  render() {
    const { setting, dict } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList, print } = this.state
    const { roleList, print, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -108,7 +109,7 @@
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col>
            <Col span={12}>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label="打印按钮">
                {getFieldDecorator('print', {
                  initialValue: print
@@ -119,8 +120,8 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {print === 'true' ? <Col span={12}>
            </Col> : null}
            {print === 'true' && appType !== 'mob' ? <Col span={12}>
              <Form.Item label="打印尺寸">
                {getFieldDecorator('pageSize', {
                  initialValue: setting.pageSize || 'A4',
@@ -139,7 +140,7 @@
                )}
              </Form.Item>
            </Col> : null}
            {print === 'true' ? <Col span={12}>
            {print === 'true' && appType !== 'mob' ? <Col span={12}>
              <Form.Item label="打印布局">
                {getFieldDecorator('pageLayout', {
                  initialValue: setting.pageLayout || 'vertical',
src/menu/components/group/normal-group/index.jsx
@@ -6,7 +6,7 @@
import MKEmitter from '@/utils/events.js'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import './index.scss'
@@ -161,9 +161,15 @@
  render() {
    const { group } = this.state
    let _style = resetStyle(group.style)
    let paddingTop = true
    if (group.style.paddingTop && parseInt(group.style.paddingTop) >= 28) {
      paddingTop = false
    }
    return (
      <div className="menu-group-edit-box" style={group.style} onClick={this.clickComponent} id={group.uuid}>
      <div className={'menu-group-edit-box' + (paddingTop ? ' padding' : '')} style={_style} onClick={this.clickComponent} id={group.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <SettingComponent config={group} updateConfig={this.updateComponent} />
src/menu/components/group/normal-group/index.scss
@@ -26,7 +26,7 @@
    color: #bcbcbc;
  }
}
.menu-group-edit-box::before {
.menu-group-edit-box.padding:before {
  content: ' ';
  display: block;
  float: right;
src/menu/components/search/main-search/dragsearch/card.jsx
@@ -3,12 +3,16 @@
import { Icon, Select, DatePicker, Input, Popover, Form } from 'antd'
import moment from 'moment'
import asyncComponent from '@/utils/asyncComponent'
import DateGroup from '../dategroup'
import './index.scss'
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
const { Search } = Input
const CheckCard = asyncComponent(() => import('@/templates/modalconfig/checkCard'))
const appType = sessionStorage.getItem('appType')
const Card = ({ id, card, moveCard, copyCard, findCard, editCard, delCard }) => {
const Card = ({ id, card, showField, moveCard, copyCard, findCard, editCard, delCard }) => {
  const originalIndex = findCard(id).index
  const [{ isDragging }, drag] = useDrag({
    item: { type: 'search', id, originalIndex },
@@ -55,6 +59,38 @@
    }
  }
  let formItem = null
  if (card.type === 'text') {
    if (card.inputType === 'search') {
      formItem = (<Search style={{marginTop: '4px'}} placeholder={card.labelShow === 'false' ? card.label : ''} value={card.initval} enterButton />)
    } else {
      formItem = (<Input style={{marginTop: '4px'}} placeholder={card.labelShow === 'false' ? card.label : ''} value={card.initval} />)
    }
  } else if (card.type === 'multiselect' || card.type === 'select' || card.type === 'link') {
    formItem = (<Select value={_defaultValue}></Select>)
  } else if (card.type === 'date' && appType === 'mob') {
    formItem = (<div className="mob-list-item">{card.initval ? moment().subtract(card.initval, 'days').format('YYYY-MM-DD') : '请选择'}<Icon type="right" /></div>)
  } else if (card.type === 'datemonth' && appType === 'mob') {
    formItem = (<div className="mob-list-item">{card.initval ? moment().subtract(card.initval, 'month').format('YYYY-MM') : '请选择'}<Icon type="right" /></div>)
  } else if (card.type === 'date') {
    formItem = (<Input style={{marginTop: '4px'}} placeholder={card.labelShow === 'false' ? card.label : ''} value={card.initval} />)
  } else if (card.type === 'dateweek') {
    formItem = (<WeekPicker value={card.initval ? moment().subtract(card.initval * 7, 'days') : null} />)
  } else if (card.type === 'datemonth') {
    formItem = (<MonthPicker value={card.initval ? moment().subtract(card.initval, 'month') : null} />)
  } else if (card.type === 'daterange') {
    formItem = (<RangePicker
      className="data-range"
      placeholder={['BeginTime', 'EndTime']}
      renderExtraFooter={() => 'extra footer'}
      value={_defaultValue}
    />)
  } else if (card.type === 'group') {
    formItem = (<DateGroup card={card} />)
  } else if (card.type === 'checkcard') {
    formItem = <CheckCard config={card} />
  }
  return (
    <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
      <div className="mk-popover-control">
@@ -63,38 +99,16 @@
        <Icon className="close" title="delete" type="close" onClick={() => delCard(id)} />
      </div>
    } trigger="hover">
      <div className={'page-card ' + card.labelShow} style={{ opacity: opacity}}>
      <div className={'page-card ' + card.labelShow + ' ' + card.type} style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          <Form.Item
            labelCol={{xs: { span: 24 }, sm: { span: 8 }}}
            wrapperCol = {{xs: { span: 24 }, sm: { span: 16 }}}
            label={card.labelShow !== 'false' ? card.label : ''}
            required={card.required === 'true'}
            help={showField ? card.field + (card.datefield ? ', ' + card.datefield : '') : ''}
          >
            {card.type === 'text' ?
              <Input style={{marginTop: '4px'}} placeholder={card.labelShow === 'false' ? card.label : ''} value={card.initval} /> : null
            }
            {(card.type === 'multiselect' || card.type === 'select' || card.type === 'link') ?
              <Select value={_defaultValue}></Select> : null
            }
            {card.type === 'date' ?
              <DatePicker value={card.initval ? moment().subtract(card.initval, 'days') : null} /> : null
            }
            {card.type === 'dateweek' ?
              <WeekPicker value={card.initval ? moment().subtract(card.initval * 7, 'days') : null} /> : null
            }
            {card.type === 'datemonth' ?
              <MonthPicker value={card.initval ? moment().subtract(card.initval, 'month') : null} /> : null
            }
            {card.type === 'daterange' ?
              <RangePicker
                className="data-range"
                placeholder={['BeginTime', 'EndTime']}
                renderExtraFooter={() => 'extra footer'}
                value={_defaultValue}
              /> : null
            }
            {card.type === 'group' ? <DateGroup card={card} /> : null }
            {formItem}
          </Form.Item>
        </div>
      </div>
src/menu/components/search/main-search/dragsearch/index.jsx
@@ -7,7 +7,7 @@
import Card from './card'
import './index.scss'
const Container = ({list, placeholder, handleList, handleMenu, deleteMenu }) => {
const Container = ({list, showField, placeholder, handleList, handleMenu, deleteMenu }) => {
  const [cards, setCards] = useState(list)
  const moveCard = (id, atIndex) => {
    const { card, index } = findCard(id)
@@ -77,13 +77,27 @@
    drop() {}
  })
  const appType = sessionStorage.getItem('appType')
  return (
    <div ref={drop} className="ant-row">
      {cards.length > 0 ? <Col key="preaction" className="action pre-action" span={6}>
        <div className="ant-row ant-form-item" style={{lineHeight: '40px', height: '55px', marginBottom: 0}}>
          <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
          </div>
          <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
            <Button type="primary">搜索</Button>
            {appType !== 'mob' ? <Button style={{ marginLeft: 8 }}>重置</Button> : null}
            <div style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}></div>
          </div>
        </div>
      </Col> : null}
      {cards.map(card => (
        <Col key={card.uuid} span={card.ratio || 6}>
          <Card
            id={`${card.uuid}`}
            card={card}
            showField={showField}
            moveCard={moveCard}
            copyCard={copyCard}
            editCard={editCard}
@@ -92,13 +106,13 @@
          />
        </Col>
      ))}
      {cards.length > 0 ? <Col key="action" className="action" span={6}>
      {cards.length > 0 ? <Col key="nextaction" className="action next-action" span={6}>
        <div className="ant-row ant-form-item" style={{lineHeight: '40px', height: '55px', marginBottom: 0}}>
          <div className="ant-col ant-form-item-label ant-col-xs-24 ant-col-sm-8">
          </div>
          <div className="ant-col ant-form-item-control-wrapper ant-col-xs-24 ant-col-sm-16">
            <Button type="primary">搜索</Button>
            <Button style={{ marginLeft: 8 }}>重置</Button>
            {appType !== 'mob' ? <Button style={{ marginLeft: 8 }}>重置</Button> : null}
            <div style={{position: 'absolute', top: 0, bottom: 0, left: 0, right: 0}}></div>
          </div>
        </div>
src/menu/components/search/main-search/index.jsx
@@ -1,7 +1,7 @@
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, notification, Popover, Icon } from 'antd'
import { Modal, notification, Popover, Icon, Switch } from 'antd'
import moment from 'moment'
import Api from '@/api'
@@ -9,9 +9,8 @@
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import { getSearchForm } from '@/templates/zshare/formconfig'
import { resetStyle } from '@/utils/utils-custom.js'
import asyncIconComponent from '@/utils/asyncIconComponent'
import SearchForm from '@/templates/sharecomponent/searchcomponent/searchform'
import DragElement from './dragsearch'
import MKEmitter from '@/utils/events.js'
import './index.scss'
@@ -19,6 +18,7 @@
const { confirm } = Modal
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const SearchForm = asyncIconComponent(() => import('@/templates/sharecomponent/searchcomponent/searchform'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const PasteComponent = asyncIconComponent(() => import('@/menu/components/share/pastecomponent'))
@@ -31,9 +31,11 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    searchlist: null,    // 搜索条件集
    sqlVerifing: false,  // sql验证中
    visible: false,      // 模态框控制
    showField: false,
    editcard: null       // 编辑中元素
  }
@@ -53,7 +55,7 @@
        width: 24,
        name: card.name,
        subtype: card.subtype,
        wrap: { name: card.name, width: 24, show: 'true', float: 'left' },
        wrap: { name: card.name, width: 24, show: this.state.appType === 'mob' ? 'false' : 'true', float: 'left' },
        style: {
          marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px'
        },
@@ -120,6 +122,20 @@
    component.name = component.wrap.name
    this.props.updateConfig(component)
  }
  checkComponent = (component) => {
    this.updateComponent(component)
    let _item = null
    component.search.forEach(item => {
      if (!_item && item.focus) {
        _item = item
      }
    })
    if (_item) {
      this.handleSearch(_item)
    }
  }
  /**
@@ -248,7 +264,7 @@
        return
      }
      if (['select', 'multiselect', 'link'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
      if (['select', 'multiselect', 'link', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
@@ -339,6 +355,14 @@
    })
  }
  onFieldChange = () => {
    const { showField } = this.state
    this.setState({
      showField: !showField
    })
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
@@ -347,12 +371,15 @@
  }
  render() {
    const { dict, card, visible, sqlVerifing } = this.state
    const { dict, card, visible, sqlVerifing, showField } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className={`main-search-edit-list ${card.wrap.float} ${card.wrap.show || ''}`} onClick={this.clickComponent} id={card.uuid} style={card.style}>
      <div className={`main-search-edit-list ${card.wrap.float} ${card.wrap.show || ''}`} onClick={this.clickComponent} id={card.uuid} style={_style}>
        <Switch checkedChildren={dict['model.switch.open']} size="small" unCheckedChildren={dict['model.switch.close']} defaultChecked={showField} onChange={this.onFieldChange} />
        <DragElement
          list={card.search}
          showField={showField}
          handleList={this.handleList}
          handleMenu={this.handleSearch}
          deleteMenu={this.deleteElement}
@@ -363,7 +390,7 @@
            <Icon className="plus" title="添加" onClick={this.addSearch} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent}/>
            <CopyComponent type="mainsearch" card={card}/>
            <PasteComponent config={card} options={['search', 'form']} updateConfig={this.updateComponent} />
            <PasteComponent config={card} options={['search', 'form']} updateConfig={this.checkComponent} />
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(card.uuid)} />
          </div>
src/menu/components/search/main-search/index.scss
@@ -2,7 +2,17 @@
  min-height: 50px;
  position: relative;
  background: #ffffff;
  padding-bottom: 15px;
  .ant-form-item-control {
    line-height: 1.5;
  }
  >.ant-switch {
    position: absolute;
    z-index: 3;
    right: 10px;
    bottom: 5px;
  }
  >.anticon-tool {
    position: absolute;
    z-index: 3;
@@ -15,9 +25,16 @@
  }
  > .ant-row {
    min-height: 65px;
    > .ant-col {
      padding: 0 12px!important;
    }
  }
  .ant-row .ant-col-6 {
    padding: 0 12px!important;
  >.ant-row:not(.ant-form-item) {
    > .ant-col {
      display: inline-block;
      float: none;
      vertical-align: top;
    }
  }
  .ant-row.ant-form-item .ant-col {
    padding: 0;
@@ -73,14 +90,31 @@
    min-width: 100px!important;
    width: 100%;
  }
  .check-card-edit-box {
    margin-top: 5px!important;
  }
  .mob-list-item {
    text-align: right;
    line-height: 40px;
    white-space: nowrap;
    padding-right: 5px;
    i {
      margin-left: 5px;
    }
  }
}
.main-search-edit-list:not(.right) {
  .pre-action {
    display: none!important;
  }
}
.main-search-edit-list.right {
  .next-action {
    display: none!important;
  }
  >.ant-row {
    >.ant-col {
      float: right;
    }
    >.ant-col.action {
      display: none;
    }
  }
}
src/menu/components/search/main-search/wrapsetting/settingform/index.jsx
@@ -12,8 +12,8 @@
  }
  state = {
    float: this.props.wrap.float,
    roleList: []
    roleList: [],
    appType: sessionStorage.getItem('appType')
  }
  UNSAFE_componentWillMount () {
@@ -55,7 +55,7 @@
  render() {
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { float, roleList } = this.state
    const { roleList, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -108,6 +108,18 @@
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="高级搜索弹窗的宽度,注:当宽度值小于100时表示占窗口的百分比,大于100时表示宽度的绝对值。">
                  <Icon type="question-circle" />
                  高级搜索
                </Tooltip>
              }>
                {getFieldDecorator('advanceWidth', {
                  initialValue: wrap.advanceWidth || 1000
                })(<InputNumber min={10} max={3000} precision={0}/>)}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="右对齐时,隐藏搜索按钮。">
@@ -118,14 +130,14 @@
                {getFieldDecorator('float', {
                  initialValue: wrap.float || 'left'
                })(
                  <Radio.Group onChange={(e) => this.setState({float: e.target.value})}>
                  <Radio.Group>
                    <Radio value="left">左对齐</Radio>
                    <Radio value="right">右对齐</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {float !== 'right' ? <Col span={12}>
            <Col span={12}>
              <Form.Item label="搜索按钮">
                {getFieldDecorator('show', {
                  initialValue: wrap.show || 'true'
@@ -136,7 +148,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            </Col>
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
src/menu/components/share/actioncomponent/actionform/index.jsx
@@ -2,15 +2,17 @@
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Row, Col, Input, Select, Icon, Radio, notification, Tooltip, InputNumber, Cascader } from 'antd'
import { btnIcons, btnCustomClasses, formRule } from '@/utils/option.js'
import { btnCustomClasses, formRule } from '@/utils/option.js'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const { TextArea } = Input
const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
const actionTypeOptions = {
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width'],
  pop: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
  prompt: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
  exec: ['label', 'OpenType', 'intertype', 'Ot', 'show', 'icon', 'class', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width', 'openmenu', 'open'],
  excelIn: ['label', 'Ot', 'OpenType', 'intertype', 'show', 'icon', 'class', 'sheet', 'execSuccess', 'execError', 'resetPageIndex', 'syncComponent', 'width'],
  excelOut: ['label', 'OpenType', 'intertype', 'show', 'icon', 'class', 'execSuccess', 'execError', 'syncComponent', 'resetPageIndex', 'pagination', 'search', 'width'],
  popview: ['label', 'Ot', 'OpenType', 'show', 'icon', 'class', 'popClose', 'resetPageIndex', 'width'],
@@ -35,6 +37,8 @@
    interType: null, // 接口类型:内部、外部
    funcType: null,  // 功能类型
    procMode: null,  // 参数方式
    pageTemplate: null,
    Ot: null,
    requireOptions: [{
      value: 'notRequired',
      text: this.props.dict['header.form.notRequired']
@@ -90,15 +94,19 @@
    let _opentype = card.OpenType                // 打开方式
    let _intertype = card.intertype || 'system'  // 接口类型
    let _funcType = card.funcType || ''          // 功能按钮默认类型
    let _procMode = card.procMode || 'system'    // 参数请求方式
    let _procMode = card.procMode || 'system'
    let _Ot = card.Ot || 'requiredSgl'
    let _pageTemplate = card.pageTemplate || ''
    let _options = this.getOptions(_opentype, _intertype, _funcType, card.pageTemplate, _procMode)
    let _options = this.getOptions(_opentype, _intertype, _funcType, _pageTemplate, _procMode, _Ot)
    this.setState({
      Ot: _Ot,
      openType: _opentype,
      interType: _intertype,
      procMode: _procMode,
      funcType: _funcType,
      pageTemplate: _pageTemplate,
      formlist: this.props.formlist.map(item => {
        if (item.key === 'class') {
          item.options = btnCustomClasses
@@ -107,12 +115,10 @@
        } else if (item.key === 'intertype') {
          let iscustom = ['pop', 'prompt', 'exec'].includes(_opentype)
          item.options = this.state.interTypeOptions.filter(op => (iscustom || op.value !== 'custom'))
        } else if (item.key === 'icon') {
          item.options = btnIcons
        } else if (item.key === 'Ot') {
          if (type === 'card') {
            item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
          } else if (card.pageTemplate === 'pay') { // 行级按钮、支付按钮,只能选单行
          } else if (_pageTemplate === 'pay') { // 行级按钮、支付按钮,只能选单行
            item.options = this.state.requireOptions.filter(op => ['requiredSgl'].includes(op.value))
          } else if (['innerpage', 'tab', 'popview', 'excelIn'].includes(_opentype)) {
            item.options = this.state.requireOptions.filter(op => ['notRequired', 'requiredSgl'].includes(op.value))
@@ -137,8 +143,8 @@
    })
  }
  getOptions = (_opentype, _intertype, _funcType, _pageTemplate, _procMode) => {
    let _options = fromJS(actionTypeOptions[_opentype]).toJS() // 选项列表
  getOptions = (_opentype, _intertype, _funcType, _pageTemplate, _procMode, _Ot) => {
    let _options = actionTypeOptions[_opentype] ? fromJS(actionTypeOptions[_opentype]).toJS() : [] // 选项列表
    
    if (_opentype === 'innerpage') {         // 新页面,可选模板(自定义时,可填入外部链接)
      if (_pageTemplate === 'custom') {
@@ -186,6 +192,13 @@
      }
    }
    if (_Ot !== 'notRequired' && _opentype !== 'excelOut') {
      _options.push('controlField', 'controlVal')
    }
    if (_Ot === 'requiredSgl' && ['pop', 'prompt', 'exec'].includes(_opentype)) {
      _options.push('swipe')
    }
    return _options
  }
@@ -210,13 +223,12 @@
   */
  optionChange = (key, value) => {
    const { card, type } = this.props
    const { openType, procMode } = this.state
    const { openType, procMode, Ot, pageTemplate } = this.state
    if (key === 'OpenType') {
      let _options = this.getOptions(value, 'system', this.state.funcType, card.pageTemplate, 'system')
      let _options = this.getOptions(value, 'system', this.state.funcType, '', 'system', Ot)
      let _fieldval = {}
      let _formlist = this.state.formlist.map(item => {
        item.hidden = !_options.includes(item.key)
@@ -248,6 +260,8 @@
            item.options = this.state.insertUpdateOptions
          }
          _fieldval.sqlType = ''
        } else if (item.key === 'pageTemplate') {
          item.initVal = ''
        }
        return item
@@ -257,6 +271,7 @@
        openType: value,
        intertype: 'system',
        procMode: 'system',
        pageTemplate: '',
        formlist: _formlist
      }, () => {
        if (value === 'excelIn') {
@@ -270,7 +285,7 @@
        this.props.form.setFieldsValue(_fieldval)
      })
    } else if (key === 'funcType') {
      let _options = this.getOptions(openType, this.state.interType, value, card.pageTemplate, procMode)
      let _options = this.getOptions(openType, this.state.interType, value, pageTemplate, procMode, Ot)
      let _fieldval = {}
      this.setState({
@@ -334,9 +349,10 @@
      })
    } else if (key === 'pageTemplate') {
      let _fieldval = {}
      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, value, procMode)
      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, value, procMode, Ot)
      this.setState({
        pageTemplate: value,
        formlist: this.state.formlist.map(item => {
          item.hidden = !_options.includes(item.key)
@@ -357,7 +373,7 @@
        this.props.form.setFieldsValue(_fieldval)
      })
    } else if (key === 'intertype') {
      let _options = this.getOptions(openType, value, this.state.funcType, '', procMode)
      let _options = this.getOptions(openType, value, this.state.funcType, pageTemplate, procMode, Ot)
      this.setState({
        interType: value,
@@ -379,7 +395,7 @@
        })
      })
    } else if (key === 'procMode') {
      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, '', value)
      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, pageTemplate, value, Ot)
      this.setState({
        procMode: value,
@@ -389,6 +405,16 @@
          if (item.key === 'innerFunc') {
            item.required = true
          }
          return item
        })
      })
    } else if (key === 'Ot') {
      let _options = this.getOptions(openType, this.state.interType, this.state.funcType, pageTemplate, procMode, value)
      this.setState({
        Ot: value,
        formlist: this.state.formlist.map(item => {
          item.hidden = !_options.includes(item.key)
          return item
        })
      })
@@ -503,7 +529,7 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉搜索
      } else if (item.type === 'select') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.tooltip ?
@@ -523,13 +549,14 @@
              })(
                <Select
                  showSearch
                  filterOption={(input, option) => option.props.children[2].toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  allowClear={item.allowClear === true}
                  filterOption={(input, option) => option.props.children.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}>
                      {item.key === 'icon' && option.value && <Icon type={option.value} />} {option.text}
                    <Select.Option key={index} value={(option.value || option.field)}>
                      {(option.text || option.label)}
                    </Select.Option>
                  )}
                </Select>
@@ -602,6 +629,24 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'icon') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
              {getFieldDecorator(item.key, {
                initialValue: item.initVal,
                rules: [
                  {
                    required: !!item.required,
                    message: this.props.dict['form.required.select'] + item.label + '!'
                  }
                ]
              })(
                <MkIcon allowClear/>
              )}
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'mcascader') {
        fields.push(
          <Col span={12} key={index}>
@@ -627,6 +672,7 @@
        if (!err) {
          values.uuid = card.uuid
          values.verify = card.verify || null
          values.modal = card.modal || null
          if (values.show === 'icon' && !values.icon) {
            notification.warning({
src/menu/components/share/actioncomponent/dragaction/card.jsx
@@ -1,6 +1,7 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Button, Popover } from 'antd'
import { resetStyle } from '@/utils/utils-custom.js'
import './index.scss'
const Card = ({ id, card, moveCard, findCard, editCard, delCard, copyCard, changeStyle, profileCard, doubleClickCard }) => {
@@ -36,12 +37,13 @@
  }
  let btnElement = null
  let _style = resetStyle(card.style)
  if (card.show === 'icon') {
    btnElement = (
      <Button
        type="link"
        icon={card.icon}
        style={card.btnstyle}
        style={_style}
        onDoubleClick={() => doubleClickCard(id)}
      >{card.icon ? '' : card.label}</Button>
    )
@@ -49,7 +51,7 @@
    btnElement = (
      <Button
        type="link"
        style={card.btnstyle}
        style={_style}
        onDoubleClick={() => doubleClickCard(id)}
      >{card.label}{card.icon ? <Icon type={card.icon}/> : null}</Button>
    )
@@ -57,7 +59,7 @@
    btnElement = (
      <Button
        icon={card.icon}
        style={card.btnstyle}
        style={_style}
        onDoubleClick={() => doubleClickCard(id)}
      >
        {card.label}
src/menu/components/share/actioncomponent/formconfig.jsx
@@ -11,7 +11,10 @@
 * @param {*} usefulFields   存储过程可用的开始字段
 * @param {*} type           按钮类型,用于区分可选的打开方式
 */
export function getActionForm (card, functip, setting, usefulFields, type, menulist = [], modules = []) {
export function getActionForm (card, functip, config, usefulFields, type, menulist = [], modules = []) {
  let appType = sessionStorage.getItem('appType')
  let setting = config.setting || {}
  let columns = config.columns || []
  let appMenus = []
  let opentypes = [
    {
@@ -49,7 +52,7 @@
    { value: 'pay', text: Formdict['model.pay'] },
    { value: 'custom', text: Formdict['header.form.custom'] }
  ]
  const isApp = sessionStorage.getItem('appType') === 'pc'
  const isApp = ['pc', 'mob'].includes(appType)
  let funTypes = [
    { value: 'changeuser', text: Formdict['header.form.func.changeuser'] },
@@ -57,9 +60,8 @@
  ]
  
  if (isApp) {
    opentypes = opentypes.filter(item => item.value !== 'tab')
    pageTemps = [
      { value: 'page', text: '菜单' },
      // { value: 'page', text: '菜单' },
      { value: 'linkpage', text: '关联菜单' },
      { value: 'billprint', text: '单据打印' },
      { value: 'pay', text: Formdict['model.pay'] },
@@ -79,9 +81,15 @@
    } else {
      appMenus = []
    }
    if (appType === 'mob') {
      opentypes = opentypes.filter(item => ['pop', 'prompt', 'exec', 'innerpage'].includes(item.value))
    } else {
      opentypes = opentypes.filter(item => item.value !== 'tab')
    }
  }
  
  if (type === 'chart') {
  if (type === 'chart' && appType !== 'mob') {
    opentypes = opentypes.filter(item => item.value === 'excelIn' || item.value === 'excelOut')
  }
@@ -107,7 +115,7 @@
      type: 'radio',
      key: 'funcType',
      label: Formdict['header.form.funcType'],
      initVal: card.funcType || (isApp ? 'changeuser' : ''),
      initVal: card.funcType || '',
      required: true,
      options: funTypes
    },
@@ -193,34 +201,12 @@
      options: pageTemps
    },
    {
      type: 'radio',
      key: 'open',
      label: '链接方式',
      initVal: card.open || 'blank',
      required: true,
      forbid: !isApp,
      options: [{
        value: 'blank',
        text: '新窗口'
      }, {
        value: 'self',
        text: '当前窗口'
      }]
    },
    {
      type: 'select',
      key: 'linkmenu',
      label: '关联菜单',
      initVal: card.linkmenu || '',
      required: true,
      options: appMenus
    },
    {
      type: 'select',
      key: 'copyMenuId',
      label: '复制菜单',
      initVal: card.copyMenuId || '',
      required: false,
      forbid: !isApp,
      options: appMenus
    },
    {
@@ -430,21 +416,39 @@
      type: 'radio',
      key: 'show',
      label: "显示为",
      initVal: card.show || 'icon',
      initVal: card.show || 'button',
      required: true,
      options: [{
        value: 'icon',
        text: '图标'
      }, {
        value: 'button',
        text: '按钮'
        text: '图标+文字'
      }, {
        value: 'link',
        text: '链接'
        text: '文字+图标'
      }]
    },
    {
      type: 'select',
      type: 'radio',
      key: 'swipe',
      label: "滑动显示",
      initVal: card.swipe || 'false',
      required: false,
      forbid: (type !== 'datacard' || appType !== 'mob'),
      options: [{
        value: 'false',
        text: '否'
      }, {
        value: 'left',
        text: '左滑'
      }, {
        value: 'right',
        text: '右滑'
      }]
    },
    {
      type: 'icon',
      key: 'icon',
      label: Formdict['model.icon'],
      initVal: card.icon,
@@ -456,6 +460,7 @@
      key: 'class',
      label: Formdict['model.form.color'],
      initVal: card.class,
      tooltip: '此颜色为按钮初始化颜色,可在样式调整中修改。',
      required: false,
      options: []
    },
@@ -515,6 +520,50 @@
      initVal: card.syncComponent || [],
      required: false,
      options: modules
    },
    {
      type: 'select',
      key: 'controlField',
      label: '控制字段',
      tooltip: '禁用控制字段,可根据数据控制按钮是否禁用。',
      initVal: card.controlField || '',
      required: false,
      allowClear: true,
      options: columns
    },
    {
      type: 'text',
      key: 'controlVal',
      label: '控制值',
      tooltip: '当选择控制字段,且字段值与控制值相等时,按钮会禁用,多个值用逗号分隔。',
      initVal: card.controlVal || '',
      required: false
    },
    {
      type: 'select',
      key: 'openmenu',
      label: '打开菜单',
      tooltip: '执行成功后需要打开的菜单。',
      initVal: card.openmenu || '',
      forbid: appType !== 'pc' && appType !== 'mob',
      required: false,
      allowClear: true,
      options: appMenus
    },
    {
      type: 'radio',
      key: 'open',
      label: '打开方式',
      initVal: card.open || 'blank',
      required: true,
      forbid: appType !== 'pc',
      options: [{
        value: 'blank',
        text: '新窗口'
      }, {
        value: 'self',
        text: '当前窗口'
      }]
    }
  ]
src/menu/components/share/actioncomponent/index.jsx
@@ -32,6 +32,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,          // 编辑中元素
    formlist: null,      // 表单信息
    actionlist: null,    // 按钮组
@@ -43,8 +44,20 @@
   * @description 搜索条件初始化
   */
  UNSAFE_componentWillMount () {
    const { config } = this.props
    let actionlist = fromJS(config.action).toJS() || []
    this.setState({
      actionlist: fromJS(this.props.config.action).toJS()
      actionlist: actionlist.map(item => {
        if (item.btnstyle) { // 兼容
          item.style = item.style || {}
          item.style = {...item.style, ...item.btnstyle}
          delete item.btnstyle
        }
        return item
      })
    })
  }
@@ -85,7 +98,7 @@
    if (comIds[0] !== config.uuid || comIds[1] !== 'actionlist') return
    let _card = fromJS(card).toJS()
    _card.btnstyle = style
    _card.style = style
    let _actionlist = actionlist.map(cell => {
      if (cell.uuid === _card.uuid) return _card
@@ -102,8 +115,8 @@
  changeBtnStyle = (element) => {
    const { config } = this.props
    let _style = element.btnstyle ? fromJS(element.btnstyle).toJS() : {}
    let options = ['font', 'border', 'background', 'margin']
    let _style = element.style ? fromJS(element.style).toJS() : {}
    let options = ['font', 'border', 'background', 'margin', 'padding']
    this.setState({
      card: element
@@ -175,7 +188,7 @@
    this.setState({
      visible: true,
      card: card,
      formlist: getActionForm(card, functip, config.setting, usefulFields, this.props.type, menulist, modules)
      formlist: getActionForm(card, functip, config, usefulFields, this.props.type, menulist, modules)
    })
  }
@@ -221,15 +234,15 @@
        }
        if (item.uuid === btn.uuid) {
          btn.btnstyle = item.btnstyle || {}
          btn.style = item.style || {}
          if (btn.class !== item.class || btn.show !== item.show || !btn.btnstyle.color) {
          if (btn.class !== item.class || btn.show !== item.show || !btn.style.color) {
            if (btn.show === 'link' || btn.show === 'icon') {
              btn.btnstyle.color = color[btn.class]
              btn.btnstyle.backgroundColor = 'transparent'
              btn.style.color = color[btn.class]
              btn.style.backgroundColor = 'transparent'
            } else {
              btn.btnstyle.color = '#ffffff'
              btn.btnstyle.backgroundColor = color[btn.class]
              btn.style.color = '#ffffff'
              btn.style.backgroundColor = color[btn.class]
            }
          }
          return btn
@@ -261,7 +274,7 @@
   */
  deleteElement = (card) => {
    const { config } = this.props
    const { dict } = this.state
    const { dict, appType } = this.state
    let _this = this
    confirm({
@@ -270,10 +283,6 @@
        let _actionlist = fromJS(_this.state.actionlist).toJS()
        _actionlist = _actionlist.filter(item => item.uuid !== card.uuid)
        if (!card.origin) {
          MKEmitter.emit('delButtons', [card.uuid])
        }
        let btnlog = config.btnlog || []
        if (card.OpenType === 'popview' || card.verify || card.modal) {
@@ -285,6 +294,11 @@
        }, () => {
          _this.props.updateaction({...config, action: _actionlist, btnlog})
        })
        if (card.origin || appType === 'mob') return
        if (appType === 'pc' && card.OpenType !== 'popview') return
        MKEmitter.emit('delButtons', [card.uuid])
      },
      onCancel() {}
    })
@@ -354,7 +368,7 @@
        let _param = {
          funcName: btn.innerFunc,
          name: _config.setting.tableName || '',
          fields: btn.fields,
          fields: btn.modal ? btn.modal.fields : [],
          menuNo: menu.MenuNo
        }
        newLText = Utils.formatOptions(FuncUtils.getfunc(_param, btn, menu, _config))
@@ -392,8 +406,8 @@
   * @description 按钮双击触发子配置
   */
  btnDoubleClick = (element) => {
    if (sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') !== 'false') return
    if (sessionStorage.getItem('style-control') && sessionStorage.getItem('style-control') === 'true') return
    if (element.OpenType === 'pop' || element.OpenType === 'popview' || element.execMode === 'pop') {
      this.props.setSubConfig(element)
    } else if (element.OpenType === 'innerpage' && element.pageTemplate === 'page') {
src/menu/components/share/actioncomponent/index.scss
@@ -23,12 +23,19 @@
    }
    button {
      cursor: move;
      height: auto;
      min-height: 32px;
      .anticon-table {
        font-size: 10px;
        position: absolute;
        right: 1px;
        bottom: 0px;
      }
      span {
        font-style: inherit;
        text-decoration: inherit;
        font-weight: inherit;
      }
    }
  }
}
src/menu/components/share/logcomponent/index.jsx
@@ -18,6 +18,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    visible: false,
    data: [],
    columns: [
@@ -69,11 +70,15 @@
  }
  revert = (item) => {
    const { appType } = this.state
    const data = this.state.data.filter(d => d.uuid !== item.uuid)
    this.setState({data})
    MKEmitter.emit('thawButtons', item.uuid)
    this.props.handlelog('revert', data, item)
    if (appType === 'mob' || (appType === 'pc' && item.OpenType !== 'popview')) return
    MKEmitter.emit('thawButtons', item.uuid)
  }
  handleDelete = (item) => {
src/menu/components/share/markcomponent/index.jsx
@@ -13,6 +13,7 @@
import '@/assets/css/table.scss'
const EditTable = asyncComponent(() => import('@/templates/zshare/editTable'))
const { confirm } = Modal
class MarkColumn extends Component {
  static propTpyes = {
@@ -128,8 +129,11 @@
  resetMark = () => {
    const { marks, columns, type } = this.props
    let markColumns = fromJS(this.state.markColumns).toJS()
    let _columns = fromJS(columns).toJS()
    let options = columns.map(col => {
    _columns.unshift({field: '$Index', label: '序号'})
    let options = _columns.map(col => {
      return {
        value: col.field,
        label: col.label,
@@ -142,7 +146,7 @@
          {
            value: 'dynamic',
            label: '动态值',
            children: columns.map(cell => {
            children: _columns.map(cell => {
              return {
                value: cell.field,
                label: cell.label
@@ -250,7 +254,7 @@
      }
    ]
    if (type === 'line') {
    if (type === 'line' || type === 'sequence') {
      signs.pop()
    } else if (type === 'slider') {
      markColumns = markColumns.filter(col => {
@@ -278,9 +282,13 @@
  }
  markSubmit = () => {
    this.setState({
      visible: false
    })
    let save = false
    let input = document.getElementById('contrastValue')
    let val = input && input.value ? input.value : ''
    if (!val) {
      save = true
    }
    let marks = this.state.marks.map(item => {
      if (item.signType && item.signType[0] === 'background') {
@@ -295,10 +303,28 @@
          item.fontColor = ''
        }
      }
      if (val && item.contrastValue === val) {
        save = true
      }
      return item
    })
    this.props.onSubmit(marks)
    if (save) {
      this.setState({
        visible: false
      })
      this.props.onSubmit(marks)
    } else {
      const _this = this
      confirm({
        title: '存在未保存标记,确定忽略吗?',
        onOk() {
          _this.setState({ visible: false })
          _this.props.onSubmit(marks)
        },
        onCancel() {}
      })
    }
  }
  render() {
src/menu/components/share/mobPagination/index.jsx
New file
@@ -0,0 +1,20 @@
import React, {Component} from 'react'
import { Icon, Pagination } from 'antd-mobile'
import './index.scss'
class MobPagination extends Component {
  render () {
    return (
      <Pagination className="mob-pagination" total={5}
        current={1}
        locale={{
          prevText: (<span><Icon type="left" />上一页</span>),
          nextText: (<span>下一页<Icon type="right" /></span>),
        }}
      />
    )
  }
}
export default MobPagination
src/menu/components/share/mobPagination/index.scss
New file
@@ -0,0 +1,14 @@
.mob-pagination {
  .am-button::before {
    display: none;
  }
  .am-button {
    border: none;
    font-size: 16px;
    background: transparent;
    .am-icon {
      position: relative;
      top: 5px;
    }
  }
}
src/menu/components/share/normalheader/index.jsx
@@ -5,6 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import MKEmitter from '@/utils/events.js'
import { resetStyle } from '@/utils/utils-custom.js'
import './index.scss'
const SearchComponent = asyncComponent(() => import('@/menu/components/share/searchcomponent'))
@@ -15,6 +16,10 @@
    hideSearch: PropTypes.any,       // 隐藏搜索条件
    config: PropTypes.object,        // 配置信息
    updateComponent: PropTypes.func  // 配置更新
  }
  state = {
    appType: sessionStorage.getItem('appType')
  }
  componentDidMount () {
@@ -48,22 +53,30 @@
  changeStyle = () => {
    const { config } = this.props
    // MKEmitter.emit('changeStyle', [config.uuid, 'header'], ['font', 'height', 'border'], config.headerStyle)
    MKEmitter.emit('changeStyle', [config.uuid, 'header'], ['font', 'border'], config.headerStyle)
  }
  render() {
    const { config, defaultshow, hideSearch } = this.props
    const { appType } = this.state
    let title = config.plot ? config.plot.title : config.wrap.title
    let show = true
    if (!title && appType === 'mob' && config.type === 'card' && config.subtype === 'datacard' && config.action && config.action.length) {
      title = ' '
    }
    if (defaultshow === 'hidden') {
      if (!title && (!config.search || config.search.length === 0)) {
        show = false
      }
    }
    let _style = resetStyle(config.headerStyle)
    return (
      <div className={'normal-header' + (!show ? ' hidden' : '')} style={config.headerStyle}>
      <div className={'normal-header' + (!show ? ' hidden' : '') + (config.wrap && config.wrap.searchable === 'true' ? ' tree-search' : '')} style={_style}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
@@ -71,6 +84,7 @@
        } trigger="hover">
          <span className="title">{title}</span>
        </Popover>
        {config.wrap && config.wrap.searchable === 'true' ? <span className="ant-input-search ant-input-affix-wrapper"><span className="ant-input-suffix"><Icon type="search" /></span></span> : null}
        {hideSearch !== 'true' && config.search ? <SearchComponent config={config} updatesearch={this.props.updateComponent}/> : null}
      </div>
    )
src/menu/components/share/normalheader/index.scss
@@ -3,19 +3,37 @@
  height: 45px;
  border-bottom: 1px solid #e8e8e8;
  overflow: hidden;
  line-height: 45px;
  .title {
    text-decoration: inherit;
    font-weight: inherit;
    font-style: inherit;
    float: left;
    line-height: 45px;
    line-height: inherit;
    margin-left: 10px;
    position: relative;
    z-index: 1;
    min-height: 45px;
    min-width: 30px;
  }
  .model-custom-header-search-list {
    flex: 1;
  }
  .ant-input-search.ant-input-affix-wrapper {
    width: calc(100% - 140px);
    max-width: 130px;
    margin-top: 8px;
    margin-right: 25px;
    float: right;
    height: 28px;
    border-radius: 20px;
    border: 1px solid #d9d9d9;
    opacity: 0.6;
  }
}
.normal-header:not(.tree-search) {
  display: flex;
}
.normal-header.hidden {
  display: none;
src/menu/components/share/pastecomponent/index.jsx
@@ -24,7 +24,7 @@
    this.setState({visible: true})
  }
  resetconfig = (item, copyBtns) => {
  resetconfig = (item, copyBtns, config) => {
    let _uuid = Utils.getuuid()
    if (item.OpenType === 'popview') {
@@ -38,9 +38,33 @@
      item.uuid = _uuid
    }
    if (item.copyType === 'cardcell') {
    if (item.copyType === 'cardcell' && config.subtype === 'datacard') {
      item.setting = item.setting || {}
      item.$cardType = 'extendCard'
      item.setting.width = item.setting.width || 6
      if (item.elements) {
        item.elements = item.elements.map(cell => {
          if (cell.datatype === 'dynamic') {
            cell.datatype = 'static'
          }
          cell.uuid = Utils.getuuid()
          return cell
        })
      }
      if (item.backElements) {
        item.backElements = item.backElements.map(cell => {
          if (cell.datatype === 'dynamic') {
            cell.datatype = 'static'
          }
          cell.uuid = Utils.getuuid()
          return cell
        })
      }
    } else if (item.copyType === 'cardcell') {
      item.setting = item.setting || {}
      item.setting.width = item.setting.width || 6
      delete item.$cardType
      if (item.elements) {
        item.elements = item.elements.map(cell => {
@@ -105,12 +129,9 @@
  pasteSubmit = () => {
    const { options } = this.props
    this.pasteFormRef.handleConfirm().then(res => {
      if (!options.includes(res.copyType)) {
        notification.warning({
          top: 92,
          message: '配置信息格式错误!',
          duration: 5
        })
        notification.warning({ top: 92, message: '配置信息格式错误!', duration: 5 })
        return
      }
@@ -118,7 +139,7 @@
      let config = fromJS(this.props.config).toJS()
      let copyBtns = new Map()
      res = this.resetconfig(res, copyBtns)
      res = this.resetconfig(res, copyBtns, config)
      delete res.copyType
      copyBtns = [...copyBtns.values()]
@@ -127,10 +148,18 @@
        MKEmitter.emit('copyButtons', copyBtns)
      }
      if (type === 'action') {
      if (config.type === 'form' && config.subtype === 'stepform') {
        this.props.updateConfig(res)
        this.setState({visible: false})
        return
      } else if (type === 'action') {
        config.action = config.action || []
        config.action = config.action.filter(item => !item.origin)
        if (['line', 'bar', 'scatter'].includes(config.type) && !['excelOut', 'excelIn'].includes(res.OpenType)) {
          notification.warning({ top: 92, message: '图表中不支持此类按钮!', duration: 5 })
          return
        }
        MKEmitter.emit('addButton', config.uuid, res)
      } else if (type === 'search' || type === 'form') {
        config.search = config.search || []
@@ -139,7 +168,7 @@
        let keys = config.search.map(item => item.field.toLowerCase())
        if (type === 'form') {
          if (['number', 'switch', 'textarea', 'checkcard', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
          if (['number', 'switch', 'textarea', 'fileupload', 'hint', 'color', 'funcvar'].includes(res.type)) {
            res.type = 'text'
          } else if (res.type === 'radio') {
            res.type = 'select'
@@ -162,6 +191,8 @@
        config.search.push(res)
      } else if (type === 'cardcell') {
        config.subcards.push(res)
      } else if (type === 'menucell') {
        config.subMenus.push(res)
      } else if (type === 'cols') {
        config.cols = config.cols.filter(col => !col.origin)
src/menu/components/share/searchcomponent/dategroup/index.jsx
File was deleted
src/menu/components/share/searchcomponent/dategroup/index.scss
File was deleted
src/menu/components/share/searchcomponent/dragsearch/card.jsx
@@ -3,7 +3,7 @@
import { Icon, Select, DatePicker, Input, Popover, Form } from 'antd'
import moment from 'moment'
import DateGroup from '../dategroup'
import DateGroup from '@/menu/components/search/main-search/dategroup'
import './index.scss'
const { MonthPicker, WeekPicker, RangePicker } = DatePicker
src/menu/components/share/searchcomponent/dragsearch/index.scss
@@ -1,6 +0,0 @@
.common-drawarea-placeholder {
  width: 100%;
  line-height: 65px;
  text-align: center;
  color: #bcbcbc;
}
src/menu/components/share/searchcomponent/index.jsx
@@ -210,7 +210,7 @@
        return
      }
      if ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && res.resourceType === '1' && /\s/.test(res.dataSource)) {
      if (['select', 'multiselect', 'link', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
src/menu/components/share/sourcecomponent/index.jsx
@@ -14,12 +14,23 @@
  }
  state = {
    url: this.props.value,
    url: '',
    visible: ''
  }
  UNSAFE_componentWillMount () {
    const { value } = this.props
    let val = ''
    if (value) {
      val = value
    } else if (this.props['data-__meta']) {
      val = this.props['data-__meta'].initialValue || ''
    }
    this.setState({
      url: val,
    })
  }
  shouldComponentUpdate (nextProps, nextState) {
@@ -67,6 +78,8 @@
          width={visible !== 'system' ? 600 : 1000}
          closable={false}
          maskClosable={false}
          okText="确定"
          cancelText="取消"
          onOk={this.popSubmit}
          onCancel={() => {this.setState({visible: ''})}}
          destroyOnClose
src/menu/components/share/sourcecomponent/inputform/index.jsx
@@ -27,7 +27,6 @@
    originlist: [],
    list: [],
    pagelist: [],
    fileList: [],
    searchKey: '',
    pageSize: 12,
    pageIndex: 1,
@@ -79,7 +78,7 @@
    let list = originlist
    let pagelist = list.filter((item, index) => index < this.state.pageSize)
    this.setState({originlist, list, url: '', searchKey: '', pageIndex: 1, fileList: [], pagelist})
    this.setState({originlist, list, url: '', searchKey: '', pageIndex: 1, pagelist})
  }
  changeSearch = () => {
@@ -100,12 +99,8 @@
    this.setState({pageIndex: page, pagelist})
  }
  changeFile = (vals) => {
    this.setState({fileList: vals})
    if (vals && vals[0] && vals[0].status === 'done' && vals[0].response) {
      this.setState({url: vals[0].response})
    }
  changeFile = (val) => {
    this.setState({url: val})
  }
  selectItem = (item) => {
@@ -162,7 +157,7 @@
  render () {
    const { type, keyword } = this.props
    const { list, url, pagelist, fileList, searchKey, pageIndex, pageSize, selectId, editvisible, card } = this.state
    const { list, url, pagelist, searchKey, pageIndex, pageSize, selectId, editvisible, card } = this.state
    
    return (
      <div className="mk-source-pop-wrap">
@@ -170,7 +165,12 @@
          <TextArea id="source-input" value={url} rows={4} onChange={this.changeValue}/>
        </Form.Item> : null}
        {keyword === 'upload' ? <Form.Item label="上传" labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={{xs: { span: 24 }, sm: { span: 20 }}}>
          <FileUpload value={fileList} onChange={this.changeFile} accept={type === 'video' ? '.mp4,.webm,.ogg' : '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp'} maxFile={1} fileType={type === 'video' ? 'text' : 'picture'} />
          <FileUpload config={{
            initval: '',
            suffix: type === 'video' ? '.mp4,.webm,.ogg' : '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
            maxfile: 1,
            fileType: type === 'video' ? 'text' : 'picture'
          }} onChange={this.changeFile} />
        </Form.Item> : null}
        {keyword === 'system' ?
          <Search value={searchKey} placeholder="" onChange={(e) => this.setState({searchKey: e.target.value})} onSearch={this.changeSearch} enterButton/> : null}
src/menu/components/share/styleInput/index.jsx
New file
@@ -0,0 +1,140 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Select, Input } from 'antd'
import './index.scss'
const { Option } = Select
class StyleInput extends Component {
  static propTpyes = {
    defaultValue: PropTypes.any,
    options: PropTypes.any,
    value: PropTypes.any,
    onChange: PropTypes.func,
  }
  state = {
    value: '',
    unit: '',
    options: null
  }
  UNSAFE_componentWillMount () {
    const { value, options } = this.props
    let val = value || ''
    let unit = options[0]
    if (val) {
      if (val.indexOf('px') > -1) {
        unit = 'px'
      } else if (val.indexOf('%') > -1) {
        unit = '%'
      } else if (val.indexOf('vw') > -1) {
        unit = 'vw'
      } else if (val.indexOf('vh') > -1) {
        unit = 'vh'
      }
    }
    let _val = parseFloat(val)
    if (isNaN(_val)) {
      _val = ''
    }
    this.setState({value: _val, options: options, unit})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.value === '' && this.state.value !== '') {
      this.setState({value: ''})
    } else if (nextProps.value && nextProps.value !== `${this.state.value}${this.state.unit}`) {
      let val = nextProps.value
      let unit = this.state.unit
      if (val) {
        if (val.indexOf('px') > -1) {
          unit = 'px'
        } else if (val.indexOf('%') > -1) {
          unit = '%'
        } else if (val.indexOf('vw') > -1) {
          unit = 'vw'
        } else if (val.indexOf('vh') > -1) {
          unit = 'vh'
        }
      }
      let _val = parseFloat(val)
      if (isNaN(_val)) {
        _val = ''
      }
      this.setState({value: _val, unit})
    }
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  changeValue = (e) => {
    const { unit } = this.state
    let val = e.target.value
    if (/\d+\.$/.test(val)) {
      this.setState({
        value: val
      })
      return
    }
    let _val = parseFloat(val)
    if (isNaN(_val)) {
      _val = ''
    }
    this.setState({
      value: _val,
    }, () => {
      this.props.onChange(_val !== '' ? `${_val}${unit}` : '')
    })
  }
  changeUnit = (val) => {
    const { value } = this.state
    this.setState({unit: val}, () => {
      this.props.onChange(value !== '' ? `${value}${val}` : '')
    })
  }
  render () {
    const { value, options, unit } = this.state
    return (
      <div className="style-input-wrap">
        <Input value={value} addonAfter={
          options.length > 1 ?
          <Select value={unit} onChange={this.changeUnit}>
            {options.map(item => <Option key={item} value={item}>{item}</Option>)}
          </Select> :
          <div className="single-unit">{unit}</div>
        } onChange={this.changeValue}/>
      </div>
    )
  }
}
export default StyleInput
src/menu/components/share/styleInput/index.scss
New file
@@ -0,0 +1,11 @@
.style-input-wrap {
  line-height: 32px;
  .ant-select {
    width: 60px!important;
  }
  .single-unit {
    width: 38px;
    text-align: left;
    color: rgba(255, 255, 255, 0.65);
  }
}
src/menu/components/share/usercomponent/index.jsx
@@ -163,6 +163,7 @@
                func: 's_custom_components_adduptdel',
                c_id: config.uuid,
                images: Utils.getcloudurl(result.Images),
                typename: sessionStorage.getItem('appType') || '',
                c_name: res.name,
                long_param: window.btoa(window.encodeURIComponent(JSON.stringify(template))),
                del_type: ''
src/menu/components/table/normal-table/columns/editColumn/formconfig.jsx
@@ -6,9 +6,9 @@
/**
 * @description 获取显示列表单配置信息
 * @param {object} card       // 搜索条件对象
 * @param {Array}  menulist   // 菜单列表-用于字段透视
 */
export function getColumnForm (card, menulist = [], fields = []) {
export function getColumnForm (card, fields = []) {
  let appType = sessionStorage.getItem('appType')
  let roleList = sessionStorage.getItem('sysRoles')
  if (roleList) {
    try {
@@ -18,6 +18,24 @@
    }
  } else {
    roleList = []
  }
  let menulist = []
  if (appType === 'pc') {
    menulist = sessionStorage.getItem('appMenus')
  } else if (!appType) {
    menulist = sessionStorage.getItem('fstMenuList')
  }
  if (menulist) {
    try {
      menulist = JSON.parse(menulist)
    } catch {
      menulist = []
    }
  } else {
    menulist = []
  }
  let options = [{
@@ -41,6 +59,9 @@
  }, {
    value: 'colspan',
    text: '合并列'
  }, {
    value: 'index',
    text: '序号'
  }]
  if (!card.isSub) {
@@ -243,21 +264,32 @@
      key: 'postfix',
      label: Formdict['header.form.postfix'],
      initVal: card.postfix || '',
      tooltipClass: 'middle',
      required: false,
      readonly: false
    },
    {
      type: 'radio',
      type: 'select',
      key: 'lenWidRadio',
      label: '长宽比',
      initVal: card.lenWidRadio || '1:1',
      required: true,
      options: [
        { value: '1:1', text: '1:1' },
        { value: '3:2', text: '3:2' },
        { value: '4:3', text: '4:3' },
        { value: '16:9', text: '16:9' }
        { value: '3:2', text: '3:2' },
        { value: '16:9', text: '16:9' },
        { value: '2:1', text: '2:1' },
        { value: '3:1', text: '3:1' },
        { value: '4:1', text: '4:1' },
        { value: '5:1', text: '5:1' },
        { value: '6:1', text: '6:1' },
        { value: '7:1', text: '7:1' },
        { value: '8:1', text: '8:1' },
        { value: '9:1', text: '9:1' },
        { value: '10:1', text: '10:1' },
        { value: '3:4', text: '3:4' },
        { value: '2:3', text: '2:3' },
        { value: '9:16', text: '9:16' },
      ]
    },
    {
@@ -309,22 +341,25 @@
      }, {
        value: 'linkurl',
        text: '链接'
      }]
      }],
      forbidden: appType === 'mob'
    },
    {
      type: 'cascader',
      type: appType === 'pc' ? 'select' : 'cascader',
      key: 'linkmenu',
      label: Formdict['model.menu'],
      initVal: card.linkmenu || [],
      initVal: card.linkmenu || (appType === 'pc' ? '' : []),
      required: true,
      options: menulist
      options: menulist,
      forbidden: appType === 'mob'
    },
    {
      type: 'textarea',
      key: 'linkurl',
      label: '链接地址',
      initVal: card.linkurl || '',
      required: true
      required: true,
      forbidden: appType === 'mob'
    },
    {
      type: 'multiselect',
@@ -332,7 +367,20 @@
      label: '关联字段',
      initVal: card.linkfields || [],
      required: false,
      options: fields
      options: fields,
      forbidden: appType === 'mob'
    },
    {
      type: 'radio',
      key: 'open',
      label: '打开方式',
      initVal: card.open || 'blank',
      required: false,
      forbid: appType !== 'pc',
      options: [
        { value: 'blank', text: '新窗口' },
        { value: 'self', text: '当前窗口' }
      ]
    },
    {
      type: 'multiselect',
src/menu/components/table/normal-table/columns/editColumn/index.jsx
@@ -16,7 +16,8 @@
  picture: ['label', 'field', 'type', 'Align', 'Hide', 'IsSort', 'Width', 'blacklist', 'scale', 'lenWidRadio', 'picSort'],
  colspan: ['label', 'type', 'Align', 'Hide', 'blacklist'],
  custom: ['label', 'type', 'Align', 'Hide', 'Width', 'blacklist'],
  action: ['label', 'type', 'Align', 'Width']
  action: ['label', 'type', 'Align', 'Width'],
  index: ['label', 'type', 'Align', 'Width']
}
class MainSearch extends Component {
@@ -41,24 +42,13 @@
  }
  editColumn = (column) => {
    let menulist = sessionStorage.getItem('fstMenuList')
    if (menulist) {
      try {
        menulist = JSON.parse(menulist)
      } catch {
        menulist = []
      }
    } else {
      menulist = []
    }
    let formlist = getColumnForm(column, menulist, this.props.fields)
    let formlist = getColumnForm(column, this.props.fields)
    let _options = fromJS(columnTypeOptions[column.type]).toJS()
    if (column.type === 'text' || column.type === 'number') {
      if (column.perspective === 'linkmenu') {
        _options.push('linkmenu', 'linkfields')
        _options.push('linkmenu', 'linkfields', 'open')
      } else if (column.perspective === 'linkurl') {
        _options.push('linkurl', 'linkfields')
        _options.push('linkurl', 'linkfields', 'open')
      }
    }
@@ -103,13 +93,34 @@
        }
      })
    } else if (key === 'field') {
      this.props.form.setFieldsValue({label: option.props.children})
      if (this.state.type === 'number') {
      let values = {label: option.props.children}
      if (/Decimal|int/ig.test(option.props.datatype)) {
        let decimal = 0
        if (/Decimal/ig.test(option.props.datatype)) {
          decimal = +option.props.datatype.replace(/Decimal\(18,/ig, '').replace(')', '')
        }
        this.props.form.setFieldsValue({decimal})
        values.type = 'number'
        values.decimal = decimal
      } else if (/nvarchar/ig.test(option.props.datatype)) {
        values.type = 'text'
      }
      if (values.type !== this.state.type) {
        values.perspective = ''
        let _options = fromJS(columnTypeOptions[values.type]).toJS()
        this.setState({
          type: values.type,
          formlist: this.state.formlist.map(item => {
            item.hidden = !_options.includes(item.key)
            return item
          })
        }, () => {
          this.props.form.setFieldsValue(values)
        })
      } else {
        this.props.form.setFieldsValue(values)
      }
    } else if (key === 'format' && value === 'percent') {
      this.props.form.setFieldsValue({postfix: '%'})
@@ -121,9 +132,9 @@
      let _options = fromJS(columnTypeOptions[this.state.type]).toJS()
      if (value === 'linkmenu') {
        _options.push('linkmenu', 'linkfields')
        _options.push('linkmenu', 'linkfields', 'open')
      } else if (value === 'linkurl') {
        _options.push('linkurl', 'linkfields')
        _options.push('linkurl', 'linkfields', 'open')
      }
      this.setState({
@@ -157,7 +168,7 @@
        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
@@ -179,7 +190,7 @@
        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
@@ -197,7 +208,7 @@
            </Form.Item>
          </Col>
        )
      } else if (item.type === 'select') { // 下拉搜索
      } else if (item.type === 'select') {
        fields.push(
          <Col span={12} key={index}>
            <Form.Item label={item.label}>
@@ -217,8 +228,8 @@
                  getPopupContainer={() => document.getElementById('columnwinter')}
                >
                  {item.options.map((option, index) =>
                    <Select.Option key={`${option.value || option.field}${index}`} datatype={option.datatype || ''} value={option.value || option.field}>
                      {option.text || option.label}
                    <Select.Option key={index} datatype={option.datatype || ''} value={(option.value || option.field || option.MenuID)}>
                      {(option.text || option.label || option.MenuName)}
                    </Select.Option>
                  )}
                </Select>
@@ -230,7 +241,7 @@
        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
src/menu/components/table/normal-table/columns/index.jsx
@@ -37,17 +37,19 @@
  shouldComponentUpdate (nextProps, nextState) {
    if (this.props.rowSpan !== nextProps.rowSpan || this.props.colSpan !== nextProps.colSpan) {
      return true
    }
    if (!nextProps.column) return false
    return !is(fromJS(this.props.column), fromJS(nextProps.column)) ||
      !is(fromJS(this.props.fields), fromJS(nextProps.fields)) ||
      this.props.index !== nextProps.index ||
      this.props.rowSpan !== nextProps.rowSpan ||
      this.props.colSpan !== nextProps.colSpan
      this.props.index !== nextProps.index
  }
  render() {
    const { connectDragSource, connectDropTarget, moveCol, addElement, updateCol, editColumn, deleteCol, index, column, align, fields, children, ...restProps } = this.props
    const { connectDragSource, connectDropTarget, moveCol, addElement, updateCol, editColumn, changeStyle, deleteCol, index, column, align, fields, children, ...restProps } = this.props
    if (index !== undefined) {
      return connectDragSource(
@@ -58,6 +60,7 @@
                <Icon className="plus" title="添加" type="plus" onClick={() => this.props.addElement(column)} /> : null
              }
              <Icon className="edit" title="编辑" type="edit" onClick={() => this.props.editColumn(column)} />
              {column && column.type === 'custom' ? <Icon className="style" title="调整样式" onClick={() => this.props.changeStyle(column)} type="font-colors" /> : null}
              <Icon className="close" title="删除" type="delete" onClick={this.deleteCol} />
              {column && ['text', 'number'].includes(column.type) ? <MarkColumn columns={fields} marks={column.marks} onSubmit={this.updateMarks} /> : null }
            </div>
@@ -141,7 +144,7 @@
    if (column && column.type === 'custom') {
      return (
        <td style={{padding: 0, minWidth: column.Width || 100}} className={className}>
        <td style={{padding: 0, minWidth: column.Width || 100, ...(column.style || {})}} className={className}>
          <CardCellComponent cards={config} cardCell={column} elements={column.elements} updateElement={this.updateCard}/>
        </td>
      )
@@ -154,7 +157,7 @@
    } else if (column) {
      return (
        <td style={{...style, minWidth: column.Width || 100}} className={className}>
          {column.field}
          {column.field || (column.type === 'index' ? '$Index' : '')}
          {column.marks && column.marks.length ? <Icon className="profile" type="ant-design"/> : null}
        </td>
      )
@@ -176,11 +179,13 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    tableId: '',
    data: [{uuid: Utils.getuuid()}],
    refresh: false,    // 强制刷新
    columns: [],
    fields: [],
    editStyleCard: null,
    lineMarks: []
  }
@@ -341,6 +346,24 @@
    this.updateCol(col)
  }
  changeStyle = (col) => {
    this.setState({
      editStyleCard: fromJS(col).toJS()
    })
    MKEmitter.emit('changeStyle', [col.uuid], ['font', 'padding'], col.style || {})
  }
  getStyle = (comIds, style) => {
    const { editStyleCard } = this.state
    if (!editStyleCard || comIds[0] !== editStyleCard.uuid || comIds.length !== 1) return
    let _card = {...editStyleCard, style}
    this.updateCol(_card)
  }
  cancelCol = () => {
    const { card } = this.state
@@ -361,22 +384,29 @@
  }
  deleteCol = (col) => {
    const { appType } = this.state
    let _columns = fromJS(this.state.columns).toJS()
    _columns = this.loopDelCol(_columns, col)
    if (col.type === 'action') {
      let uuids = []
      col.elements && col.elements.forEach(c => {
        uuids.push(c.uuid)
      })
      MKEmitter.emit('delButtons', uuids)
    }
    _columns = this.loopDelCol(_columns, col)
    this.setState({
      columns: _columns
    }, () => {
      this.props.updatecolumn({...this.props.config, cols: _columns})
    })
    if (col.type !== 'action' || appType === 'mob') return
    let uuids = []
    col.elements && col.elements.forEach(c => {
      if (appType === 'pc' && c.OpenType !== 'popview') return
      uuids.push(c.uuid)
    })
    if (uuids.length === 0) return
    MKEmitter.emit('delButtons', uuids)
  }
  updateLineMarks = (vals) => {
@@ -433,6 +463,7 @@
          updateCol: this.updateCol,
          addElement: this.addElement,
          editColumn: this.editColumn,
          changeStyle: this.changeStyle,
          deleteCol: this.deleteCol,
        }),
        children: col.subcols && col.subcols.length ? this.handlecolumns(col.subcols, fields, config, true) : null,
@@ -480,6 +511,20 @@
    })
  }
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
  }
  render() {
    const { config } = this.props
    const { fields, card, lineMarks, dict, tableId } = this.state
@@ -494,8 +539,16 @@
    const columns = this.handlecolumns(this.state.columns, fields, config)
    let style = {}
    if (config.wrap.color) {
      style.color = config.wrap.color
    }
    if (config.wrap.fontSize) {
      style.fontSize = config.wrap.fontSize
    }
    return (
      <div className={`normal-table-columns ${config.setting.laypage} ${config.wrap.tableType}`} id={tableId}>
      <div className={`normal-table-columns ${config.setting.laypage} ${config.wrap.tableType} ${config.wrap.mode || ''}`} id={tableId}>
        <div className="col-control">
          <Icon title="复制" type="copy" onClick={this.copycolumn} />
          <MarkColumn columns={fields} type="line" marks={lineMarks} onSubmit={this.updateLineMarks} />
@@ -506,6 +559,7 @@
            rowKey="uuid"
            size={config.wrap.size || 'middle'}
            rowClassName="editable-row"
            style={style}
            bordered={config.wrap.bordered !== 'false'}
            components={components}
            dataSource={this.state.data}
src/menu/components/table/normal-table/columns/index.scss
@@ -1,5 +1,9 @@
.normal-table-columns {
  position: relative;
  .ant-table {
    color: inherit;
    font-size: inherit;
  }
  .ant-table-body {
    overflow-x: auto;
    tr {
@@ -89,3 +93,29 @@
    border-radius: 0;
  }
}
.normal-table-columns.ghost {
  .ant-table-thead > tr {
    > th {
      color: inherit;
      background: transparent;
      .ant-table-column-sorter .ant-table-column-sorter-inner {
        color: inherit;
      }
    }
    > th:hover {
      background: transparent;
    }
  }
  .ant-table-body {
    overflow-x: auto;
    tr {
      td {
        background: transparent;
      }
    }
    tr:hover td {
      background: transparent!important;
    }
  }
}
src/menu/components/table/normal-table/index.jsx
@@ -5,7 +5,7 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
@@ -35,6 +35,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    card: null,
    back: false
  }
@@ -60,9 +61,9 @@
          { origin: true, uuid: Utils.getuuid(), label: 'label', type: 'date', match: 'greater' }
        ],
        action: [
          { origin: true, uuid: Utils.getuuid(), label: '添加', intertype: 'system', OpenType: 'pop', icon: 'plus', class: 'green', btnstyle: {color: 'rgb(255, 255, 255)', background: 'rgb(38, 194, 129)', marginRight: '15px'} },
          { origin: true, uuid: Utils.getuuid(), label: '修改', intertype: 'system', OpenType: 'pop', icon: 'form', class: 'purple', btnstyle: {color: 'rgb(255, 255, 255)', background: 'rgb(142, 68, 173)', marginRight: '15px'} },
          { origin: true, uuid: Utils.getuuid(), label: '删除', intertype: 'system', OpenType: 'prompt', icon: 'delete', class: 'danger', btnstyle: {color: 'rgb(255, 255, 255)', background: 'rgb(255, 77, 79)', marginRight: '15px'} }
          { origin: true, uuid: Utils.getuuid(), label: '添加', intertype: 'system', OpenType: 'pop', icon: 'plus', class: 'green', style: {color: 'rgb(255, 255, 255)', background: 'rgb(38, 194, 129)', marginRight: '15px'} },
          { origin: true, uuid: Utils.getuuid(), label: '修改', intertype: 'system', OpenType: 'pop', icon: 'form', class: 'purple', style: {color: 'rgb(255, 255, 255)', background: 'rgb(142, 68, 173)', marginRight: '15px'} },
          { origin: true, uuid: Utils.getuuid(), label: '删除', intertype: 'system', OpenType: 'prompt', icon: 'delete', class: 'danger', style: {color: 'rgb(255, 255, 255)', background: 'rgb(255, 77, 79)', marginRight: '15px'} }
        ],
        name: card.name,
        subtype: card.subtype,
@@ -88,8 +89,11 @@
        _card.style = config.style
        _card.headerStyle = config.headerStyle
        let oriUids = {}
        _card.action = config.action.map(item => {
          item.uuid = Utils.getuuid()
          let _uuid = Utils.getuuid()
          oriUids[item.uuid] = _uuid
          item.uuid = _uuid
          return item
        })
        _card.search = config.search.map(item => {
@@ -113,6 +117,10 @@
          }
          return col
        })
        if (_card.wrap.doubleClick) {
          _card.wrap.doubleClick = oriUids[_card.wrap.doubleClick] || ''
        }
      }
      
      this.setState({
@@ -274,7 +282,7 @@
    newcard.errorTime = 10
    newcard.verify = null
    newcard.show = 'button'
    newcard.btnstyle = {marginRight: '15px'}
    newcard.style = {marginRight: '15px'}
    // 注册事件-添加按钮
    MKEmitter.emit('addButton', card.uuid, newcard)
@@ -291,13 +299,13 @@
  }
  setSubConfig = (item) => {
    const { card } = this.state
    const { card, appType } = this.state
    let btn = fromJS(item).toJS()
    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' },
          setting: { title: btn.label, width: appType === 'mob' ? 100 : 60, cols: '2', container: 'view', focus: '', finish: 'close', clickouter: 'unclose', display: 'modal' },
          tables: [],
          groups: [],
          fields: []
@@ -378,15 +386,16 @@
  }
  render() {
    const { card } = this.state
    const { card, appType } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-normal-table-edit-box" style={{...card.style, height: card.wrap.height}} onClick={this.clickComponent} id={card.uuid}>
      <div className="menu-normal-table-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader defaultshow="hidden" hideSearch="true" config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加列" onClick={this.addColumns} type="plus" />
            <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" />
            {appType !== 'mob' ? <Icon className="plus" title="添加搜索" onClick={this.addSearch} type="plus-circle" /> : null}
            <Icon className="plus" title="添加按钮" onClick={this.addButton} type="plus-square" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="normaltable" card={card}/>
src/menu/components/table/normal-table/wrapsetting/index.jsx
@@ -60,7 +60,7 @@
          wrapClassName="popview-modal"
          title="表格设置"
          visible={visible}
          width={700}
          width={750}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
src/menu/components/table/normal-table/wrapsetting/settingform/index.jsx
@@ -14,7 +14,8 @@
  }
  state = {
    roleList: []
    roleList: [],
    appType: sessionStorage.getItem('appType')
  }
  UNSAFE_componentWillMount () {
@@ -56,7 +57,7 @@
  render() {
    const { wrap, config } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const { roleList, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -135,6 +136,18 @@
                )}
              </Form.Item>
            </Col>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label="可收起">
                {getFieldDecorator('collapse', {
                  initialValue: wrap.collapse || 'false'
                })(
                  <Radio.Group>
                    <Radio key="true" value="true"> 是 </Radio>
                    <Radio key="false" value="false"> 否 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="表格大小">
                {getFieldDecorator('size', {
@@ -163,6 +176,39 @@
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="模式">
                {getFieldDecorator('mode', {
                  initialValue: wrap.mode || 'default'
                })(
                  <Radio.Group>
                    <Radio key="default" value="default"> 常规 </Radio>
                    <Radio key="ghost" value="ghost"> 透明 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12} style={{height: '64px'}}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="默认值 rgba(0, 0, 0, 0.65)。">
                  <Icon type="question-circle" />
                  字体颜色
                </Tooltip>
              }>
                {getFieldDecorator('color', {
                  initialValue: wrap.color || 'rgba(0, 0, 0, 0.65)'
                })(
                  <ColorSketch />
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="字体大小">
                {getFieldDecorator('fontSize', {
                  initialValue: wrap.fontSize || 14
                })(<InputNumber min={14} max={30} precision={0} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
@@ -180,7 +226,19 @@
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="高级搜索弹窗的宽度,注:当宽度值小于100时表示占窗口的百分比,大于100时表示宽度的绝对值。">
                  <Icon type="question-circle" />
                  高级搜索
                </Tooltip>
              }>
                {getFieldDecorator('advanceWidth', {
                  initialValue: wrap.advanceWidth || 1000
                })(<InputNumber min={10} max={3000} precision={0} onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col> : null}
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="双击表格中行,触发的按钮。">
                  <Icon type="question-circle" />
@@ -197,7 +255,7 @@
                  </Select>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
src/menu/components/tabs/antv-tabs/dragabletabs.jsx
@@ -1,5 +1,6 @@
import React, { Component } from 'react'
import { Tabs } from 'antd'
import { is, fromJS } from 'immutable'
import { DndProvider, DragSource, DropTarget } from 'react-dnd'
// Drag & Drop node
@@ -52,8 +53,6 @@
    const newOrder = this.state.order.slice()
    const { children } = this.props
    if (dragKey === 'tool' || hoverKey === 'tool') return
    React.Children.forEach(children, c => {
      if (newOrder.indexOf(c.key) === -1) {
        newOrder.push(c.key)
@@ -65,12 +64,11 @@
    newOrder.splice(dragIndex, 1)
    newOrder.splice(hoverIndex, 0, dragKey)
    let _order = newOrder.filter(item => item !== 'tool')
    
    this.setState({
      order: [..._order, 'tool']
      order: newOrder
    })
    this.props.tabsMove(_order)
    this.props.tabsMove(newOrder)
  }
  renderTabBar = (props, DefaultTabBar) => (
@@ -83,10 +81,16 @@
    </DefaultTabBar>
  )
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState)) ||
      !is(fromJS(nextProps.children), fromJS(this.props.children)) ||
      nextProps.tabPosition !== this.props.tabPosition ||
      nextProps.type !== this.props.type
  }
  render() {
    const { order } = this.state
    const { children } = this.props
    const tabs = []
    React.Children.forEach(children, c => {
      tabs.push(c)
src/menu/components/tabs/antv-tabs/index.jsx
@@ -7,7 +7,8 @@
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import DraggableTabs from './dragabletabs'
import { resetStyle } from '@/utils/utils-custom.js'
import MenuUtils from '@/utils/utils-custom.js'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
@@ -31,6 +32,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    tabs: null,
    editab: null,
    labelvisible: false
@@ -74,6 +76,7 @@
  componentDidMount () {
    MKEmitter.addListener('submitStyle', this.getStyle)
    MKEmitter.addListener('submitSearch', this.getSearch)
    MKEmitter.addListener('tabsChange', this.handleTabsChange)
    MKEmitter.addListener('submitComponentStyle', this.updateComponentStyle)
  }
@@ -86,6 +89,7 @@
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitSearch', this.getSearch)
    MKEmitter.removeListener('tabsChange', this.handleTabsChange)
    MKEmitter.removeListener('submitComponentStyle', this.updateComponentStyle)
  }
@@ -215,6 +219,7 @@
    this.tabLabelRef.handleConfirm().then(res => {
      editab.label = res.label
      editab.icon = res.icon
      editab.hasSearch = res.hasSearch || ''
      editab.blacklist = res.blacklist
      if (editab.uuid) {
@@ -245,12 +250,18 @@
    tabs.subtabs = tabs.subtabs.filter(t => t.uuid !== tab.uuid)
    let uuids = MenuUtils.getDelButtonIds({...tab, type: 'group'})
    confirm({
      title: '确定删除标签?',
      content: '',
      onOk() {
        _this.setState({tabs})
        _this.props.updateConfig(tabs)
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -288,6 +299,45 @@
    this.props.updateConfig(tabs)
  }
  getSearch = (config) => {
    const { tabs } = this.state
    if (tabs.uuid !== config.uuid) return
    let _tabs = fromJS(tabs).toJS()
    _tabs.subtabs = _tabs.subtabs.map(t => {
      if (t.uuid === config.tabId) {
        t.search = config.search
      }
      return t
    })
    this.setState({
      tabs: _tabs
    })
    this.props.updateConfig(_tabs)
  }
  setSearch = (tab) => {
    const { tabs } = this.state
    let card = {
      uuid: tabs.uuid,
      tabId: tab.uuid,
      search: tab.search
    }
    if (!card.search) {
      card.search = {
        floor: 1,
        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
        groups: [],
        fields: []
      }
    }
    MKEmitter.emit('changeSearch', card)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
@@ -296,9 +346,11 @@
  }
  render() {
    const { tabs, dict, labelvisible, editab } = this.state
    const { tabs, dict, labelvisible, editab, appType } = this.state
    let _style = resetStyle(tabs.style)
    return (
      <div className="menu-tabs-edit-box" style={tabs.style} onClick={this.clickComponent} id={tabs.uuid}>
      <div className={'menu-tabs-edit-box ' + tabs.setting.display} style={_style} onClick={this.clickComponent} id={tabs.uuid}>
        <DraggableTabs tabPosition={tabs.setting.position} type={tabs.setting.tabStyle} tabsMove={this.moveSwitch}>
          {tabs.subtabs.map(tab => (
            <TabPane tab={
@@ -312,24 +364,23 @@
                <span>{tab.icon ? <Icon type={tab.icon} /> : null}{tab.label}</span>
              </Popover>
            } key={tab.uuid}>
              {appType === 'mob' && tabs.setting.position === 'top' && tabs.setting.display === 'inline-block' && tab.hasSearch === 'icon' ?
                <Icon className="search-icon" onDoubleClick={() => this.setSearch(tab)} type="search" /> : null}
              <TabComponents config={tab} handleList={this.updateTabComponent} deleteCard={this.deleteCard} />
            </TabPane>
          ))}
          <TabPane disabled tab={
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
                <Icon className="plus" title="添加标签" type="plus" onClick={this.tabAdd} />
                <SettingComponent config={tabs} updateConfig={this.updateComponent} />
                <CopyComponent type="tabs" card={tabs}/>
                <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
                <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
              </div>
            } trigger="hover">
              <Icon type="tool" />
            </Popover>
          } key="tool">
          </TabPane>
        </DraggableTabs>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加标签" type="plus" onClick={this.tabAdd} />
            <SettingComponent config={tabs} updateConfig={this.updateComponent} />
            <CopyComponent type="tabs" card={tabs}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Icon className="close" title="delete" type="delete" onClick={() => this.props.deletecomponent(tabs.uuid)} />
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        <Modal
          wrapClassName="popview-modal"
          title={'标签编辑'}
@@ -344,6 +395,7 @@
          <TabLabelComponent
            dict={dict}
            tab={editab}
            setting={tabs.setting}
            inputSubmit={this.tabLabelSubmit}
            wrappedComponentRef={(inst) => this.tabLabelRef = inst}
          />
src/menu/components/tabs/antv-tabs/index.scss
@@ -6,6 +6,23 @@
  background-repeat: no-repeat;
  background-size: cover;
  >.anticon-tool {
    position: absolute;
    z-index: 2;
    font-size: 16px;
    right: 1px;
    top: 1px;
    cursor: pointer;
    padding: 5px;
    background: rgba(255, 255, 255, 0.55);
  }
  .ant-tabs.ant-tabs-left, .ant-tabs.ant-tabs-bottom {
    .tab-shell-inner {
      padding-top: 25px;
    }
  }
  .ant-tabs-tabpane-active {
    min-height: 200px;
  }
@@ -20,41 +37,25 @@
  }
  
  .ant-tabs .ant-tabs-left-bar .ant-tabs-tab {
    padding: 0px;
    text-align: right;
    > span {
      display: inline-block;
      padding: 8px 24px;
    }
    .anticon-tool {
      padding: 8px 24px;
    }
  }
  .ant-tabs .ant-tabs-right-bar .ant-tabs-tab {
    padding: 0px;
    text-align: left;
    > span {
      display: inline-block;
      padding: 8px 24px;
    }
    .anticon-tool {
      padding: 8px 24px;
    }
  }
  .ant-tabs-tab {
    padding: 0px;
    padding: 0px!important;
    text-align: center;
    > span {
      display: inline-block;
      padding: 12px 16px;
    }
    .anticon-tool {
      color: rgba(0, 0, 0, 0.65);
      font-size: 16px;
      padding: 12px 16px;
      margin-right: 0px;
      min-width: 100%;
      cursor: pointer;
    }
  }
  .ant-tabs-bottom .ant-tabs-bottom-bar .ant-tabs-ink-bar {
@@ -66,9 +67,6 @@
        > span {
          padding: 0px 16px;
        }
        .anticon-tool {
          padding: 0px 16px;
        }
      }
      .ant-tabs-tab-active {
        padding-left: 0px!important;
@@ -78,24 +76,47 @@
    
    .ant-tabs-card-bar {
      .ant-tabs-tab {
        padding: 0px;
        > span {
          display: inline-block;
          padding: 0px 16px;
        }
      }
      .ant-tabs-tab:last-child {
        padding: 0px;
        border: 0px;
        background: transparent;
        .anticon-tool {
          padding: 12px 16px;
        }
      }
    }
  }
  .search-icon {
    position: absolute;
    top: 10px;
    right: 40px;
    font-size: 18px;
    cursor: pointer;
    padding: 3px;
  }
}
.menu-tabs-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
.mob-shell {
  .menu-tabs-edit-box.flex {
    >.ant-tabs.ant-tabs-top, >.ant-tabs.ant-tabs-bottom {
      >.ant-tabs-bar {
        >.ant-tabs-nav-container {
          >.ant-tabs-nav-wrap {
            >.ant-tabs-nav-scroll {
              >.ant-tabs-nav {
                display: block;
                >div {
                  display: flex;
                  >.ant-tabs-tab {
                    flex: 1;
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}
src/menu/components/tabs/tabcomponents/card.jsx
@@ -7,15 +7,20 @@
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
const NormalForm = asyncComponent(() => import('@/menu/components/form/normal-form'))
const NormalTable = asyncComponent(() => import('@/menu/components/table/normal-table'))
const NormalGroup = asyncComponent(() => import('@/menu/components/group/normal-group'))
const BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const CodeSandbox = asyncComponent(() => import('@/menu/components/code/sandbox'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -57,6 +62,14 @@
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'dashboard') {
      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tree') {
      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'scatter') {
      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      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') {
@@ -75,6 +88,8 @@
      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}/>)
    }
  }
src/menu/components/tabs/tabcomponents/index.jsx
@@ -54,8 +54,11 @@
      title: `确定删除《${card.name}》吗?`,
      content: hasComponent ? '当前组件中含有子组件!' : '',
      onOk() {
        MKEmitter.emit('delButtons', uuids)
        handleList({...config, components: cards.filter(item => item.uuid !== card.uuid)})
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -89,7 +92,7 @@
      let name = ''
      let names = {
        bar: '柱状图',
        bbar: '柱状图',
        line: '折线图',
        tabs: '标签组',
        pie: '饼图',
@@ -97,7 +100,12 @@
        table: '表格',
        group: '分组',
        editor: '富文本',
        code: '自定义',
        carousel: '轮播',
        form: '表单',
        dashboard: '仪表盘',
        scatter: '散点图',
        tree: '树形列表',
        card: '卡片'
      }
      let i = 1
src/menu/components/tabs/tabcomponents/index.scss
@@ -1,9 +1,6 @@
.tab-shell-inner {
  margin: -8px;
  margin: 0px;
  >.ant-col {
    padding: 8px;
  }
  .anticon {
    cursor: unset;
  }
src/menu/components/tabs/tablabelform/index.jsx
@@ -1,18 +1,18 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Icon, Select } from 'antd'
import { Form, Row, Col, Input, Icon, Select, Radio } from 'antd'
// import { formRule } from '@/utils/option.js'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,        // 字典项
    tab: PropTypes.object,         // 数据源配置
    inputSubmit: PropTypes.func    // 回车事件
    dict: PropTypes.object,
    setting: PropTypes.object,
    tab: PropTypes.object,
    inputSubmit: PropTypes.func
  }
  state = {roleList: []}
  state = {roleList: [], appType: sessionStorage.getItem('appType')}
  UNSAFE_componentWillMount () {
    let roleList = sessionStorage.getItem('sysRoles')
@@ -51,9 +51,9 @@
  }
  render() {
    const { tab } = this.props
    const { tab, setting } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const { roleList, appType } = this.state
    const formItemLayout = {
      labelCol: {
@@ -97,6 +97,18 @@
              )}
            </Form.Item>
          </Col>
          {appType === 'mob' && setting.position === 'top' && setting.display === 'inline-block' ? <Col span={24}>
            <Form.Item label="搜索">
              {getFieldDecorator('hasSearch', {
                initialValue: tab.hasSearch || 'false'
              })(
                <Radio.Group>
                  <Radio value="false">无</Radio>
                  <Radio value="icon">有</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col> : null}
          <Col span={24}>
            <Form.Item label="黑名单">
              {getFieldDecorator('blacklist', {
src/menu/components/tabs/tabsetting/settingform/index.jsx
@@ -12,6 +12,8 @@
  }
  state = {
    appType: sessionStorage.getItem('appType'),
    position: this.props.setting.position,
    roleList: []
  }
@@ -54,7 +56,7 @@
  render() {
    const { setting } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const { roleList, appType, position } = this.state
    const formItemLayout = {
      labelCol: {
@@ -112,7 +114,7 @@
                {getFieldDecorator('position', {
                  initialValue: setting.position || 'top'
                })(
                  <Select>
                  <Select onChange={(val) => this.setState({position: val})}>
                    <Select.Option key="top" value="top"> top </Select.Option>
                    <Select.Option key="bottom" value="bottom"> bottom </Select.Option>
                    <Select.Option key="left" value="left"> left </Select.Option>
@@ -121,7 +123,7 @@
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
            {appType !== 'mob' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="标签位置为top时有效,默认值为line。">
                  <Icon type="question-circle" />
@@ -137,7 +139,19 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            {appType === 'mob' && (position === 'top' || position === 'bottom') ? <Col span={12}>
              <Form.Item label="标签显示">
                {getFieldDecorator('display', {
                  initialValue: setting.display || 'flex'
                })(
                  <Radio.Group>
                    <Radio value="flex">弹性布局</Radio>
                    <Radio value="inline-block">定宽</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
src/menu/components/tree/antd-tree/index.jsx
New file
@@ -0,0 +1,188 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover, Tree } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import 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 NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
const UserComponent = asyncIconComponent(() => import('@/menu/components/share/usercomponent'))
const ClockComponent = asyncIconComponent(() => import('@/menu/components/share/clockcomponent'))
const WrapComponent = asyncIconComponent(() => import('./wrapsetting'))
const { TreeNode } = Tree
class AntdTree 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 || '',
        dataName: card.dataName || '',
        format: 'array',    // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: true,   // 组件属性 - 数据是否可切换
        width: card.width || 12,
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, title: '', width: card.width || 12, showIcon: 'false', showLine: 'false', searchable: 'false' },
        style: { marginLeft: '8px', marginRight: '8px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        columns: [],
        scripts: [],
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
        _card.headerStyle = config.headerStyle
      }
      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)
  }
  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[0] !== card.uuid || comIds.length !== 1) return
    let _card = {...card, style}
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  /**
   * @description 更新搜索条件配置信息
   */
  updateconfig = (config) => {
    this.setState({
      card: config
    })
    this.props.updateConfig(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 } = this.state
    let _style = resetStyle(card.style)
    return (
      <div className="menu-editor-sand-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="normaltable" card={card}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <ClockComponent config={card} updateConfig={this.updateComponent}/>
            <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>
        <div className="tree-box">
          <Tree defaultExpandAll={true} blockNode showIcon={card.wrap.showIcon === 'true'} showLine={card.wrap.showLine === 'true'} >
            <TreeNode icon={<Icon type="folder-open" />} title="parent 0" key="0-0">
              <TreeNode icon={<Icon type="file" />} title="leaf 0-0" key="0-0-0" isLeaf />
              <TreeNode icon={<Icon type="file" />} title="leaf 0-1" key="0-0-1" isLeaf />
            </TreeNode>
            <TreeNode icon={<Icon type="folder-open" />} title="parent 1" key="0-1">
              <TreeNode icon={<Icon type="file" />} title="leaf 1-0" key="0-1-0" isLeaf />
              <TreeNode icon={<Icon type="file" />} title="leaf 1-1" key="0-1-1" isLeaf />
            </TreeNode>
          </Tree>
        </div>
      </div>
    )
  }
}
export default AntdTree
src/menu/components/tree/antd-tree/index.scss
New file
@@ -0,0 +1,36 @@
.menu-editor-sand-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 30px;
  .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);
  }
  .empty-content {
    text-align: center;
    font-size: 30px;
    margin: 0;
    line-height: 90px;
    color: #bcbcbc;
  }
}
.menu-editor-sand-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-editor-sand-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/menu/components/tree/antd-tree/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" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title="基本设置"
          visible={visible}
          width={700}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            config={config}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/menu/components/tree/antd-tree/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/menu/components/tree/antd-tree/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,262 @@
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="标题">
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="数据值字段。">
                  <Icon type="question-circle" />
                  Value
                </Tooltip>
              }>
                {getFieldDecorator('valueField', {
                  initialValue: wrap.valueField || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + 'Value字段!'
                    }
                  ]
                })(
                  <Select>
                    {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" />
                  Label
                </Tooltip>
              }>
                {getFieldDecorator('labelField', {
                  initialValue: wrap.labelField || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + 'Label字段!'
                    }
                  ]
                })(
                  <Select>
                    {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" />
                  Parent
                </Tooltip>
              }>
                {getFieldDecorator('parentField', {
                  initialValue: wrap.parentField || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + 'Parent字段!'
                    }
                  ]
                })(
                  <Select>
                    {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('mark', {
                  initialValue: wrap.mark || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="图标">
                {getFieldDecorator('showIcon', {
                  initialValue: wrap.showIcon || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="分割线">
                {getFieldDecorator('showLine', {
                  initialValue: wrap.showLine || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="过滤条件">
                {getFieldDecorator('searchable', {
                  initialValue: wrap.searchable || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</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/tree/antd-tree/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,15 @@
.model-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
  .color-sketch-block {
    position: relative;
    top: 7px;
  }
}
src/menu/datasource/index.jsx
@@ -16,6 +16,7 @@
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    appType: sessionStorage.getItem('appType'),
    sourcelist: [],
    mainSearch: [],
    visible: false,
@@ -35,42 +36,129 @@
  editDataSource = () => {
    const { config } = this.props
    const { appType } = this.state
    let search = []
    let parents = []
    let _conf = config
    let getParents = (box) => {
      box.components.forEach(item => {
        if (item.type !== 'tabs') return
    let menu = fromJS(window.GLOB.customMenu).toJS()
        item.subtabs.forEach(tab => {
          if (_conf.parentId === tab.parentId && _conf.tabId === tab.uuid) {
            parents.unshift(tab)
            _conf = item
            if (_conf.parentId && _conf.tabId) {
              getParents(tab)
            }
          } else {
            getParents(tab)
          }
        })
      })
    }
    if (config.parentId && config.tabId) {
      getParents(window.GLOB.customMenu)
    }
    parents.unshift(window.GLOB.customMenu)
    parents.forEach(parent => {
      parent.components.forEach(item => {
        if (item.type === 'search') {
          search = item.search
    if (appType === 'mob') {
      let ms = null
      menu.components.forEach(item => {
        if (item.type === 'topbar' && (item.wrap.type === 'search' || (item.wrap.type === 'navbar' && item.wrap.search === 'true'))) {
          ms = item.search
        }
      })
    })
      if (config.floor > 1) {
        let _search = null
        let filterComponent = (box) => {
          box.components.forEach(item => {
            if (_search) return
            if (item.uuid === config.uuid) {
              _search = box.slist.pop()
            } else if (item.type === 'group') {
              item.components.forEach(m => {
                if (m.uuid !== config.uuid) return
                _search = box.slist.pop()
              })
            } else if (item.type === 'tabs') {
              let able = item.setting.display === 'inline-block' && item.setting.position === 'top'
              item.subtabs.forEach(tab => {
                if (able && tab.hasSearch === 'icon' && tab.search) {
                  tab.slist = [...box.slist, tab.search]
                } else {
                  tab.slist = [...box.slist]
                }
                filterComponent(tab)
              })
            }
          })
        }
        menu.slist = []
        filterComponent(menu)
        if (_search) {
          ms = _search
        }
      }
      if (ms) {
        if (ms.setting.type === 'search') {
          search.push({
            type: 'text',
            label: '搜索栏',
            field: ms.setting.field,
            match: ms.setting.match,
            required: ms.setting.required,
            value: ms.setting.initval || ''
          })
        }
        ms.fields.forEach(item => {
          if (item.type === 'range') {
            item.initval = `${item.minValue},${item.maxValue}`
          }
          search.push(item)
        })
        ms.groups.forEach(group => {
          if (group.setting.type === 'search') {
            search.push({
              type: 'text',
              label: group.wrap.name,
              field: group.setting.field,
              match: group.setting.match,
              required: group.setting.required,
              value: group.setting.initval || ''
            })
          }
          group.fields.forEach(item => {
            if (item.type === 'range') {
              item.initval = `${item.minValue},${item.maxValue}`
            }
            search.push(item)
          })
        })
      }
    } else {
      if (config.floor > 1) {
        let _search = null
        let filterComponent = (box) => {
          box.components.forEach(item => {
            if (_search) return
            if (item.type === 'search') {
              box.slist = [...box.slist, item.search]
            } else if (item.uuid === config.uuid) {
              _search = box.slist.pop()
            } else if (item.type === 'group') {
              item.components.forEach(m => {
                if (m.uuid !== config.uuid) return
                _search = box.slist.pop()
              })
            } else if (item.type === 'tabs') {
              item.subtabs.forEach(tab => {
                tab.slist = [...box.slist]
                filterComponent(tab)
              })
            }
          })
        }
        menu.slist = []
        filterComponent(menu)
        if (_search) {
          search = _search
        }
      } else {
        menu.components.forEach(item => {
          if (item.type !== 'search') return
          search = item.search
        })
      }
    }
    this.setState({
      visible: true,
@@ -100,7 +188,19 @@
          return item
        })
      }
      if (window.GLOB.funcs && window.GLOB.funcs.length > 0) {
        window.GLOB.funcs.forEach(m => {
          let reg = new RegExp('\\$ex@' + m.func_code + '@ex\\$', 'ig')
          if (res.setting.dataresource) {
            res.setting.dataresource = res.setting.dataresource.replace(reg, `/*$ex@${m.func_code}-begin*/\n${m.key_sql}\n/*@ex$-end*/`)
          }
          res.scripts.forEach(item => {
            item.sql = item.sql.replace(reg, `/*$ex@${m.func_code}-begin*/\n${m.key_sql}\n/*@ex$-end*/`)
          })
        })
      }
      this.setState({loading: false, visible: false})
      this.props.updateConfig({...config, ...res})
    }, () => {
src/menu/datasource/verifycard/customscript/index.jsx
@@ -32,9 +32,7 @@
      if (!item.field) return
      if (item.type === 'group') {
        if (item.transfer === 'true') {
          _usefulFields.push(item.field)
        }
        _usefulFields.push(item.field)
        _usefulFields.push(item.datefield)
        _usefulFields.push(item.datefield + '1')
      } else if (['dateweek', 'datemonth', 'daterange'].includes(item.type)) {
@@ -60,9 +58,7 @@
        if (!item.field) return
        if (item.type === 'group') {
          if (item.transfer === 'true') {
            _usefulFields.push(item.field)
          }
          _usefulFields.push(item.field)
          _usefulFields.push(item.datefield)
          _usefulFields.push(item.datefield + '1')
        } else if (['dateweek', 'datemonth', 'daterange'].includes(item.type)) {
@@ -220,7 +216,7 @@
          </Col>
          <Col span={24} className="sqlfield">
            <Form.Item label={'可用字段'}>
              id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}
              id, bid, loginuid, sessionuid, userid, username, fullname, login_city, appkey, time_id, orderBy, pageSize, pageIndex{usefulFields ? ', ' + usefulFields : ''}{window.GLOB.urlFields && window.GLOB.urlFields.length > 0 ? ', ' + window.GLOB.urlFields.join(', ') : ''}
            </Form.Item>
          </Col>
          <Col span={10} style={{width: '43%'}}>
src/menu/datasource/verifycard/index.jsx
@@ -1,7 +1,7 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { fromJS } from 'immutable'
import { Form, Tabs, Popconfirm, Icon, notification, Modal, Typography, Spin } from 'antd'
import { Form, Tabs, Popconfirm, Icon, notification, Modal, Typography, Spin, message } from 'antd'
import moment from 'moment'
import Api from '@/api'
@@ -34,6 +34,7 @@
    initsql: '',          // sql验证时变量声明及赋值
    usefulfields: '',
    defaultsql: '',       // 默认Sql
    defaultSearch: '',
    systemScripts: [],
    colColumns: [
      {
@@ -49,6 +50,7 @@
        inputType: 'input',
        editable: true,
        unique: true,
        copy: true,
        width: '28%'
      },
      {
@@ -193,12 +195,32 @@
    getcomponentmarks(menu, config)
    let _setting = fromJS(config.setting).toJS()
    let scripts = fromJS(config.scripts).toJS()
    if (window.GLOB.funcs && window.GLOB.funcs.length > 0) {
      window.GLOB.funcs.forEach(m => {
        let reg = new RegExp(`\\/\\*\\$ex@${m.func_code}-begin\\*\\/[\\s\\S]+\\/\\*@ex\\$-end\\*\\/`, 'ig')
        if (_setting.dataresource) {
          _setting.dataresource = _setting.dataresource.replace(reg, `$ex@${m.func_code}@ex$`)
        }
        scripts && scripts.forEach(item => {
          item.sql = item.sql.replace(reg, `$ex@${m.func_code}@ex$`)
        })
      })
    }
    let _search = this.formatSearch(search)
    _search = Utils.joinMainSearchkey(_search)
    _search = _search.replace(/@\$@/ig, '')
    _search = _search ? 'where ' + _search : ''
    this.setState({
      scripts,
      columns: fromJS(config.columns).toJS(),
      setting: _setting,
      scripts: fromJS(config.scripts).toJS(),
      searches: search,
      defaultSearch: _search,
      varMarks: Marks
    })
@@ -372,8 +394,15 @@
          return
        }
        let _search = this.formatSearch(search)
        _search = Utils.joinMainSearchkey(_search)
        _search = _search.replace(/@\$@/ig, '')
        _search = _search ? 'where ' + _search : ''
        this.setState({
          searches: search,
          defaultSearch: _search,
          setting: res
        }, () => {
          this.sqlverify(() => { // 验证成功
@@ -427,15 +456,9 @@
  }
  getdefaultSql = () => {
    const { columns, searches, setting } = this.state
    const { columns, setting, defaultSearch } = this.state
    let defaultsql = ''
    let arr_field = columns.map(col => col.field).join(',')
    let _search = this.formatSearch(searches)
    _search = Utils.joinMainSearchkey(_search)
    _search = _search.replace(/@\$@/ig, '')
    _search = _search ? 'where ' + _search : ''
    if (setting.dataresource) {
      let _dataresource = setting.dataresource
@@ -444,7 +467,7 @@
        _dataresource = '(' + _dataresource + ') tb'
      }
      defaultsql = `select top @pageSize@ ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by @orderBy@) as rows from ${_dataresource} ${_search}) tmptable where rows > (@pageSize@ * (@pageIndex@ - 1)) order by tmptable.rows`
      defaultsql = `select top @pageSize@ ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by @orderBy@) as rows from ${_dataresource} ${defaultSearch}) tmptable where rows > (@pageSize@ * (@pageIndex@ - 1)) order by tmptable.rows`
    }
    this.setState({defaultsql})
@@ -455,7 +478,7 @@
   * @param {Array} searches 搜索条件数组
   */
  formatSearch (searches) {
    if (!searches || searches.length === 0) return []
    if (!searches) return []
    let newsearches = []
    searches.forEach(search => {
@@ -470,37 +493,32 @@
        required: search.required === 'true'
      }
      if (item.type === 'group') {
        let copy = fromJS(item).toJS()
        copy.key = search.datefield
        item.key = search.datefield
        item.type = 'daterange'
        item.match = 'between'
        item.value = [moment().format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')].join(',')
        item.value = search.initval && search.initval[0] ? search.initval[0] : '@$@'
        item.match = '='
        copy.type = 'daterange'
        copy.match = 'between'
        copy.value = [moment().format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')]
        if (search.transfer === 'true') {
          newsearches.push(item)
        }
        newsearches.push(copy)
        newsearches.push(item)
        return
      } else if (item.type === 'date') {
        item.value = moment().format('YYYY-MM-DD')
      } else if (item.type === 'datemonth') {
        item.value = moment().format('YYYY-MM')
      } else if (item.type === 'dateweek') {
        item.value = [moment().startOf('week').format('YYYY-MM-DD'), moment().endOf('week').format('YYYY-MM-DD')]
        item.value = moment().format('YYYY-MM-DD')
      } else if (item.type === 'daterange') {
        item.value = [moment().format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')]
      } else if (item.type === 'multiselect') {
        item.value = ['@$@']
        item.value = [moment().format('YYYY-MM-DD'), moment().format('YYYY-MM-DD')].join(',')
      } else if (item.type === 'range') {
      } else if (item.type === 'multiselect' || (item.type === 'checkcard' && search.multiple === 'true')) {
        item.type = 'multi'
        item.value = '@$@'
      } else {
        item.value = '@$@'
      }
      newsearches.push(item)
    })
    return newsearches
  }
@@ -527,8 +545,15 @@
            return
          }
          let _search = this.formatSearch(search)
          _search = Utils.joinMainSearchkey(_search)
          _search = _search.replace(/@\$@/ig, '')
          _search = _search ? 'where ' + _search : ''
          this.setState({
            searches: search,
            defaultSearch: _search,
            setting: res
          }, () => {
            this.sqlverify(() => { resolve({setting: res, columns, scripts }) }, reject, false)
@@ -562,7 +587,7 @@
  }
  sqlverify = (resolve, reject, change = false, testScripts) => {
    const { columns, setting, scripts, searches } = this.state
    const { columns, setting, scripts, searches, defaultSearch } = this.state
    let _scripts = scripts.filter(item => item.status !== 'false')
@@ -580,7 +605,7 @@
    }
    if ((setting.interType === 'system' && setting.execute !== 'false') || _scripts.length > 0) {
      let result = SettingUtils.getDebugSql(setting, _scripts, columns, searches)
      let result = SettingUtils.getDebugSql(setting, _scripts, columns, searches, defaultSearch)
      if (result.error) {
        notification.warning({
@@ -622,6 +647,27 @@
    })
  }
  copyColumns = () => {
    const { columns } = this.state
    let m = []
    let n = []
    columns.forEach(col => {
      m.push(`${col.field}(${col.label})`)
      n.push(col.field)
    })
    let oInput = document.createElement('input')
    oInput.value = `/*${m.join(',')}*/
      ${n.join(',')}`
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
    document.body.removeChild(oInput)
    message.success('复制成功。')
  }
  /**
   * @description 组件销毁,清除state更新
   */
@@ -657,6 +703,7 @@
              type="fields"
              updatefield={this.updatefields}
            />
            <Icon type="copy" onClick={this.copyColumns} style={{position: 'absolute', cursor: 'pointer', zIndex: 1, top: '-35px', right: '0px', color: '#1890ff'}} />
            <EditTable actions={['edit', 'move', 'copy', 'del']} type="datasourcefield" data={columns} columns={colColumns} onChange={(columns) => this.setState({columns})}/>
          </TabPane>
          <TabPane tab={
src/menu/datasource/verifycard/settingform/index.jsx
@@ -8,6 +8,8 @@
import CodeMirror from '@/templates/zshare/codemirror'
import './index.scss'
const { TextArea } = Input
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,       // 字典项
@@ -182,6 +184,10 @@
                      required: true,
                      message: this.props.dict['form.required.input'] + '表名!'
                    },
                    {
                      max: 50,
                      message: '表名最长为50个字符!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" />)}
              </Form.Item>
@@ -245,7 +251,7 @@
                })(<Input placeholder={''} autoComplete="off" />)}
              </Form.Item>
            </Col> : null}
            {interType === 'outer' ? <Col span={8}>
            {interType === 'outer' ? <Col className="outer-interface" span={24}>
              <Form.Item label="接口地址">
                {getFieldDecorator('interface', {
                  initialValue: setting.interface || '',
@@ -255,7 +261,18 @@
                      message: this.props.dict['form.required.input'] + '接口地址!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" />)}
                })(<TextArea rows={2}/>)}
              </Form.Item>
            </Col> : null}
            {interType === 'outer' ? <Col className="outer-interface" span={24}>
              <Form.Item label={<Tooltip placement="topLeft" title="正式系统接口地址,为空时使用接口地址">
                  <Icon type="question-circle" />
                  正式地址
                </Tooltip>
              }>
                {getFieldDecorator('proInterface', {
                  initialValue: setting.proInterface || ''
                })(<TextArea rows={2}/>)}
              </Form.Item>
            </Col> : null}
            {interType === 'outer' ? <Col span={8}>
@@ -270,7 +287,7 @@
            </Col> : null}
            {interType === 'system' ? <Col span={24} className="data-source" style={{paddingLeft: '7px'}}>
              <Form.Item labelCol={{xs: { span: 24 }, sm: { span: 2 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 22 }} } label={
                <Tooltip placement="topLeft" title={'使用系统函数时,需填写数据源。注:数据权限替换符 $@ -> /* 或 \'\'、 @$ -> */ 或 \'\''}>
                <Tooltip placement="topLeft" title={`使用系统函数时,需填写数据源。注:数据权限替换符 $@ -> /* 或 ''、 @$ -> */ 或 '';查询替换符 $select@ -> /* 或 ''、 @select$ -> */ 或 '';统计替换符 $sum@ -> /* 或 ''、 @sum$ -> */ 或 ''。`}>
                  <Icon type="question-circle" />
                  数据源
                </Tooltip>
@@ -336,7 +353,7 @@
                </Radio.Group>)}
              </Form.Item>
            </Col> : null}
            <Col span={8}>
            {config.type !== 'navbar' && config.type !== 'balcony' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'该组件如果受其他组件控制,请选项相应的组件,没有时选“无”。'}>
                  <Icon type="question-circle" />
@@ -355,7 +372,7 @@
                  <Cascader options={modules} onChange={this.changeSupModule} expandTrigger="hover" placeholder="" />
                )}
              </Form.Item>
            </Col>
            </Col> : null}
            {config.pageable ? <Col span={8}>
              <Form.Item label="分页">
                {getFieldDecorator('laypage', {
@@ -387,7 +404,7 @@
              </Form.Item>
            </Col> : null}
            {/* 1、不分页且不存在上级模块 */}
            {(!config.pageable || (config.pageable && laypage === 'false')) && (!supModule || supModule.length === 0 || supModule[0] === 'empty') ? <Col span={8}>
            {config.type !== 'navbar' && (!config.pageable || (config.pageable && laypage === 'false')) && (!supModule || supModule.length === 0 || supModule[0] === 'empty') ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'初始化加载时,是否与其他组件一同加载数据,注:仅在使用系统函数,且初始化加载数据时有效,分页请求时无效。'}>
                  <Icon type="question-circle" />
@@ -404,7 +421,7 @@
                )}
              </Form.Item>
            </Col> : null}
            <Col span={8}>
            {config.type !== 'navbar' && config.type !== 'balcony' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'优先使用同级的搜索条件组件,同级搜索不存在时,依次向上选取,与当前组件的搜索条件一同用作数据过滤(当前组件的搜索条件优先)。'}>
                  <Icon type="question-circle" />
@@ -420,8 +437,8 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {useMSearch === 'true' ? <Col span={8}>
            </Col> : null}
            {config.type !== 'navbar' && config.type !== 'balcony' && useMSearch === 'true' ? <Col span={8}>
              <Form.Item label={
                <Tooltip placement="topLeft" title={'外层搜索条件改变时,是否刷新当前组件数据。'}>
                  <Icon type="question-circle" />
@@ -438,7 +455,7 @@
                )}
              </Form.Item>
            </Col> : null}
            <Col span={8}>
            {config.type !== 'navbar' && config.type !== 'balcony' ? <Col span={8}>
              <Form.Item label="初始化数据">
                {getFieldDecorator('onload', {
                  initialValue: setting.onload || 'true'
@@ -449,7 +466,7 @@
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            </Col> : null}
          </Row>
        </Form>
      </div>
src/menu/datasource/verifycard/settingform/index.scss
@@ -24,4 +24,12 @@
  .ant-radio-group {
    white-space: nowrap;
  }
  .outer-interface {
    .ant-form-item-label {
      width: 10.5%;
    }
    .ant-form-item-control-wrapper {
      width: 89.5%;
    }
  }
}
src/menu/datasource/verifycard/utils.jsx
@@ -7,7 +7,7 @@
   * @return {Object}  setting       页面设置
   * @return {Array}   columns       显示字段
   */
  static getDebugSql (setting, scripts, columns, searches = []) {
  static getDebugSql (setting, scripts, columns, searches = [], defSearch) {
    let sql = ''
    let error = ''
    let _dataresource = ''
@@ -26,8 +26,20 @@
      _dataresource = setting.dataresource || ''
    }
    
    if (window.GLOB.funcs && window.GLOB.funcs.length > 0) {
      window.GLOB.funcs.forEach(item => {
        let reg = new RegExp('\\$ex@' + item.func_code + '@ex\\$', 'ig')
        _dataresource = _dataresource.replace(reg, `/*$ex@${item.func_code}-begin*/\n${item.key_sql}\n/*@ex$-end*/`)
        _customScript = _customScript.replace(reg, `/*$ex@${item.func_code}-begin*/\n${item.key_sql}\n/*@ex$-end*/`)
      })
    }
    _dataresource = _dataresource.replace(/@\$|\$@/ig, '')
    _customScript = _customScript.replace(/@\$|\$@/ig, '')
    _dataresource = _dataresource.replace(/@select\$|\$select@/ig, '')
    _customScript = _customScript.replace(/@select\$|\$select@/ig, '')
    _dataresource = _dataresource.replace(/@sum\$|\$sum@/ig, '')
    _customScript = _customScript.replace(/@sum\$|\$sum@/ig, '')
    if (_customScript) {
      _customScript = `declare @ErrorCode nvarchar(50),@retmsg nvarchar(4000),@UserName nvarchar(50),@FullName nvarchar(50),@login_city nvarchar(50) select @ErrorCode='',@retmsg =''
@@ -48,8 +60,7 @@
        _customScript = _customScript.replace(reg, '0')
      })
    }
    // 正则替换
    let _regoptions = []
    let _fields = []
@@ -66,7 +77,7 @@
          reg: new RegExp('@' + item.datefield + '1@', 'ig')
        })
      }
      if (['dateweek', 'datemonth', 'daterange'].includes(item.type)) {
      if (['dateweek', 'datemonth', 'daterange', 'range'].includes(item.type)) {
        _regoptions.push({
          var: new RegExp('@' + item.field, 'ig'),
          reg: new RegExp('@' + item.field + '@', 'ig')
@@ -103,7 +114,7 @@
      }
    })
    let _search = ''
    let _search = defSearch
    if (setting.queryType === 'statistics' && _dataresource) {
      _regoptions.forEach(item => {
@@ -125,7 +136,7 @@
      }, {
        reg: new RegExp('@FullName@', 'ig'),
      })
      if (setting.laypage !== 'false') {
      if (setting.laypage === 'true') {
        _regoptions.push({
          reg: new RegExp('@pageSize@', 'ig'),
        }, {
@@ -140,6 +151,9 @@
      if (setting.varMark) {
        originscript = originscript.replace(/@ErrorCode/ig, '')
        originscript = originscript.replace(/@retmsg/ig, '')
        originscript = originscript.replace(/@UserName@/ig, '').replace(/@UserName/ig, '')
        originscript = originscript.replace(/@FullName@/ig, '').replace(/@FullName/ig, '')
        originscript = originscript.replace(/@login_city@/ig, '').replace(/@login_city/ig, '')
        originscript = originscript.replace(/@id@/ig, '').replace(/@id/ig, '')
        originscript = originscript.replace(/@bid@/ig, '').replace(/@bid/ig, '')
        originscript = originscript.replace(/@loginuid@/ig, '').replace(/@loginuid/ig, '')
@@ -162,7 +176,11 @@
        _dataresource = '(' + _dataresource + ') tb'
      }
      _dataresource = `select${setting.laypage !== 'false' ?  ' top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage !== 'false' ?  'where rows > 0' : ''} order by tmptable.rows`
      if (setting.order) {
        _dataresource = `select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from (select ${arr_field} ,ROW_NUMBER() over(order by ${setting.order}) as rows from ${_dataresource} ${_search}) tmptable ${setting.laypage === 'true' ?  'where rows > 0' : ''} order by tmptable.rows`
      } else {
        _dataresource = `select${setting.laypage === 'true' ?  ' top 10' : ''} ${arr_field} from ${_dataresource} ${_search}`
      }
    }
    if (_customScript) {
src/menu/menushell/card.jsx
@@ -7,9 +7,13 @@
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
const NormalTree = asyncComponent(() => import('@/menu/components/tree/antd-tree'))
const CarouselDataCard = asyncComponent(() => import('@/menu/components/carousel/data-card'))
const CarouselPropCard = asyncComponent(() => import('@/menu/components/carousel/prop-card'))
const TableCard = asyncComponent(() => import('@/menu/components/card/table-card'))
@@ -59,6 +63,12 @@
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'dashboard') {
      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tree') {
      return (<NormalTree card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'scatter') {
      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
@@ -81,6 +91,8 @@
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'balcony') {
      return (<Balcony card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  return (
src/menu/menushell/index.jsx
@@ -19,6 +19,10 @@
    handleList({...menu, components: _cards})
    setCards(_cards)
  }
  if (menu.components.length > cards.length) {
    setCards(menu.components)
  }
  
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
@@ -53,9 +57,12 @@
      content: hasComponent ? '当前组件中含有子组件!' : '',
      onOk() {
        const _cards = cards.filter(item => item.uuid !== card.uuid)
        MKEmitter.emit('delButtons', uuids)
        handleList({...menu, components: _cards})
        setCards(_cards)
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -93,6 +100,10 @@
        code: '自定义',
        carousel: '轮播',
        form: '表单',
        dashboard: '仪表盘',
        scatter: '散点图',
        tree: '树形列表',
        balcony: '浮动卡',
        card: '卡片'
      }
      let i = 1
src/menu/modalconfig/controller.jsx
@@ -48,12 +48,6 @@
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitModal', config, btn, modal)
    this.setState({
      visible: false,
      config: null,
      btn: null
    })
  }
  render () {
src/menu/modalconfig/index.jsx
@@ -12,7 +12,6 @@
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import ModalForm from '@/templates/zshare/modalform'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import asyncComponent from '@/utils/asyncComponent'
@@ -22,6 +21,7 @@
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const EditComponent = asyncComponent(() => import('@/templates/zshare/editcomponent'))
const DragElement = asyncComponent(() => import('@/templates/modalconfig/dragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
@@ -37,20 +37,11 @@
    dict: CommonDict,      // 字典
    config: null,          // 页面配置,包括模板类型、模态框设置、添加表名、表单列表
    visible: false,        // 表单编辑模态框,显示控制
    tableVisible: false,   // 数据表字段列表模态框,显示控制
    tableColumns: [],      // 表格字段名列表
    fields: null,          // 表单,可选字段(去重后)
    formlist: null,        // 表单编辑模态框,可编辑字段
    card: null,            // 编辑元素
    closeloading: false,   // 菜单保存中
    settingVisible: false, // 全局配置模态框
    closeVisible: false,   // 关闭模态框
    tables: [],            // 可用表名
    selectedTables: [],    // 已选表名
    originConfig: null,    // 原始菜单
    sources: null,         // 表单类型
    sqlVerifing: false,    // sql验证
    openEdition: '',       // 编辑版本标记,防止多人操作
    showField: false,      // 显示表单字段值
    standardform: null
  }
@@ -132,7 +123,7 @@
      if (card.uuid === item.uuid) {
        index = i
      }
      if (item.type !== 'select' && item.type !== 'link' && item.type !== 'radio') return
      if (!['select', 'link', 'radio', 'checkcard'].includes(item.type)) return
      if (item.field && !uniq.has(item.field)) {
        uniq.set(item.field, true)
@@ -227,7 +218,7 @@
      _config.fields = _config.fields.filter(item => !item.origin)
      if ((res.type === 'select' || res.type === 'multiselect' || res.type === 'link') && res.resourceType === '1' && /\s/.test(res.dataSource)) {
      if (['select', 'multiselect', 'link', 'checkbox', 'radio', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
@@ -297,15 +288,26 @@
  submitConfig = () => {
    const { config } = this.state
    this.setState({originConfig: fromJS(config).toJS()})
    this.props.handleSave(config)
    notification.success({
      top: 92,
      message: '保存成功。',
      duration: 2
    })
  }
  cancelConfig = () => {
    const { config, originConfig } = this.state
    if (!is(fromJS(config), fromJS(originConfig))) {
      this.setState({
        closeVisible: true
      let _this = this
      confirm({
        content: '配置信息未保存,确定返回吗?',
        onOk() {
          _this.props.handleBack()
        },
        onCancel() {}
      })
    } else {
      this.props.handleBack()
@@ -423,8 +425,8 @@
            <Card title={dict['header.menu.form.configurable']} bordered={false} extra={
              <div>
                <EditComponent dict={dict} options={['form']} config={this.state.config} refresh={(res) => this.updateConfig(res.config)}/>
                <Button type="primary" onClick={this.submitConfig}>{dict['model.confirm']}</Button>
                <Button onClick={this.cancelConfig}>{dict['model.cancel']}</Button>
                <Button type="primary" onClick={this.submitConfig}>保存</Button>
                <Button onClick={this.cancelConfig}>返回</Button>
              </div>
            } style={{ width: '100%' }}>
              <Icon type="setting" onClick={this.changeSetting} />
@@ -486,7 +488,7 @@
        <Modal
          title={this.state.dict['model.edit']}
          visible={this.state.settingVisible}
          width={700}
          width={850}
          maskClosable={false}
          onOk={this.settingSave}
          onCancel={() => { this.setState({ settingVisible: false }) }}
@@ -499,21 +501,6 @@
            inputSubmit={this.settingSave}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
        <Modal
          bodyStyle={{textAlign: 'center', color: '#000000', fontSize: '16px'}}
          closable={false}
          maskClosable={false}
          visible={this.state.closeVisible}
          onCancel={() => { this.setState({closeVisible: false}) }}
          footer={[
            <Button key="save" className="mk-btn mk-green" loading={this.state.closeloading} onClick={this.submitConfig}>{this.state.dict['model.save']}</Button>,
            <Button key="confirm" className="mk-btn mk-yellow" onClick={this.props.handleBack}>{this.state.dict['model.notsave']}</Button>,
            <Button key="cancel" onClick={() => { this.setState({closeVisible: false}) }}>{this.state.dict['model.cancel']}</Button>
          ]}
          destroyOnClose
        >
          {this.state.dict['header.menu.config.placeholder']}
        </Modal>
      </div>
    )
src/menu/modalconfig/index.scss
@@ -127,6 +127,20 @@
    .ant-card-body {
      position: relative;
      padding: 0;
      .modal-fields-row.up_down {
        .ant-form-item {
          display: block!important;
          .ant-form-item-label {
            width: 100%!important;
            text-align: left;
          }
          .ant-form-item-control-wrapper {
            width: 100%!important;
          }
        }
      }
      .ant-modal-content {
        max-width: 95%;
        margin: 0 auto;
@@ -292,6 +306,7 @@
  }
}
.modal-fields {
  .ant-modal {
    top: 50px;
src/menu/modulesource/index.jsx
@@ -55,6 +55,7 @@
            c_id: item.uuid,
            images: '',
            c_name: item.title,
            typename: sessionStorage.getItem('appType') || '',
            long_param: '',
            del_type: 'Y'
          }).then(result => {
src/menu/modulesource/option.jsx
@@ -12,11 +12,16 @@
import Editor from '@/assets/mobimg/editor.png'
import SandBox from '@/assets/mobimg/sandbox.png'
import Pie1 from '@/assets/mobimg/ring.png'
import Pie3 from '@/assets/mobimg/nest.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'
import dashboard from '@/assets/mobimg/dashboard.png'
import ratioboard from '@/assets/mobimg/ratioboard.png'
import scatter from '@/assets/mobimg/scatter.png'
import tree from '@/assets/mobimg/tree.png'
// 组件配置信息
export const menuOptions = [
@@ -24,19 +29,25 @@
  { 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: card2, component: 'balcony', subtype: 'balcony', 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: tree, component: 'tree', subtype: 'normaltree', 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: Pie3, component: 'pie', subtype: 'nest', title: '嵌套饼图', width: 12 },
  { type: 'menu', url: Pie2, component: 'pie', subtype: 'nightingale', title: '南丁格尔图', width: 12 },
  { type: 'menu', url: dashboard, component: 'dashboard', subtype: 'dashboard', title: '仪表盘', width: 12 },
  { type: 'menu', url: ratioboard, component: 'dashboard', subtype: 'ratioboard', title: '占比图', width: 12 },
  { type: 'menu', url: scatter, component: 'scatter', subtype: 'scatter', title: '散点图', width: 24 },
  { 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, forbid: ['billPrint'] },
]
src/menu/pastecontroller/index.jsx
@@ -60,8 +60,13 @@
        cell = this.resetconfig(cell, item, true, copyBtns)
        return cell
      })
    } else if (item.type === 'menubar') {
      item.subMenus = item.subMenus.map(cell => {
        cell.uuid = Utils.getuuid()
        return cell
      })
    } else if (item.type === 'card' || (item.type === 'table' && item.subtype === 'tablecard')) {
      item.subcards.forEach(card => {
      item.subcards && item.subcards.forEach(card => {
        card.uuid = Utils.getuuid()
        if (card.elements) {
          if (sessionStorage.getItem('editMenuType') === 'popview') {
@@ -149,12 +154,16 @@
      item.btnlog = []
    }
    let oriUids = {}
    if (item.action) {
      if (sessionStorage.getItem('editMenuType') === 'popview') {
        item.action = item.action.filter(c => c.OpenType !== 'popview' && c.OpenType !== 'funcbutton')
      }
      item.action = item.action.map(cell => {
        let _uuid = Utils.getuuid()
        oriUids[cell.uuid] = _uuid
        if (cell.OpenType === 'popview') {
          let _cell = fromJS(cell).toJS()
          _cell.$originUuid = _cell.uuid
@@ -177,6 +186,13 @@
        return cell
      })
    }
    if (item.setting && item.setting.supModule) {
      item.setting.supModule = ''
    }
    if (item.wrap && item.wrap.doubleClick) {
      item.wrap.doubleClick = oriUids[item.wrap.doubleClick] || ''
    }
    return item
  }
@@ -185,16 +201,20 @@
    const { Tab } = this.props
    let isgroup = Tab && Tab.type === 'group' ? true : false
    let options = ['tabs', 'datacard', 'propcard', 'mainsearch', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter']
    if (sessionStorage.getItem('appType') === 'mob') {
      options.push('menubar')
    }
    this.pasteFormRef.handleConfirm().then(res => {
      if (!isgroup && !['tabs', 'datacard', 'propcard', 'mainsearch', 'group', 'normaltable', 'tablecard', 'line', 'bar', 'pie'].includes(res.copyType)) {
      if (!isgroup && !options.includes(res.copyType)) {
        notification.warning({
          top: 92,
          message: '配置信息格式错误!',
          duration: 5
        })
        return
      } else if (isgroup && !['datacard', 'propcard', 'normaltable', 'tablecard', 'line', 'bar', 'pie'].includes(res.copyType)) {
      } else if (isgroup && !['datacard', 'propcard', 'normaltable', 'tablecard', 'line', 'bar', 'pie', 'dashboard', 'scatter'].includes(res.copyType)) {
        notification.warning({
          top: 92,
          message: '配置信息格式错误!',
src/menu/picturecontroller/editform/index.jsx
@@ -1,6 +1,6 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, notification } from 'antd'
import { Form, Row, Col, Input, Radio } from 'antd'
import FileUpload from '@/tabviews/zshare/fileupload'
import './index.scss'
@@ -14,7 +14,6 @@
  }
  state = {
    urls: [],
    linkurl: '',
    plusType: 'upload'
  }
@@ -24,29 +23,8 @@
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          if (values.urls && values.urls[0].status === 'error') {
            notification.warning({
              top: 92,
              message: '请重新上传文件!',
              duration: 5
            })
            return
          } 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
          if (values.urls) {
            values.linkurl = values.urls
          }
          resolve(values)
        } else {
@@ -57,28 +35,19 @@
  }
  changeType = (val) => {
    const { linkurl, urls } = this.state
    let _urls = this.props.form.getFieldValue('urls') || ''
    let _url = this.props.form.getFieldValue('linkurl') || ''
    let _url = ''
    if (val === 'input') {
      if (_urls && _urls[0] && _urls[0].status === 'done' && (_urls[0].url || _urls[0].response)) {
        _url = _urls[0].url || _urls[0].response
      } else {
        _url = linkurl || ''
      }
    } else {
      _urls = urls.filter(item => item.status === 'done')
      _url = linkurl
      _url = this.props.form.getFieldValue('urls') || ''
    }
    
    this.setState({plusType: val, urls: _urls, linkurl: _url})
    this.setState({plusType: val, linkurl: _url})
  }
  render() {
    const { getFieldDecorator } = this.props.form
    const { card } = this.props
    const { urls, linkurl, plusType } = this.state
    const { linkurl, plusType } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
@@ -103,7 +72,7 @@
          {!card.id && card.typecharone === 'image' && plusType === 'upload' ? <Col span={24}>
            <Form.Item label="图片上传">
              {getFieldDecorator('urls', {
                initialValue: urls,
                initialValue: '',
                rules: [
                  {
                    required: true,
@@ -111,14 +80,19 @@
                  }
                ]
              })(
                <FileUpload accept=".jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp" maxFile={1} fileType={'picture'} />
                <FileUpload config={{
                  initval: '',
                  suffix: '.jpg,.png,.gif,.pjp,.pjpeg,.jpeg,.jfif,.webp',
                  maxfile: 1,
                  fileType: 'picture'
                }} />
              )}
            </Form.Item>
          </Col> : null}
          {!card.id && card.typecharone === 'video' && plusType === 'upload' ? <Col span={24}>
            <Form.Item label="视频上传">
              {getFieldDecorator('urls', {
                initialValue: urls,
                initialValue: '',
                rules: [
                  {
                    required: true,
@@ -126,7 +100,12 @@
                  }
                ]
              })(
                <FileUpload accept=".mp4,.webm,.ogg" maxFile={1} fileType={'text'} />
                <FileUpload config={{
                  initval: '',
                  suffix: '.mp4,.webm,.ogg',
                  maxfile: 1,
                  fileType: 'text'
                }}/>
              )}
            </Form.Item>
          </Col> : null}
src/menu/popview/index.jsx
@@ -19,8 +19,10 @@
const { confirm } = Modal
const MenuForm = asyncComponent(() => import('./menuform'))
const Header = asyncComponent(() => import('@/menu/header'))
const SourceWrap = asyncComponent(() => import('@/menu/modulesource'))
const MenuShell = asyncComponent(() => import('@/menu/menushell'))
const ReplaceField = asyncComponent(() => import('@/menu/replaceField'))
const BgController = asyncComponent(() => import('@/menu/bgcontroller'))
const PasteController = asyncComponent(() => import('@/menu/pastecontroller'))
const PaddingController = asyncComponent(() => import('@/menu/padcontroller'))
@@ -84,6 +86,7 @@
  updateCustomComponent = () => {
    Api.getSystemConfig({
      func: 's_get_custom_components',
      typename: sessionStorage.getItem('appType'),
      typecharone: ''
    }).then(res => {
      let coms = []
@@ -245,7 +248,7 @@
            buttons.push(`select '${btn.uuid}' as menuid, '${item.name + '-' + btn.label}' as menuname, '${_sort * 10}' as Sort`)
            _sort++
          })
          item.subcards.forEach(card => {
          item.subcards && item.subcards.forEach(card => {
            card.elements && card.elements.forEach(cell => {
              if (cell.eleType !== 'button') return
              this.checkBtn(cell)
@@ -527,6 +530,18 @@
    window.GLOB.customMenu = config
  }
  resetConfig = (config) => {
    this.setState({
      config: {...config, components: []},
    }, () => {
      this.setState({
        config: config
      })
    })
    window.GLOB.customMenu = config
  }
  /**
   * @description 更新常用表信息,快捷添加后更新配置信息
   */
@@ -550,49 +565,53 @@
    const { activeKey, MenuType, dict, config, menuloading, customComponents } = this.state
    return (
      <DndProvider backend={HTML5Backend}>
        <div className="menu-body">
          <div className="menu-setting">
            <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
              {/* 基本信息 */}
              <Panel header={dict['mob.basemsg']} key="basedata">
                {/* 菜单信息 */}
                {config ? <MenuForm dict={dict} config={config} btn={btn} updateConfig={this.updateConfig}/> : null}
                {/* 表名添加 */}
                {config ? <TableComponent config={config} updatetable={this.updatetable}/> : null}
              </Panel>
              {/* 组件添加 */}
              <Panel header={dict['mob.component']} key="component">
                <SourceWrap MenuType={MenuType} />
              </Panel>
              {customComponents && customComponents.length ? <Panel header="自定义组件" key="cuscomponent">
                <SourceWrap components={customComponents} MenuType={MenuType} />
              </Panel> : null}
              <Panel header={'页面背景'} key="background">
                {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
              </Panel>
              <Panel header={'页面内边距'} key="padding">
                {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
              </Panel>
            </Collapse>
      <div className="pc-poper-view">
        <Header />
        <DndProvider backend={HTML5Backend}>
          <div className="menu-body">
            <div className="menu-setting">
              <Collapse accordion activeKey={activeKey} bordered={false} onChange={(key) => this.setState({activeKey: key})}>
                {/* 基本信息 */}
                <Panel header={dict['mob.basemsg']} key="basedata">
                  {/* 菜单信息 */}
                  {config ? <MenuForm dict={dict} config={config} btn={btn} updateConfig={this.updateConfig}/> : null}
                  {/* 表名添加 */}
                  {config ? <TableComponent config={config} updatetable={this.updatetable}/> : null}
                </Panel>
                {/* 组件添加 */}
                <Panel header={dict['mob.component']} key="component">
                  <SourceWrap MenuType={MenuType} />
                </Panel>
                {customComponents && customComponents.length ? <Panel header="自定义组件" key="cuscomponent">
                  <SourceWrap components={customComponents} MenuType={MenuType} />
                </Panel> : null}
                <Panel header={'页面背景'} key="background">
                  {config ? <BgController config={config} updateConfig={this.updateConfig} /> : null}
                </Panel>
                <Panel header={'页面内边距'} key="padding">
                  {config ? <PaddingController config={config} updateConfig={this.updateConfig} /> : null}
                </Panel>
              </Collapse>
            </div>
            <div className={'menu-view ' + (menuloading ? 'saving' : '')}>
              <Card title={
                <div> {config && config.MenuName} </div>
              } bordered={false} extra={
                <div>
                  <ReplaceField type="custom" config={config} updateConfig={this.resetConfig}/>
                  <StyleCombControlButton menu={config} />
                  <PasteController type="menu" Tab={null} insert={this.insert} />
                  {config ? <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config.enabled} onChange={this.onEnabledChange} /> : null}
                  <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
                  <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
                </div>
              } style={{ width: '100%' }}>
                {config && config.components ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
              </Card>
            </div>
          </div>
          <div className={'menu-view ' + (menuloading ? 'saving' : '')}>
            <Card title={
              <div> {config && config.MenuName} </div>
            } bordered={false} extra={
              <div>
                <StyleCombControlButton menu={config} />
                <PasteController type="menu" Tab={null} insert={this.insert} />
                {config ? <Switch className="big" checkedChildren={dict['mob.enable']} unCheckedChildren={dict['mob.disable']} checked={config.enabled} onChange={this.onEnabledChange} /> : null}
                <Button type="primary" onClick={this.submitConfig} loading={menuloading}>{dict['mob.save']}</Button>
                <Button type="default" onClick={this.closeView}>{dict['mob.return']}</Button>
              </div>
            } style={{ width: '100%' }}>
              {config && config.components ? <MenuShell menu={config} handleList={this.updateConfig} /> : null}
            </Card>
          </div>
        </div>
      </DndProvider>
        </DndProvider>
      </div>
    )
  }
}
src/menu/popview/index.scss
@@ -0,0 +1,162 @@
.pc-poper-view {
  background: #000;
  min-height: 100vh;
  >.menu-body {
    width: 100vw;
    height: 100vh;
    overflow-x: hidden;
    position: relative;
    background: #ffffff;
    padding: 50px 0px 0px 0px;
    .menu-setting {
      position: fixed;
      left: 0;
      top: 48px;
      z-index: 10;
      height: calc(100vh - 48px);
      width: 300px;
      background: #ffffff;
      box-shadow: 0px 2px 5px #bcbcbc;
      overflow-y: auto;
      overflow-x: hidden;
      > .ant-collapse {
        background-color: #ffffff;
        .ant-collapse-item.ant-collapse-item-active {
          border-bottom: 1px solid #d9d9d9;
        }
        .ant-collapse-header {
          padding: 11px 16px 10px 40px;
          border-bottom: 1px solid #d9d9d9;
          background: #1890ff;
          color: #ffffff;
        }
        .ant-collapse-content-box {
          .ant-form-item {
            margin-bottom: 10px;
          }
          .model-table-tablemanage-view {
            >.ant-list {
              margin-top: 20px;
              .ant-list-item {
                display: -webkit-box;
                padding-right: 20px;
                position: relative;
                padding-left: 5px;
                overflow: hidden;
                text-overflow: ellipsis;
                -webkit-line-clamp: 2;
                -webkit-box-orient: vertical;
                min-height: 55px;
                width: 100%;
                .anticon {
                  position: absolute;
                  top: 0px;
                  right: 0px;
                  padding: 3px 3px 10px 10px;
                  cursor: pointer;
                }
              }
            }
            >.tables {
              width: 66.66666667%!important;
            }
            >.ant-form-item-label {
              width: 33.33333333%;
            }
          }
        }
      }
      >.ant-tabs {
        >.ant-tabs-bar {
          border-bottom: 1px solid #181F29;
          margin-bottom: 0px;
          min-height: 48px;
          .ant-tabs-tab {
            padding: 14px 16px;
            color: rgba(255, 255, 255, 0.85);
          }
          .ant-tabs-tab-active.ant-tabs-tab {
            color: #1890ff;
          }
        }
      }
    }
    .menu-setting::-webkit-scrollbar {
      width: 4px;
    }
    .menu-setting::-webkit-scrollbar-thumb {
      border-radius: 5px;
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
      background: rgba(0, 0, 0, 0.08);
    }
    .menu-setting::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
      border-radius: 3px;
      border: 1px solid rgba(0, 0, 0, 0.07);
      background: rgba(0, 0, 0, 0);
    }
    .menu-view {
      position: relative;
      width: calc(100vw - 300px);
      margin-left: 300px;
      height: calc(100vh - 50px);
      overflow-y: auto;
      > .ant-card {
        >.ant-card-head {
          margin-bottom: 0px;
          position: relative;
          .ant-card-head-title {
            color: #1890ff;
            padding: 5px 0;
          }
          .ant-card-extra {
            padding: 5px 0;
            button {
              margin-left: 20px;
            }
            .ant-switch.big {
              min-width: 60px;
              height: 28px;
              line-height: 28px;
              margin-top: -2px;
              .ant-switch-inner {
                font-size: 14px;
              }
            }
            .ant-switch.big:after {
              width: 24px;
              height: 24px;
            }
          }
        }
        >.ant-card-body {
          padding: 0px;
        }
      }
    }
    .menu-view.saving {
      .anticon-tool {
        display: none;
      }
    }
    .menu-view::-webkit-scrollbar {
      width: 7px;
    }
    .menu-view::-webkit-scrollbar-thumb {
      border-radius: 5px;
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.08);
      background: rgba(0, 0, 0, 0.08);
    }
    .menu-view::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
      border-radius: 3px;
      border: 1px solid rgba(0, 0, 0, 0.07);
      background: rgba(0, 0, 0, 0);
    }
  }
}
src/menu/replaceField/index.jsx
New file
@@ -0,0 +1,381 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Button, notification } from 'antd'
import moment from 'moment'
import Api from '@/api'
import options from '@/store/options.js'
import Utils from '@/utils/utils.js'
import SettingForm from './settingform'
import { queryTableSql } from '@/utils/option.js'
import './index.scss'
class ReplaceField extends Component {
  static propTpyes = {
    type: PropTypes.string,
    config: PropTypes.object,
    updateConfig: PropTypes.func
  }
  state = {
    visible: false,
    loadingTable: false,
    confirming: false,
    tables: [],
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  trigger = () => {
    const { tables } = this.state
    if (tables.length === 0) {
      let param = {
        func: 'sPC_Get_SelectedList',
        LText: queryTableSql,
        obj_name: 'data',
        arr_field: 'TbName,Remark'
      }
      param.LText = Utils.formatOptions(param.LText)
      param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
      param.secretkey = Utils.encrypt(param.LText, param.timestamp)
      param.open_key = Utils.encryptOpenKey(param.secretkey, param.timestamp) // 云端数据验证
      if (options.cloudServiceApi) { // 且存在云端地址
        param.rduri = options.cloudServiceApi
        param.userid = sessionStorage.getItem('CloudUserID') || ''
        param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
      }
      this.setState({
        loadingTable: true
      })
      Api.getSystemCacheConfig(param).then(res => {
        if (res.status) {
          this.setState({
            visible: true,
            confirming: false,
            tables: res.data,
            loadingTable: false
          })
        } else {
          this.setState({
            confirming: false,
            loadingTable: false
          })
          notification.warning({
            top: 92,
            message: res.message,
            duration: 5
          })
        }
      })
    } else {
      this.setState({
        confirming: false,
        visible: true
      })
    }
  }
  submit = () => {
    let config = fromJS(this.props.config).toJS()
    this.settingRef.handleConfirm().then(res => {
      this.setState({confirming: true})
      let param = {func: 'sPC_Get_FieldName', TBName: res.table}
      if (options.cloudServiceApi) { // 且存在云端地址
        param.rduri = options.cloudServiceApi
        param.userid = sessionStorage.getItem('CloudUserID') || ''
        param.LoginUID = sessionStorage.getItem('CloudLoginUID') || ''
      }
      Api.getSystemCacheConfig(param).then(result => {
        if (!result.status) {
          this.setState({
            confirming: false
          })
          notification.warning({
            top: 92,
            message: result.message,
            duration: 5
          })
          return
        }
        let map = {}
        result.FDName.forEach(item => {
          if (/NVARCHAR|INT|Decimal/ig.test(item.FieldType)) {
            item.datatype = item.FieldType
          }
          map[item.FieldDec] = item
        })
        if (this.props.type === 'custom') {
          let _replace = (components) => {
            return components.map(item => {
              if (item.type === 'tabs') {
                item.subtabs.forEach(tab => {
                  tab.components = _replace(tab.components)
                })
                return item
              } else if (item.type === 'group') {
                item.components = _replace(item.components)
                return item
              }
              if (item.columns) {
                item.columns = item.columns.map(col => {
                  if (map[col.field]) {
                    col.field = map[col.field].FieldName
                    if (map[col.field].datatype) {
                      col.datatype = map[col.field].datatype
                    }
                  }
                  return col
                })
              }
              if (item.search) {
                item.search = item.search.map(col => {
                  if (map[col.field]) {
                    col.field = map[col.field].FieldName
                  }
                  return col
                })
              }
              if (item.action) {
                item.action.forEach(m => {
                  if (m.modal && m.modal.fields) {
                    m.modal.fields = m.modal.fields.map(col => {
                      if (map[col.field]) {
                        col.field = map[col.field].FieldName
                      }
                      return col
                    })
                  }
                })
              }
              if (item.subcards) {
                item.subcards.forEach(card => {
                  if (card.elements) { // 卡片
                    card.elements = card.elements.map(m => {
                      if (m.datatype === 'dynamic' && map[m.field]) {
                        m.field = map[m.field].FieldName
                      }
                      if (m.modal && m.modal.fields) {
                        m.modal.fields = m.modal.fields.map(col => {
                          if (map[col.field]) {
                            col.field = map[col.field].FieldName
                          }
                          return col
                        })
                      }
                      return m
                    })
                  }
                  if (card.backElements) { // 卡片
                    card.backElements = card.backElements.map(m => {
                      if (m.datatype === 'dynamic' && map[m.field]) {
                        m.field = map[m.field].FieldName
                      }
                      if (m.modal && m.modal.fields) {
                        m.modal.fields = m.modal.fields.map(col => {
                          if (map[col.field]) {
                            col.field = map[col.field].FieldName
                          }
                          return col
                        })
                      }
                      return m
                    })
                  }
                  if (card.fields) { // 表单
                    card.fields = card.fields.map(m => {
                      if (map[m.field]) {
                        m.field = map[m.field].FieldName
                      }
                      return m
                    })
                  }
                })
              }
              if (item.elements) {
                item.elements = item.elements.map(m => {
                  if (m.datatype === 'dynamic' && map[m.field]) {
                    m.field = map[m.field].FieldName
                  }
                  if (m.modal && m.modal.fields) {
                    m.modal.fields = m.modal.fields.map(col => {
                      if (map[col.field]) {
                        col.field = map[col.field].FieldName
                      }
                      return col
                    })
                  }
                  return m
                })
              }
              if (item.plot) {
                if (item.plot.Xaxis && map[item.plot.Xaxis]) {
                  item.plot.Xaxis = map[item.plot.Xaxis].FieldName
                }
                // 统计图
                if (item.plot.InfoValue && map[item.plot.InfoValue]) {
                  item.plot.InfoValue = map[item.plot.InfoValue].FieldName
                }
                if (item.plot.InfoType && map[item.plot.InfoType]) {
                  item.plot.InfoType = map[item.plot.InfoType].FieldName
                }
                // 占比图
                if (item.plot.valueField && map[item.plot.valueField]) {
                  item.plot.valueField = map[item.plot.valueField].FieldName
                }
                if (item.plot.labelField && map[item.plot.labelField]) {
                  item.plot.labelField = map[item.plot.labelField].FieldName
                }
                // 饼图
                if (item.plot.type && map[item.plot.type]) {
                  item.plot.type = map[item.plot.type].FieldName
                }
                // 散点图
                if (item.plot.gender && map[item.plot.gender]) {
                  item.plot.gender = map[item.plot.gender].FieldName
                }
                if (item.Yaxis) {
                  if (Array.isArray(item.Yaxis)) {
                    item.Yaxis = item.Yaxis.map(m => {
                      if (map[m]) {
                        return map[m].FieldName
                      }
                      return m
                    })
                  } else {
                    if (map[item.Yaxis]) {
                      item.Yaxis = map[item.Yaxis].FieldName
                    }
                  }
                }
              }
              if (item.cols) {
                let _update = (cols) => {
                  return cols.map(col => {
                    if (col.type === 'action' && col.elements) {
                      col.elements = col.elements.map(m => {
                        if (m.modal && m.modal.fields) {
                          m.modal.fields = m.modal.fields.map(col => {
                            if (map[col.field]) {
                              col.field = map[col.field].FieldName
                            }
                            return col
                          })
                        }
                        return m
                      })
                    } else if (col.type === 'custom' && col.elements) {
                      col.elements = col.elements.map(m => {
                        if (m.datatype === 'dynamic' && map[m.field]) {
                          m.field = map[m.field].FieldName
                        }
                        return m
                      })
                    } else if (col.type === 'colspan') {
                      col.subcols = _update(col.subcols)
                    } else if (col.field) {
                      if (map[col.field]) {
                        col.field = map[col.field].FieldName
                      }
                    }
                    return col
                  })
                }
                item.cols = _update(item.cols)
              }
              return item
            })
          }
          config.components = _replace(config.components)
        } else if (this.props.type === 'table') {
          config.columns = config.columns.map(col => {
            if (col.field && map[col.field]) {
              col.field = map[col.field].FieldName
            }
            return col
          })
          config.search = config.search.map(col => {
            if (col.field && map[col.field]) {
              col.field = map[col.field].FieldName
            }
            if (col.datefield && map[col.datefield]) {
              col.datefield = map[col.datefield].FieldName
            }
            return col
          })
        } else if (this.props.type === 'form') {
          config.fields = config.fields.map(col => {
            if (col.field && map[col.field]) {
              col.field = map[col.field].FieldName
            }
            return col
          })
        }
        this.setState({
          confirming: false,
          visible: false
        })
        notification.success({
          top: 92,
          message: '更新已完成。',
          duration: 3
        })
        this.props.updateConfig(config)
      })
    })
  }
  render() {
    const { visible, loadingTable, tables, confirming } = this.state
    return (
      <div style={{display: 'inline-block'}}>
        <Button className="mk-border-yellow" icon="swap" loading={loadingTable} onClick={this.trigger}>字段替换</Button>
        <Modal
          title="字段替换"
          wrapClassName="replace-field-modal"
          visible={visible}
          width={600}
          maskClosable={false}
          onOk={this.submit}
          onCancel={() => { this.setState({ visible: false })}}
          confirmLoading={confirming}
          destroyOnClose
        >
          <SettingForm tables={tables} wrappedComponentRef={(inst) => this.settingRef = inst}/>
        </Modal>
      </div>
    )
  }
}
export default ReplaceField
src/menu/replaceField/index.scss
New file
@@ -0,0 +1,9 @@
.replace-field-modal {
  .ant-modal {
    top: 70px;
  }
  .ant-modal-body {
    min-height: 150px;
    padding-top: 40px;
  }
}
src/menu/replaceField/settingform/index.jsx
New file
@@ -0,0 +1,78 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Tooltip, Icon, Select } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    tables: PropTypes.object
  }
  state = {}
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  render() {
    const { tables } = this.props
    const { getFieldDecorator } = this.props.form
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout}>
        <Row gutter={24}>
          <Col span={20}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="用于字段替换的表名。">
                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}} />
                表名
              </Tooltip>
            }>
              {getFieldDecorator('table', {
                initialValue: '',
                rules: [
                  {
                    required: true,
                    message: '请选择表名'
                  }
                ]
              })(
                <Select
                  showSearch
                  filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                >
                  {tables.map((option, i) =>
                    <Select.Option key={i} title={option.Remark + ' (' + option.TbName + ')'} value={option.TbName}>{option.Remark + ' (' + option.TbName + ')'}</Select.Option>
                  )}
                </Select>
              )}
            </Form.Item>
          </Col>
        </Row>
      </Form>
    )
  }
}
export default Form.create()(SettingForm)
src/menu/replaceField/settingform/index.scss
copy from src/tabviews/zshare/mutilform/customSwitch/index.scss copy to src/menu/replaceField/settingform/index.scss
src/menu/stylecombcontrolbutton/index.jsx
@@ -12,7 +12,7 @@
  }
  state = {
    label: '调整',
    label: '批量调整',
    parent: null,
    type: '',
    components: []
@@ -127,7 +127,7 @@
  triggerStyleChange = () => {
    const { label, components } = this.state
    if (label === '调整') {
    if (label === '批量调整') {
      document.body.className = 'style-control'
      sessionStorage.setItem('style-control', 'true')
      this.setState({label: '退出'})
@@ -141,14 +141,14 @@
      MKEmitter.emit('closeCombineStyle')
      this.setState({label: '调整', parent: null, components: []})
      this.setState({label: '批量调整', parent: null, components: []})
    }
  }
  
  render() {
    const { label } = this.state
    return (
      <Button className="style-control-button" icon="font-colors" title="调整样式" onClick={this.triggerStyleChange}>{label}</Button>
      <Button className="style-control-button" icon="font-colors" title="批量调整样式" onClick={this.triggerStyleChange}>{label}</Button>
    )
  }
}
src/menu/stylecombcontrolbutton/index.scss
@@ -15,11 +15,19 @@
    bottom: 0;
    right: 0;
    z-index: 12;
    background:rgba(0, 0, 0, 0.3);
    background:rgba(0, 0, 0, 0.2);
  }
  .menu-body .menu-view >.ant-card >.ant-card-body {
    position: relative;
    z-index: 13;
    background:#ffffff;
  }
  .menu-view {
    .anticon-tool {
      display: none;
    }
  }
  .mk-mob-view .mob-shell {
    .anticon-tool {
      display: none;
    }
src/menu/stylecontroller/index.jsx
@@ -194,6 +194,27 @@
  }
  /**
   * @description 修改背景大小
   */
  changeBackgroundSize = (val) => {
    this.updateStyle({backgroundSize: val})
  }
  /**
   * @description 修改背景位置
   */
  changeBackgroundPositon= (val) => {
    this.updateStyle({backgroundPositon: val})
  }
  /**
   * @description 修改背景重复设置
   */
  changeBackgroundRepeat = (val) => {
    this.updateStyle({backgroundRepeat: val})
  }
  /**
   * @description 修改阴影颜色 ,颜色控件
   */
  changeShadowColor = (val) => {
@@ -329,7 +350,7 @@
                    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}/>
                    <StyleInput defaultValue={card.width || ''} options={['px', 'vh', 'vw', '%']} onChange={this.changeWidth}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
@@ -448,6 +469,48 @@
                    <SourceComponent value={backgroundImage} type="" placement="right" onChange={this.imgChange}/>
                  </Form.Item>
                </Col> : null}
                {!options.includes('backgroundColor') ? <Col span={24}>
                  <Form.Item
                    colon={false}
                    label="比例"
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Select defaultValue={card.backgroundSize || 'cover'} onChange={this.changeBackgroundSize}>
                      <Option value="100%">100%</Option>
                      <Option value="100% 100%">100% 100%</Option>
                      <Option value="auto 100%">auto 100%</Option>
                      <Option value="contain">contain</Option>
                      <Option value="cover">cover</Option>
                    </Select>
                  </Form.Item>
                </Col> : null}
                {!options.includes('backgroundColor') ? <Col span={24}>
                  <Form.Item
                    colon={false}
                    label="重复"
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Select defaultValue={card.backgroundRepeat || 'no-repeat'} onChange={this.changeBackgroundRepeat}>
                      <Option value="repeat">repeat</Option>
                      <Option value="no-repeat">no-repeat</Option>
                      <Option value="repeat-x">repeat-x</Option>
                      <Option value="repeat-y">repeat-y</Option>
                    </Select>
                  </Form.Item>
                </Col> : null}
                {!options.includes('backgroundColor') ? <Col span={24}>
                  <Form.Item
                    colon={false}
                    label="位置"
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Select defaultValue={card.backgroundPositon || 'center'} onChange={this.changeBackgroundPositon}>
                      <Option value="center">center</Option>
                      <Option value="top">top</Option>
                      <Option value="bottom">bottom</Option>
                    </Select>
                  </Form.Item>
                </Col> : null}
              </Panel> : null}
              {options.includes('border') ? <Panel header="边框" key="border">
                <Col span={24}>
@@ -557,7 +620,7 @@
                    label={<Icon title="上边距" type="arrow-up"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.marginTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginTop')}/>
                    <StyleInput defaultValue={card.marginTop || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'marginTop')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -566,7 +629,7 @@
                    label={<Icon title="下边距" type="arrow-down"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.marginBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginBottom')}/>
                    <StyleInput defaultValue={card.marginBottom || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'marginBottom')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -575,7 +638,7 @@
                    label={<Icon title="左边距" type="arrow-left"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.marginLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginLeft')}/>
                    <StyleInput defaultValue={card.marginLeft || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'marginLeft')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -584,7 +647,7 @@
                    label={<Icon title="右边距" type="arrow-right"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.marginRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'marginRight')}/>
                    <StyleInput defaultValue={card.marginRight || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'marginRight')}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
@@ -595,7 +658,7 @@
                    label={<Icon title="上边距" type="arrow-up"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.paddingTop || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingTop')}/>
                    <StyleInput defaultValue={card.paddingTop || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'paddingTop')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -604,7 +667,7 @@
                    label={<Icon title="下边距" type="arrow-down"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.paddingBottom || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingBottom')}/>
                    <StyleInput defaultValue={card.paddingBottom || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'paddingBottom')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -613,7 +676,7 @@
                    label={<Icon title="左边距" type="arrow-left"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.paddingLeft || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingLeft')}/>
                    <StyleInput defaultValue={card.paddingLeft || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'paddingLeft')}/>
                  </Form.Item>
                </Col>
                <Col span={24}>
@@ -622,7 +685,7 @@
                    label={<Icon title="右边距" type="arrow-right"/>}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <StyleInput defaultValue={card.paddingRight || '0px'} options={['px', 'vh', 'vw']} onChange={(val) => this.changeNormalStyle(val, 'paddingRight')}/>
                    <StyleInput defaultValue={card.paddingRight || '0px'} options={['px', 'vh', 'vw', '%']} onChange={(val) => this.changeNormalStyle(val, 'paddingRight')}/>
                  </Form.Item>
                </Col>
              </Panel> : null}
@@ -633,7 +696,7 @@
                    label={<Icon title="浮动" type="swap" />}
                    labelCol={{xs: { span: 24 }, sm: { span: 4 }}} wrapperCol={ {xs: { span: 24 }, sm: { span: 20 }} }
                  >
                    <Radio.Group defaultValue={card.float || 'left'} onChange={(e) => this.changeNormalStyle(e.target.value, 'float')}>
                    <Radio.Group style={{whiteSpace: 'nowrap'}} defaultValue={card.float || 'left'} onChange={(e) => this.changeNormalStyle(e.target.value, 'float')}>
                      <Radio value="left">左浮动</Radio>
                      <Radio value="right">右浮动</Radio>
                      <Radio value="none">不浮动</Radio>
src/menu/sysinterface/settingform/baseform/index.jsx
@@ -242,8 +242,8 @@
                      message: dict['form.required.input'] + '回调表名!'
                    },
                    {
                      max: formRule.input.max,
                      message: formRule.input.message
                      max: 50,
                      message: '表名最长为50个字符!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" />)}
src/mob/colorsketch/index.jsx
@@ -2,7 +2,7 @@
import PropTypes from 'prop-types'
// import { is, fromJS } from 'immutable'
import { SketchPicker } from 'react-color'
import { Popover } from 'antd'
import { Popover, Icon } from 'antd'
import './index.scss'
@@ -20,10 +20,11 @@
  }
  state = {
    color: '',
    allowClear: false
  }
  UNSAFE_componentWillMount () {
    const { defaultValue, value } = this.props
    const { defaultValue, value, allowClear } = this.props
    let initVal = ''
    if (this.props['data-__meta']) {
@@ -34,7 +35,7 @@
      initVal = value
    }
    
    this.setState({color: initVal})
    this.setState({color: initVal, allowClear: allowClear === true})
  }
  handleChange = (color) => {
@@ -45,6 +46,12 @@
    })
  }
  clear = () => {
    this.setState({ color: '' }, () => {
      this.props.onChange && this.props.onChange('')
    })
  }
  UNSAFE_componentWillReceiveProps(nextProps) {
    if (nextProps.value !== undefined && nextProps.value !== this.state.color) {
      this.setState({ color: nextProps.value })
@@ -52,7 +59,7 @@
  }
  render() {
    const { color } = this.state
    const { color, allowClear } = this.state
    return (
      <div className="color-sketch-block">
        <Popover content={
@@ -62,7 +69,7 @@
            <div className="color-sketch-block-inner" style={ {background: color} }></div>
          </div>
        </Popover>
        <div className="color-sketch-value">{color}</div>
        <div className="color-sketch-value">{color}{allowClear && color ? <Icon onClick={this.clear} type="close"/> : null}</div>
      </div>
    )
  }
src/mob/colorsketch/index.scss
@@ -18,6 +18,7 @@
    height: 100%;
  }
  .color-sketch-value {
    position: relative;
    display: inline-block;
    font-size: 13px;
    width: 160px;
@@ -27,6 +28,23 @@
    vertical-align: top;
    white-space: nowrap;
    overflow: visible;
    .anticon-close {
      position: relative;
      right: -10px;
      font-size: 10px;
      padding: 3px;
      background: #eeeeee;
      border-radius: 50%;
      cursor: pointer;
      opacity: 0;
      transition: opacity 0.3s;
    }
  }
}
.color-sketch-block:hover {
  .anticon-close {
    opacity: 1;
  }
}
src/mob/components/formdragelement/card.jsx
New file
@@ -0,0 +1,200 @@
import React from 'react'
import { useDrag, useDrop } from 'react-dnd'
import { Icon, Button, Popover, Switch, Checkbox, Form } from 'antd'
import moment from 'moment'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const CheckCard = asyncComponent(() => import('@/templates/modalconfig/checkCard'))
const Card = ({ id, card, moveCard, findCard, editCard, closeCard, copyCard, showField }) => {
  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 copy = () => {
    copyCard(id)
  }
  let selectval = ''
  if (card.type === 'select' || card.type === 'link') {
    if (card.initval) {
      let _option = card.options.filter(option => option.Value === card.initval)[0]
      if (_option) {
        selectval = _option.Text || ''
      } else {
        selectval = ''
      }
    } else if (card.setAll === 'true') {
      selectval = card.emptyText || '空'
    }
  }
  let formItem = null
  if (card.type === 'text' || card.type === 'number') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className={'am-input-control ' + card.cursor}>{card.initval}</div>{card.scan && card.scan !== 'false' ? <div className="am-list-extra"><Icon type="scan" /></div> : null}</div></div>)
  } else if (card.type === 'number') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className={'am-input-control ' + card.cursor}>{card.initval}</div></div></div>)
  } else if (card.type === 'select' || card.type === 'link') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-input-control">{selectval || '请选择'}</div><div className="am-list-extra"><Icon type="right" /></div></div></div>)
  } else if (card.type === 'date') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-input-control">{card.initval ? moment().subtract(card.initval, 'days').format('YYYY-MM-DD') : '请选择'}</div><div className="am-list-extra"><Icon type="right" /></div></div></div>)
  } else if (card.type === 'datemonth') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-input-control">{card.initval ? moment().subtract(card.initval, 'month').format('YYYY-MM') : '请选择'}</div><div className="am-list-extra"><Icon type="right" /></div></div></div>)
  } else if (card.type === 'datetime') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-input-control">{card.initval ? moment().subtract(card.initval, 'days').format('YYYY-MM-DD HH:mm') : '请选择'}</div><div className="am-list-extra"><Icon type="right" /></div></div></div>)
  } else if (card.type === 'textarea') {
    let height = (card.maxRows || 2) * 25
    formItem = (<div className="am-list-item check-card">
      <div className="am-list-line">
        <div className="am-input-label">{card.label}</div>
        <div className="am-input-control">
          <div style={{textAlign: 'left', position: 'relative', height, lineHeight: 1.5}}>
            {card.initval ? card.initval : <span style={{color: '#bcbcbc'}}>请输入</span> }
            {card.count === 'true' ? <span style={{position: 'absolute', right: 0, bottom: 0}}>0/{card.fieldlength}</span> : null}
          </div>
        </div>
      </div>
    </div>)
  } else if (card.type === 'fileupload') {
    formItem = (
      <div className="am-list-item checkbox">
        <div className="am-list-line">
          <div className="am-input-label">{card.label}</div>
          <div className="am-input-control" style={{textAlign: 'left'}}>
            {card.fileType !== 'picture-card' ? <Icon type="upload" style={{position: 'absolute', right: '10px', top: '10px'}} /> : null}
            {card.fileType === 'picture-card' ? <Button style={{width: '100px', marginBottom: '10px', height: '100px', fontSize: '50px', color: '#d9d9d9'}}><Icon type="plus" /></Button> : null}
          </div>
        </div>
      </div>
    )
  } else if (card.type === 'funcvar') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-input-control">{card.linkfield}</div></div></div>)
  } else if (card.type === 'switch') {
    formItem = (<div className="am-list-item"><div className="am-list-line"><div className="am-input-label">{card.label}</div><div className="am-list-switch"><Switch checked={card.initval}/></div></div></div>)
  } else if (card.type === 'radio') {
    let options = null
    if (card.options && card.options.length > 0) {
      options = card.options
    } else {
      options = [
        {key: 'A', Value: 'A', Text: 'A'},
        {key: 'B', Value: 'B', Text: 'B'},
        {key: 'C', Value: 'C', Text: 'C'}
      ]
    }
    formItem = (
    <div className={'am-list-item checkbox mk-radio ' + (card.arrange || '')}>
      <div className="am-list-line">
        <div className="am-input-label">{card.label}</div>
        <div className="am-input-control">
          {card.arrange !== 'line' ? <Checkbox.Group value={[card.initval]}>
            {options.map(cell => <Checkbox key={cell.key} value={cell.Value}>{cell.Text}</Checkbox>)}
          </Checkbox.Group> : <div className="mk-radio-group">
            {options.map(cell => (<div key={cell.key} className="mk-radio-wrapper">
              <span className="radio-value">{cell.Text}</span>
              <span className={'radio-check ' + (card.initval === cell.Value ? 'checked' : '')}></span>
            </div>))}
          </div>}
        </div>
      </div>
    </div>)
  } else if (card.type === 'checkbox') {
    let _val = card.initval ? card.initval.split(',') : []
    let options = null
    if (card.options && card.options.length > 0) {
      options = card.options
    } else {
      options = [
        {key: 'A', Value: 'A', Text: 'A'},
        {key: 'B', Value: 'B', Text: 'B'},
        {key: 'C', Value: 'C', Text: 'C'}
      ]
    }
    formItem = (
      <div className={'am-list-item checkbox ' + (card.arrange || '')}>
        <div className="am-list-line">
          <div className="am-input-label">{card.label}</div>
          <div className="am-input-control">
            {<Checkbox.Group value={_val}>
              {options.map(cell => <Checkbox key={cell.key} value={cell.Value}>{cell.Text}</Checkbox>)}
            </Checkbox.Group>}
          </div>
        </div>
      </div>
    )
  } else if (card.type === 'hint') {
    formItem = <div className="am-list-item hint">
      <div className="am-list-line">
        <div className="am-input-label">{card.label}</div>
        <div className="am-input-control">
          {card.message}
        </div>
      </div>
    </div>
  } else if (card.type === 'split') {
    formItem = <div className="split-line">{card.label}</div>
  } else if (card.type === 'checkcard') {
    formItem = (<div className="am-list-item check-card">
      <div className="am-list-line">
        {card.hidelabel !== 'true' ? <div className="am-input-label">{card.label}</div> : null}
        <div className="am-input-control">
          <CheckCard config={card} />
        </div>
      </div>
    </div>)
  }
  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="copy" type="copy" onClick={copy} />
        <Icon className="close" type="close" onClick={close} />
      </div>
    } trigger="hover">
      <div className="page-card" style={{ opacity: opacity}}>
        <div ref={node => drag(drop(node))}>
          {card.type === 'split' ? formItem : <Form.Item
            className={'ant-form-item' + (card.required === 'true' ? ' required' : '') + (card.splitline === 'false' ? ' no-boder' : '')}
          >
            {formItem}
            {showField ? <div className="field-name">{card.field}{card.hidden === 'true' ? '(隐藏)' : ''}</div> : ''}
          </Form.Item>}
        </div>
      </div>
    </Popover>
  )
}
export default Card
src/mob/components/formdragelement/index.jsx
New file
@@ -0,0 +1,132 @@
import React, { useState } from 'react'
import { useDrop } from 'react-dnd'
import { is, fromJS } from 'immutable'
import update from 'immutability-helper'
import Utils from '@/utils/utils.js'
import Card from './card'
import './index.scss'
const Container = ({list, setting, handleList, handleForm, closeForm, showField }) => {
  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)
    delete card.focus
    handleForm(card)
  }
  const closeCard = id => {
    const { card } = findCard(id)
    closeForm(card)
  }
  const copyCard = id => {
    const { card, index: overIndex } = findCard(id)
    let _card = fromJS(card).toJS()
    _card.uuid = Utils.getuuid()
    _card.focus = true
    // 复制到剪切板
    let oInput = document.createElement('input')
    let val = JSON.parse(JSON.stringify(_card))
    val.copyType = 'form'
    oInput.value = window.btoa(window.encodeURIComponent(JSON.stringify(val)))
    document.body.appendChild(oInput)
    oInput.select()
    document.execCommand('Copy')
    oInput.className = 'oInput'
    oInput.style.display = 'none'
    document.body.removeChild(oInput)
    const _cards = update(cards, { $splice: [[overIndex + 1, 0, _card]] })
    setCards(_cards)
    handleList(_cards, _card)
  }
  const [, drop] = useDrop({
    accept: 'form',
    drop(item) {
      if (item.hasOwnProperty('originalIndex')) {
        return
      }
      let newcard = {}
      newcard.uuid = Utils.getuuid()
      newcard.label = 'label'
      newcard.type = item.subType
      newcard.resourceType = '0'
      newcard.options = []
      newcard.readonly = 'false'
      newcard.required = 'true'
      newcard.focus = true
      let targetId = ''
      if (item.dropTargetId) {
        targetId = item.dropTargetId
        delete item.dropTargetId
      } else if (cards.length > 0) {
        targetId = cards[cards.length - 1].uuid
      }
      const { index: overIndex } = findCard(`${targetId}`) // cards为空时 overIndex 为 -1
      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
      setCards(_cards)
      handleList(_cards, newcard)
    }
  })
  let style = {}
  if (setting.paddingLeft) {
    style.paddingLeft = setting.paddingLeft
  }
  if (setting.paddingRight) {
    style.paddingRight = setting.paddingRight
  }
  return (
    <div ref={drop} className="ant-row modal-fields-row mob-form" style={style}>
      {cards.map(card => {
        return <Card
          id={card.uuid}
          key={card.uuid}
          card={card}
          showField={showField}
          moveCard={moveCard}
          editCard={editCard}
          closeCard={closeCard}
          copyCard={copyCard}
          findCard={findCard}
        />
      })}
    </div>
  )
}
export default Container
src/mob/components/formdragelement/index.scss
New file
@@ -0,0 +1,245 @@
.modal-fields-row {
  padding-bottom: 35px;
  .mob-col.ant-col {
    display: inline-block;
    float: none;
    vertical-align: top;
    padding-left: 1.2%;
    padding-right: 1.2%;
  }
  .am-list-item {
    font-size: 16px;
    padding-left: 10px;
    position: relative;
    display: flex;
    height: 44px;
    min-height: 44px;
    background-color: #fff;
    vertical-align: middle;
    overflow: hidden;
    transition: background-color 200ms;
    align-items: center;
    .am-list-line {
      border-bottom: 1px solid #ddd;
      align-items: center;
      position: relative;
      display: flex;
      flex: 1 1;
      align-self: stretch;
      padding-right: 15px;
      overflow: hidden;
      .am-input-label {
        width: 28%;
        color: #000;
        font-size: 16px;
        margin-left: 0;
        margin-right: 5px;
        text-align: left;
        white-space: nowrap;
        overflow: hidden;
        padding: 2px 0;
        text-overflow: ellipsis;
      }
      .am-list-switch {
        flex: 1;
        text-align: right;
      }
      .am-input-control {
        font-size: 16px;
        flex: 1 1;
        text-align: right;
      }
      .am-input-control.left {
        text-align: left;
      }
      .am-list-extra {
        display: block;
        width: 15px;
        height: 15px;
        margin-left: 8px;
        i {
          vertical-align: top;
        }
      }
    }
  }
  .am-list-item.check-card {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
      .check-card-edit-box {
        margin-top: 0!important;
      }
    }
  }
  .am-list-item.hint {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
      .am-input-label {
        line-height: 2;
      }
      .am-input-control {
        font-size: 14px;
        line-height: 1.5;
        text-align: left;
        padding-bottom: 5px;
      }
    }
  }
  .am-list-item.checkbox {
    height: auto;
    .am-list-line {
      align-items: start;
      display: block;
    }
    .mk-radio-group {
      padding-left: 10px;
      text-align: left;
      .mk-radio-wrapper:not(:last-child) {
        border-bottom: 1px solid #ddd;
      }
      span {
        display: inline-block;
      }
      .radio-value {
        width: calc(100% - 50px);
        white-space: nowrap;
        overflow: hidden;
        padding: 2px 0;
        text-overflow: ellipsis;
        vertical-align: top;
      }
      .radio-check {
        position: relative;
        width: 50px;
        height: 30px;
        top: 8px;
      }
      .radio-check.checked::after {
        content: ' ';
        position: absolute;
        display: table;
        border: 1px solid #1890ff;
        border-top: 0;
        border-left: 0;
        top: 50%;
        left: 22%;
        width: 12px;
        height: 20px;
        transform: rotate(45deg) scale(1) translate(-50%, -50%);
      }
    }
    .ant-checkbox-group {
      display: block;
      padding-left: 10px;
      .ant-checkbox-wrapper {
        display: flex;
        text-align: left;
        font-size: 16px;
        .ant-checkbox {
          display: block;
          width: 30px;
          .ant-checkbox-inner {
            width: 22px;
            height: 22px;
            top: 12px;
          }
          .ant-checkbox-inner::after {
            width: 9px;
            height: 14px;
          }
        }
        .ant-checkbox + span {
          display: block;
          flex: 1 1;
          border-bottom: 1px solid #ddd;
        }
      }
      .ant-checkbox-wrapper:last-child {
        .ant-checkbox + span {
          border-bottom: none;
        }
      }
    }
  }
  .am-list-item.checkbox.mk-radio {
    .ant-checkbox-inner {
      border-radius: 50%;
    }
  }
  .am-list-item.checkbox:not(.line) {
    .ant-checkbox-group {
      .ant-checkbox-wrapper {
        float: left;
        margin-right: 15px;
        .ant-checkbox + span {
          border-bottom: none;
        }
      }
    }
    .mk-radio-group {
      .mk-radio-wrapper {
        float: left;
        margin-right: 15px;
      }
    }
  }
  .split-line {
    color: #1890ff;
    font-size: 16px;
    padding-left: 10px;
    padding-top: 10px;
    border-bottom: 1px solid #e9e9e9;
  }
  .check-card-edit-box .card-cell span {
    line-height: 1.5;
  }
  .ant-form-item {
    cursor: move;
    display: flex;
    margin-bottom: 0px;
    .ant-form-item-control-wrapper::after {
      content: '';
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      opacity: 0;
      z-index: 1;
    }
    .field-name {
      margin-left: 10px;
    }
    .ant-form-item-children {
      vertical-align: top;
    }
  }
  .ant-form-item.required {
    .am-input-label::before {
      display: inline-block;
      margin-right: 4px;
      color: #f5222d;
      font-size: 14px;
      font-family: SimSun, sans-serif;
      line-height: 1;
      content: '*';
    }
  }
  .ant-form-item.no-boder {
    .am-list-line {
      border-bottom: none;
    }
  }
}
.mob-form.modal-fields-row {
  .page-card {
    margin-bottom: 10px;
  }
}
src/mob/components/login/mob-login-1/index.jsx
File was deleted
src/mob/components/login/mob-login-1/index.scss
File was deleted
src/mob/components/login/mob-login-2/index.jsx
File was deleted
src/mob/components/login/mob-login-2/index.scss
File was deleted
src/mob/components/menubar/normal-menubar/index.jsx
New file
@@ -0,0 +1,265 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover, Modal } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import asyncIconComponent from '@/utils/asyncIconComponent'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import Utils from '@/utils/utils.js'
import 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 = asyncComponent(() => import('./menucomponent'))
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 NormalHeader = asyncComponent(() => import('@/menu/components/share/normalheader'))
const { confirm } = Modal
class NoramlMenuBar 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: '',
        parentId: '',
        dataName: card.dataName || '',
        width: card.width || 24,
        name: card.name,
        subtype: card.subtype,
        wrap: { name: card.name, width: card.width || 24, title: '' },
        style: { marginLeft: '0px', marginRight: '0px', marginTop: '8px', marginBottom: '8px' },
        headerStyle: { fontSize: '16px', borderBottomWidth: '1px', borderBottomColor: '#e8e8e8' },
        subMenus: [{
          uuid: Utils.getuuid(),
          setting: { type: 'menu', width: 6, sign: 'icon', icon: 'user', name: '客户', url: '', color: '#ffffff', iconFont: 20, padding: 12, background: '#1890ff', imgWidth: '' },
          style: {
            paddingTop: '15px', paddingBottom: '15px'
          }
        }]
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _card.wrap.name = card.name
        _card.style = config.style
        _card.headerStyle = config.headerStyle
        _card.subMenus = config.subMenus.map(item => {
          item.uuid = Utils.getuuid()
          return item
        })
      }
      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)
  }
  /**
   * @description 单个卡片信息更新
   */
  updateCard = (cell) => {
    let card = fromJS(this.state.card).toJS()
    card.subMenus = card.subMenus.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.subMenus = card.subMenus.filter(item => item.uuid !== cell.uuid)
        _this.setState({card})
        _this.props.updateConfig(card)
      },
      onCancel() {}
    })
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['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)
  }
  addMenu = () => {
    let card = fromJS(this.state.card).toJS()
    let newcard = {
      uuid: Utils.getuuid(),
      setting: { type: 'menu', width: 6, sign: 'icon', icon: 'user', name: '客户', url: '', color: '#ffffff', iconFont: 20, padding: 12, background: '#1890ff', imgWidth: '' },
      style: {
        paddingTop: '15px', paddingBottom: '15px'
      },
      isnew: true
    }
    if (card.subMenus.length > 0) {
      newcard = fromJS(card.subMenus.slice(-1)[0]).toJS()
      newcard.uuid = Utils.getuuid()
      newcard.isnew = true
    }
    card.subMenus.push(newcard)
    this.setState({card})
    this.props.updateConfig(card)
  }
  move = (item, direction) => {
    let card = fromJS(this.state.card).toJS()
    let dragIndex = card.subMenus.findIndex(c => c.uuid === item.uuid)
    let hoverIndex = null
    if (direction === 'left') {
      hoverIndex = dragIndex - 1
    } else {
      hoverIndex = dragIndex + 1
    }
    if (hoverIndex === -1 || hoverIndex === card.subMenus.length) return
    card.subMenus.splice(hoverIndex, 0, ...card.subMenus.splice(dragIndex, 1))
    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
    let offset = 0
    if (card.wrap.cardFloat && card.wrap.cardFloat !== 'left') {
      let _width = 0
      card.subMenus.forEach(card => {
        _width += card.setting.width
      })
      offset = _width < 24 ? 24 - _width : 0
      if (card.wrap.cardFloat === 'center') {
        offset = Math.floor(offset / 2)
      }
    }
    let _style = resetStyle(card.style)
    return (
      <div className="menu-menubar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <NormalHeader config={card} updateComponent={this.updateComponent}/>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="plus" title="添加菜单" onClick={this.addMenu} type="plus" />
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <CopyComponent type="normalmenu" card={card}/>
            <PasteComponent config={card} options={['menucell']} 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)} />
          </div>
        } trigger="hover">
          <Icon type="tool" />
        </Popover>
        {card.subMenus.map((menu, index) => (<MenuComponent key={menu.uuid} offset={!index ? offset : 0} cards={card} card={menu} move={this.move} updateElement={this.updateCard} deleteElement={this.deleteCard}/>))}
      </div>
    )
  }
}
export default NoramlMenuBar
src/mob/components/menubar/normal-menubar/index.scss
New file
@@ -0,0 +1,62 @@
.menu-menubar-edit-box {
  position: relative;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 20px;
  .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);
  }
  .menu-item {
    overflow: hidden;
    position: relative;
    min-height: 20px;
    .menu-name {
      text-align: center;
      font-style: inherit;
      font-weight: inherit;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    .menu-sign {
      text-align: center;
      .anticon {
        border-radius: 50%;
      }
      img {
        border-radius: 50%;
      }
    }
  }
}
.menu-menubar-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.menu-menubar-edit-box:hover {
  z-index: 1;
  box-shadow: 0px 0px 4px #1890ff;
}
src/mob/components/menubar/normal-menubar/menucomponent/index.jsx
New file
@@ -0,0 +1,218 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Modal, Popover, Icon, Col } from 'antd'
import asyncIconComponent from '@/utils/asyncIconComponent'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import Utils from '@/utils/utils.js'
import SettingForm from './settingform'
import { resetStyle } from '@/utils/utils-custom.js'
import MKEmitter from '@/utils/events.js'
import './index.scss'
const { confirm } = Modal
const CopyComponent = asyncIconComponent(() => import('@/menu/components/share/copycomponent'))
class MenuBoxComponent extends Component {
  static propTpyes = {
    offset: PropTypes.any,           // 偏移量
    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,
    visible: false
  }
  UNSAFE_componentWillMount () {
    const { card } = this.props
    this.setState({
      card: fromJS(card).toJS()
    })
  }
  componentDidMount () {
    const { card } = this.props
    MKEmitter.addListener('submitStyle', this.getStyle)
    if (card.isnew) {
      this.setState({
        visible: true
      })
    }
  }
  shouldComponentUpdate (nextProps, nextState) {
    const { cards } = this.props
    return !is(fromJS(cards.wrap), fromJS(nextProps.cards.wrap)) || !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @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)
  }
  changeStyle = () => {
    const { cards } = this.props
    const { card } = this.state
    let _style = card.style ? fromJS(card.style).toJS() : {}
    let options = ['font', 'border', 'padding']
    MKEmitter.emit('changeStyle', [cards.uuid, card.uuid], options, _style)
  }
  settingSubmit = () => {
    const { card } = this.state
    this.settingRef.handleConfirm().then(res => {
      let _card = {...card, setting: res}
      if (!card.isnew && card.setting.type === 'menu' && _card.setting.type !== 'menu') {
        const _this = this
        confirm({
          content: '菜单属性由“菜单”切换至其他类型时,菜单将被重置,即解除之前菜单的绑定关系,确定修改吗?',
          onOk() {
            _card.uuid = Utils.getuuid()
            _this.setState({ visible: false, card: _card })
            _this.props.updateElement(_card)
          },
          onCancel() {}
        })
      } else {
        delete _card.isnew
        this.setState({ visible: false, card: _card })
        this.props.updateElement(_card)
      }
    })
  }
  cancel = () => {
    const { card } = this.state
    if (card.isnew) {
      let _card = fromJS(card).toJS()
      delete _card.isnew
      this.setState({ visible: false, card: _card })
      this.props.updateElement(_card)
    } else {
      this.setState({ visible: false })
    }
  }
  changeMenu = () => {
    const { card } = this.state
    if (card.setting.type === 'link') {
      window.open(card.setting.linkurl)
    } else {
      MKEmitter.emit('changeEditMenu', {
        MenuID: card.setting.type === 'linkmenu' ? card.setting.linkMenuId : card.uuid,
        copyMenuId: card.setting.type === 'menu' ? card.setting.copyMenuId : '',
        MenuNo: card.setting.MenuNo || '',
        MenuName: card.setting.name,
      })
    }
  }
  render() {
    const { cards, offset } = this.props
    const { card, visible, dict } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style = resetStyle(_style)
    return (
      <Col span={card.setting.width || 6} offset={offset || 0}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <Icon className="edit" title="编辑" type="edit" onClick={() => this.setState({visible: true})} />
            <CopyComponent type="menucell" card={card}/>
            <Icon className="style" title="调整样式" onClick={this.changeStyle} type="font-colors" />
            <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
              <div className="mk-popover-control">
                <Icon className="plus" title="左移" type="arrow-left" onClick={() => this.props.move(card, 'left')} />
                <Icon className="close" title="右移" type="arrow-right" onClick={() => this.props.move(card, 'right')} />
              </div>
            } trigger="hover" getPopupContainer={() => document.getElementById(card.uuid + 'swap')}>
              <Icon type="swap" id={card.uuid + 'swap'}/>
            </Popover>
            <Icon className="close" title="删除菜单" type="delete" onClick={() => this.props.deleteElement(card)} />
          </div>
        } trigger="hover">
          <div className="menu-item" onDoubleClick={() => this.changeMenu()} style={_style}>
            {card.setting.sign === 'icon' ? <div className="menu-sign">
              <Icon style={{
                fontSize: card.setting.iconFont || 20,
                padding: card.setting.padding,
                background: card.setting.background,
                color: card.setting.color
              }} type={card.setting.icon}/>
            </div> : <div className="menu-sign">
              <img style={{width: card.setting.imgWidth, height: card.setting.imgWidth}} src={card.setting.url} alt=""/>
            </div>}
            <div className="menu-name">{card.setting.name}</div>
          </div>
        </Popover>
        <Modal
          wrapClassName="popview-modal"
          title={'菜单设置'}
          visible={visible}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.settingSubmit}
          onCancel={this.cancel}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            cards={cards}
            setting={card.setting}
            inputSubmit={this.settingSubmit}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
      </Col>
    )
  }
}
export default MenuBoxComponent
src/mob/components/menubar/normal-menubar/menucomponent/index.scss
copy from src/tabviews/zshare/mutilform/customSwitch/index.scss copy to src/mob/components/menubar/normal-menubar/menucomponent/index.scss
src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.jsx
New file
@@ -0,0 +1,302 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Radio, Tooltip, Icon, Input, InputNumber, Select } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const { TextArea } = Input
const SourceComponent = asyncComponent(() => import('@/menu/components/share/sourcecomponent'))
const ColorSketch = asyncComponent(() => import('@/mob/colorsketch'))
const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    cards: PropTypes.object,     // 卡片集
    setting: PropTypes.object,   // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    type: this.props.setting.type,
    sign: this.props.setting.sign,
    menulist: []
  }
  UNSAFE_componentWillMount() {
    let menulist = sessionStorage.getItem('appMenus')
    if (menulist) {
      try {
        menulist = JSON.parse(menulist)
      } catch {
        menulist = []
      }
    } else {
      menulist = []
    }
    this.setState({menulist})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { setting } = this.props
    const { getFieldDecorator } = this.props.form
    const { menulist, type, sign } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menubar-menu-card-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label="菜单名称">
                {getFieldDecorator('name', {
                  initialValue: setting.name || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '菜单名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="菜单参数">
                {getFieldDecorator('MenuNo', {
                  initialValue: setting.MenuNo || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '菜单参数!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  卡片宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: setting.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit}/>)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="菜单属性">
                {getFieldDecorator('type', {
                  initialValue: type,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '类型!'
                    }
                  ]
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({type: e.target.value})}>
                    <Radio value="menu">菜单</Radio>
                    <Radio value="linkmenu">关联菜单</Radio>
                    <Radio value="link">链接</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {type === 'menu' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="复制菜单仅在当前菜单不存在时有效。">
                  <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
                  复制菜单
                </Tooltip>
              }>
                {getFieldDecorator('copyMenuId', {
                  initialValue: setting.copyMenuId || ''
                })(
                  <Select allowClear>
                    {menulist.map(item => (<Select.Option key={item.MenuID} value={item.MenuID}>{item.MenuName}</Select.Option>))}
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            {type === 'linkmenu' ? <Col span={12}>
              <Form.Item label="关联菜单">
                {getFieldDecorator('linkMenuId', {
                  initialValue: setting.linkMenuId || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '关联菜单!'
                    }
                  ]
                })(
                  <Select
                    showSearch
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {menulist.map(option =>
                      <Select.Option key={option.MenuID} value={option.MenuID}>{option.MenuName}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col> : null}
            {type === 'link' ? <Col span={24} className="textarea">
              <Form.Item label="链接">
                {getFieldDecorator('linkurl', {
                  initialValue: setting.linkurl || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '链接!'
                    }
                  ]
                })( <TextArea rows={2}/> )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label="标志">
                {getFieldDecorator('sign', {
                  initialValue: sign,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '标志!'
                    }
                  ]
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}} onChange={(e) => this.setState({sign: e.target.value})}>
                    <Radio value="icon">图标</Radio>
                    <Radio value="image">图片</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {sign === 'icon' ? <Col span={12}>
              <Form.Item label="图标">
                {getFieldDecorator('icon', {
                  initialValue: setting.icon || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.select'] + '图标!'
                    }
                  ]
                })(
                  <MkIcon />
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'icon' ? <Col span={12}>
              <Form.Item label="字体大小">
                {getFieldDecorator('iconFont', {
                  initialValue: setting.iconFont || 20
                })(
                  <InputNumber min={12} max={200} precision={0} onPressEnter={this.handleSubmit}/>
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'icon' ? <Col span={12}>
              <Form.Item label="内边距">
                {getFieldDecorator('padding', {
                  initialValue: setting.padding || 12
                })(
                  <InputNumber min={0} max={200} precision={0} onPressEnter={this.handleSubmit}/>
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'icon' ? <Col span={12}>
              <Form.Item label="字体颜色">
                {getFieldDecorator('color', {
                  initialValue: setting.color || '#ffffff'
                })(
                  <ColorSketch />
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'icon' ? <Col span={12}>
              <Form.Item label="背景色">
                {getFieldDecorator('background', {
                  initialValue: setting.background || '#1890ff'
                })(
                  <ColorSketch />
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'image' ? <Col span={12}>
              <Form.Item label="图片地址">
                {getFieldDecorator('url', {
                  initialValue: setting.url || '',
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '图片地址!'
                    }
                  ]
                })(
                  <SourceComponent type="" placement="right"/>
                )}
              </Form.Item>
            </Col> : null}
            {sign === 'image' ? <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="图片宽度与高度相当,使用的图片比例应为1:1。">
                  <Icon type="question-circle" />
                  图片宽度
                </Tooltip>
              }>
                {getFieldDecorator('imgWidth', {
                  initialValue: setting.imgWidth || 36
                })(
                  <InputNumber min={10} max={500} precision={0} onPressEnter={this.handleSubmit}/>
                )}
              </Form.Item>
            </Col> : null}
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/components/menubar/normal-menubar/menucomponent/settingform/index.scss
New file
@@ -0,0 +1,25 @@
.model-menubar-menu-card-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
  .textarea {
    .ant-form-item-label {
      width: 16%;
    }
    .ant-form-item-control-wrapper {
      width: 84%;
    }
  }
  .ant-radio-wrapper {
    margin-right: 3px;
  }
  .color-sketch-block {
    margin-top: 7px;
  }
}
src/mob/components/menubar/normal-menubar/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/mob/components/menubar/normal-menubar/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,155 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, InputNumber, Select } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    config: PropTypes.object,    // 卡片行信息
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    roleList: [],
  }
  UNSAFE_componentWillMount () {
    let roleList = sessionStorage.getItem('sysRoles')
    if (roleList) {
      try {
        roleList = JSON.parse(roleList)
      } catch {
        roleList = []
      }
    } else {
      roleList = []
    }
    this.setState({roleList})
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { roleList } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menubar-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label="标题">
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="用于组件间的区分。">
                  <Icon type="question-circle" />
                  组件名称
                </Tooltip>
              }>
                {getFieldDecorator('name', {
                  initialValue: wrap.name,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '组件名称!'
                    }
                  ]
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="栅格布局,每行等分为24列。">
                  <Icon type="question-circle" />
                  宽度
                </Tooltip>
              }>
                {getFieldDecorator('width', {
                  initialValue: wrap.width || 24,
                  rules: [
                    {
                      required: true,
                      message: this.props.dict['form.required.input'] + '宽度!'
                    }
                  ]
                })(<InputNumber min={1} max={24} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="对齐方式">
                {getFieldDecorator('float', {
                  initialValue: wrap.float || 'left'
                })(
                  <Radio.Group style={{whiteSpace: 'nowrap'}}>
                    <Radio key="left" value="left"> 左对齐 </Radio>
                    <Radio key="center" value="center"> 居中 </Radio>
                    <Radio key="right" value="right"> 右对齐 </Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="黑名单">
                {getFieldDecorator('blacklist', {
                  initialValue: wrap.blacklist || []
                })(
                  <Select
                    showSearch
                    mode="multiple"
                    filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
                  >
                    {roleList.map(option =>
                      <Select.Option key={option.uuid} value={option.value}>{option.text}</Select.Option>
                    )}
                  </Select>
                )}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/components/menubar/normal-menubar/wrapsetting/settingform/index.scss
New file
@@ -0,0 +1,11 @@
.model-menubar-menu-setting-form {
  position: relative;
  .anticon-question-circle {
    color: #c49f47;
    margin-right: 3px;
  }
  .ant-input-number {
    width: 100%;
  }
}
src/mob/components/navbar/normal-navbar/index.jsx
New file
@@ -0,0 +1,187 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover } 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 SettingComponent = asyncIconComponent(() => import('@/menu/datasource'))
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,
        format: 'object',   // 组件属性 - 数据格式
        pageable: false,    // 组件属性 - 是否可分页
        switchable: false,  // 组件属性 - 数据是否可切换
        dataName: card.dataName || '',
        width: card.width || 24,
        name: card.name,
        subtype: card.subtype,
        setting: { interType: 'system' },
        wrap: { name: card.name, datatype: 'static', height: 50 },
        style: {borderTopColor: '#bcbcbc', borderTopWidth: '1px', paddingTop: '5px', fontSize: '13px' },
        menus: [],
        columns: [],
        scripts: [],
      }
      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, style}
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding'], card.style)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  changeMenu = (menu) => {
    if (menu.property === 'link') {
      window.open(menu.link)
    } else {
      MKEmitter.emit('changeEditMenu', {
        MenuID: menu.property === 'linkmenu' ? menu.linkMenuId : menu.MenuID,
        copyMenuId: menu.property === 'menu' ? menu.copyMenuId : '',
        MenuNo: menu.MenuNo,
        MenuName: menu.name,
      })
    }
  }
  render() {
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    _style.height = card.wrap.height
    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} />
            <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)} />
            {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>
        <div className="menu">
          {card.menus.map(menu => {
            return (
              <div key={menu.MenuID} className="am-tab-bar-tab" onDoubleClick={() => this.changeMenu(menu)}>
                {menu.icon ? <div className="am-tab-bar-tab-icon">
                  <span className="am-badge am-tab-bar-tab-badge tab-badge">
                    <Icon type={menu.icon} />
                    {menu.tip ? <sup className="am-badge-text"></sup> : null}
                  </span>
                </div> : null}
                <p className="am-tab-bar-tab-title">{menu.name}</p>
              </div>
            )
          })}
        </div>
      </div>
    )
  }
}
export default NormalNavbar
src/mob/components/navbar/normal-navbar/index.scss
New file
@@ -0,0 +1,111 @@
.normal-navbar-edit-box {
  position: absolute;
  bottom: 0px;
  left: 0px;
  width: 100%;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  min-height: 30px;
  z-index: 3;
  .menu {
    height: 100%;
    display: flex;
    font-size: inherit;
    color: inherit;
    .am-tab-bar-tab {
      position: relative;
      text-align: center;
      flex: 1;
      cursor: pointer;
      .anticon {
        font-size: 150%;
      }
      .am-badge-text {
        position: absolute;
        top: -2px;
        height: 8px;
        width: 8px;
        border-radius: 100%;
        background: #ff5b05;
        z-index: 1;
      }
      p {
        margin-bottom: 0;
      }
    }
  }
  .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-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;
}
src/mob/components/navbar/normal-navbar/menusetting/index.jsx
New file
@@ -0,0 +1,63 @@
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}
            cols={config.columns}
            ref={(ref) => { this.mTable = ref }}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/mob/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/mob/components/navbar/normal-navbar/menusetting/menuform/index.jsx
New file
@@ -0,0 +1,222 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon, Select } from 'antd'
import asyncComponent from '@/utils/asyncComponent'
import './index.scss'
const { TextArea } = Input
const MkIcon = asyncComponent(() => import('@/components/mkIcon'))
class SettingForm extends Component {
  static propTpyes = {
    menu: PropTypes.object,      // 菜单信息
    cols: PropTypes.array,       // 字段集
    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, cols } = 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} className="mob-menu-form">
        <Row gutter={24}>
          <Col span={12}>
            <Form.Item label="菜单名称">
              {getFieldDecorator('name', {
                initialValue: menu.name,
                rules: [
                  {
                    required: true,
                    message: '请输入菜单名称!'
                  }
                ]
              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="菜单参数">
              {getFieldDecorator('MenuNo', {
                initialValue: menu.MenuNo || '',
                rules: [
                  {
                    required: true,
                    message: '请输入菜单参数!'
                  }
                ]
              })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="图标">
              {getFieldDecorator('icon', {
                initialValue: menu.icon || ''
              })(
                <MkIcon allowClear />
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label={
              <Tooltip placement="topLeft" title="绑定提示字段后,会在菜单右上角显示提示信息。注:在添加图标时有效。">
                <Icon type="question-circle" style={{color: '#c49f47', marginRight: '3px'}}/>
                提示
              </Tooltip>
            }>
              {getFieldDecorator('tip', {
                initialValue: menu.tip || ''
              })(
                <Select allowClear>
                  {cols.map(item => <Select.Option key={item.uuid} value={item.field}>{item.label}</Select.Option>)}
                </Select>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <Form.Item label="菜单属性">
              {getFieldDecorator('property', {
                initialValue: menu.property || 'menu'
              })(
                <Radio.Group onChange={this.changeProperty} style={{whiteSpace: 'nowrap'}}>
                  <Radio value="menu">菜单</Radio>
                  <Radio value="linkmenu">关联菜单</Radio>
                  <Radio value="link">链接</Radio>
                </Radio.Group>
              )}
            </Form.Item>
          </Col>
          <Col span={12}>
            <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={24}>
            <Form.Item label="链接地址" className="textarea">
              {getFieldDecorator('link', {
                initialValue: menu.link || '',
                rules: [{
                  required: true,
                  message: '请输入链接地址!'
                }]
              })(<TextArea rows={2} />)}
            </Form.Item>
          </Col> : null}
          {property === 'linkmenu' ? <Col span={12}>
            <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={12}>
            <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/mob/components/navbar/normal-navbar/menusetting/menuform/index.scss
New file
@@ -0,0 +1,10 @@
.mob-menu-form {
  .ant-form-item.textarea {
    .ant-form-item-label {
      width: 16%;
    }
    .ant-form-item-control-wrapper {
      width: 84%;
    }
  }
}
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.jsx
New file
@@ -0,0 +1,198 @@
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 MenuTable extends Component {
  static propTpyes = {
    menus: PropTypes.array,     // 卡片行信息
    cols: PropTypes.array,      // 字段集
  }
  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: '关联菜单'}
        return trans[text]
      }},
      { title: '图标', dataIndex: 'icon', key: 'icon',  render: (text, record) => {
        return text ? <Icon type={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: '确定删除吗?',
      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: '菜单',
      property: 'menu'
    }
    this.setState({editMenu: _menu, visible: true})
  }
  menuSubmit = () => {
    const { editMenu, data } = this.state
    this.menuRef.handleConfirm().then(res => {
      let _menu = {...editMenu, ...res}
      let _data = fromJS(data).toJS()
      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
          }
        })
      }
      if (editMenu.MenuID && editMenu.property === 'menu' && _menu.property !== 'menu') {
        const _this = this
        confirm({
          content: '菜单属性由“菜单”切换至其他类型时,菜单将被重置,即解除之前菜单的绑定关系,确定修改吗?',
          onOk() {
            _data = _data.map(item => {
              if (item.MenuID === _menu.MenuID) {
                item.MenuID = Utils.getuuid()
              }
              return item
            })
            _this.setState({data: _data, editMenu: null, visible: false})
          },
          onCancel() {}
        })
      } else {
        this.setState({data: _data, editMenu: null, visible: false})
      }
    })
  }
  menuUpdate = (res) => {
    const { data } = this.state
    this.setState({
      data: data.map(item => {
        if (item.MenuID === res.MenuID) {
          return res
        } else {
          return item
        }
      })
    })
  }
  render() {
    const { cols } = this.props
    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}
          dataSource={data}
          pagination={false}
        />
        <Modal
          title="编辑"
          visible={visible}
          width={750}
          maskClosable={false}
          onOk={this.menuSubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <MenuForm
            menu={editMenu}
            cols={cols}
            inputSubmit={this.menuSubmit}
            wrappedComponentRef={(inst) => this.menuRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default MenuTable
src/mob/components/navbar/normal-navbar/menusetting/menutable/index.scss
New file
@@ -0,0 +1,23 @@
.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 {
    background: #ffffff;
  }
  .ant-table-body {
    margin: 0!important;
  }
}
src/mob/components/navbar/normal-navbar/wrapsetting/index.jsx
New file
@@ -0,0 +1,81 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title="菜单栏设置"
          visible={visible}
          width={800}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/mob/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/mob/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,118 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, InputNumber, Tooltip, Icon, Radio } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {}
  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 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="选择静态值,无需配置数据源。">
                  <Icon type="question-circle" />
                  数据来源
                </Tooltip>
              }>
                {getFieldDecorator('datatype', {
                  initialValue: wrap.datatype || 'static'
                })(
                  <Radio.Group>
                    <Radio value="dynamic">动态</Radio>
                    <Radio value="static">静态</Radio>
                  </Radio.Group>
                )}
              </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={30} max={200} precision={0} onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/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/mob/components/topbar/normal-navbar/index.jsx
New file
@@ -0,0 +1,195 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Popover } 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'))
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,
        width: 24,
        subtype: card.subtype,
        wrap: { type: 'navbar', height: 50, title: 'NavBar', back: 'true', search: 'false', logout: 'false' },
        style: {borderBottomColor: '#bcbcbc', borderBottomWidth: '1px', paddingLeft: '10px', paddingRight: '10px', lineHeight: '2.8', fontSize: '18px' },
      }
      if (card.config) {
        let config = fromJS(card.config).toJS()
        _card.wrap = config.wrap
        _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)
    MKEmitter.addListener('submitSearch', this.getSearch)
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('submitStyle', this.getStyle)
    MKEmitter.removeListener('submitSearch', this.getSearch)
  }
  /**
   * @description 卡片行外层信息更新(数据源,样式等)
   */
  updateComponent = (component) => {
    this.setState({
      card: component
    })
    this.props.updateConfig(component)
  }
  getStyle = (comIds, style) => {
    const { card } = this.state
    if (comIds[0] !== card.uuid) return
    let _card = {...card, style}
    this.setState({
      card: _card
    })
    this.props.updateConfig(_card)
  }
  changeStyle = () => {
    const { card } = this.state
    MKEmitter.emit('changeStyle', [card.uuid], ['font', 'background', 'border', 'padding', 'shadow'], card.style)
  }
  clickComponent = (e) => {
    if (sessionStorage.getItem('style-control') === 'true' || sessionStorage.getItem('style-control') === 'component') {
      e.stopPropagation()
      MKEmitter.emit('clickComponent', this.state.card)
    }
  }
  changeMenu = (menu) => {
    if (menu.property === 'link') {
      window.open(menu.link)
    } else {
      MKEmitter.emit('changeEditMenu', {
        MenuID: menu.property === 'linkmenu' ? menu.linkMenuId : menu.MenuID,
        copyMenuId: menu.property === 'menu' ? menu.copyMenuId : '',
        MenuNo: menu.MenuNo,
        MenuName: menu.name,
      })
    }
  }
  getSearch = (config) => {
    const { card } = this.state
    if (card.uuid !== config.uuid) return
    this.setState({
      card: config
    })
    this.props.updateConfig(config)
  }
  setSearch = () => {
    let card = fromJS(this.state.card).toJS()
    if (!card.search) {
      card.search = {
        floor: 1,
        setting: { type: 'title', field: '', title: '', focus: 'true', btn: 'hidden' },
        groups: [],
        fields: []
      }
    }
    this.props.updateConfig(card)
    MKEmitter.emit('changeSearch', card)
  }
  render() {
    const { card } = this.state
    let _style = {...card.style}
    if (_style.shadow) {
      _style.boxShadow = '0 0 4px ' + _style.shadow
    }
    return (
      <div className="normal-topbar-edit-box" style={_style} onClick={this.clickComponent} id={card.uuid}>
        <Popover overlayClassName="mk-popover-control-wrap" mouseLeaveDelay={0.2} mouseEnterDelay={0.2} content={
          <div className="mk-popover-control">
            <WrapComponent config={card} updateConfig={this.updateComponent} />
            <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="am-navbar">
          <div className="am-navbar-left">
            {card.wrap.back !== 'false' ? <Icon type="left" /> : null}
          </div>
          {card.wrap.type !== 'search' ?
            <div className="am-navbar-title">{card.wrap.title || ''}</div> :
            <div className="am-navbar-search" onDoubleClick={this.setSearch}><div className="search-bar"><Icon type="search" /></div></div>
          }
          <div className="am-navbar-right">
            {card.wrap.search === 'true' ? <Icon type="search" onDoubleClick={this.setSearch}/> : null}
            {card.wrap.logout === 'true' ? <Icon type="ellipsis" /> : null}
          </div>
        </div>
      </div>
    )
  }
}
export default NormalNavbar
src/mob/components/topbar/normal-navbar/index.scss
New file
@@ -0,0 +1,82 @@
.normal-topbar-edit-box {
  position: absolute;
  top: 0px;
  left: 0px;
  width: 100%;
  box-sizing: border-box;
  background: #ffffff;
  background-position: center center;
  background-repeat: no-repeat;
  background-size: cover;
  height: 50px;
  z-index: 3;
  .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);
    color: rgba(0, 0, 0, 0.65);
  }
  .am-navbar {
    height: 100%;
    display: flex;
    font-style: inherit;
    font-weight: inherit;
    .am-navbar-left {
      width: 30px;
      text-align: left;
      color: #1890ff;
      font-size: 20px;
      line-height: 50px;
    }
    .am-navbar-title {
      text-align: center;
      font-style: inherit;
      font-weight: inherit;
      flex: 1;
    }
    .am-navbar-search {
      flex: 1;
      padding: 0 5px;
      .search-bar {
        height: 30px;
        line-height: 30px;
        padding: 0 10px;
        color:rgba(0, 0, 0, 0.65);
        border: 1px solid #E9EDF0;
        background: #E9EDF0;
        border-radius: 4px;
        position: relative;
        top: 50%;
        transform: translateY(-50%);
        cursor: pointer;
        font-size: 18px;
      }
    }
    .am-navbar-right {
      text-align: right;
      color: #1890ff;
      min-width: 30px;
      font-size: 20px;
      line-height: 50px;
      .anticon-search {
        margin-right: 5px;
        padding: 5px;
        cursor: pointer;
      }
    }
  }
}
.normal-topbar-edit-box::after {
  display: block;
  content: ' ';
  clear: both;
}
.normal-topbar-edit-box:hover {
  box-shadow: 0px 0px 4px #1890ff;
}
src/mob/components/topbar/normal-navbar/wrapsetting/index.jsx
New file
@@ -0,0 +1,81 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { Icon, Modal } from 'antd'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import SettingForm from './settingform'
import './index.scss'
class DataSource extends Component {
  static propTpyes = {
    config: PropTypes.any,
    updateConfig: PropTypes.func
  }
  state = {
    dict: sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS,
    visible: false,
    wrap: null
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    this.setState({wrap: fromJS(config.wrap).toJS()})
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.props), fromJS(nextProps)) || !is(fromJS(this.state), fromJS(nextState))
  }
  editDataSource = () => {
    this.setState({
      visible: true
    })
  }
  verifySubmit = () => {
    const { config } = this.props
    this.verifyRef.handleConfirm().then(res => {
      this.setState({
        wrap: res,
        visible: false
      })
      this.props.updateConfig({...config, wrap: res})
    })
  }
  render () {
    const { visible, dict, wrap } = this.state
    return (
      <div className="model-menu-setting-wrap">
        <Icon type="edit" title="编辑" onClick={() => this.editDataSource()} />
        <Modal
          wrapClassName="popview-modal"
          title="导航栏设置"
          visible={visible}
          width={750}
          maskClosable={false}
          okText={dict['model.submit']}
          onOk={this.verifySubmit}
          onCancel={() => { this.setState({ visible: false }) }}
          destroyOnClose
        >
          <SettingForm
            dict={dict}
            wrap={wrap}
            inputSubmit={this.verifySubmit}
            wrappedComponentRef={(inst) => this.verifyRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default DataSource
src/mob/components/topbar/normal-navbar/wrapsetting/index.scss
New file
@@ -0,0 +1,7 @@
.model-menu-setting-wrap {
  display: inline-block;
  >.anticon-edit {
    color: #1890ff;
  }
}
src/mob/components/topbar/normal-navbar/wrapsetting/settingform/index.jsx
New file
@@ -0,0 +1,130 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { Form, Row, Col, Input, Radio, Tooltip, Icon } from 'antd'
import './index.scss'
class SettingForm extends Component {
  static propTpyes = {
    dict: PropTypes.object,      // 字典项
    wrap: PropTypes.object,      // 数据源配置
    inputSubmit: PropTypes.func  // 回车事件
  }
  state = {
    type: this.props.wrap.type || 'navbar'
  }
  handleConfirm = () => {
    // 表单提交时检查输入值是否正确
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (!err) {
          resolve(values)
        } else {
          reject(err)
        }
      })
    })
  }
  handleSubmit = (e) => {
    e.preventDefault()
    if (this.props.inputSubmit) {
      this.props.inputSubmit()
    }
  }
  render() {
    const { wrap } = this.props
    const { getFieldDecorator } = this.props.form
    const { type } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 8 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <div className="model-menu-setting-form">
        <Form {...formItemLayout}>
          <Row gutter={24}>
            <Col span={12}>
              <Form.Item label="类型">
                {getFieldDecorator('type', {
                  initialValue: wrap.type || 'navbar'
                })(
                  <Radio.Group onChange={(e) => this.setState({type: e.target.value})}>
                    <Radio value="navbar">导航栏</Radio>
                    <Radio value="search">搜索栏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="使用搜索栏时,标题用于搜索条件隐藏时显示。">
                  <Icon type="question-circle" />
                  标题
                </Tooltip>
              }>
                {getFieldDecorator('title', {
                  initialValue: wrap.title || ''
                })(<Input placeholder={''} autoComplete="off" onPressEnter={this.handleSubmit} />)}
              </Form.Item>
            </Col>
            <Col span={12}>
              <Form.Item label="返回">
                {getFieldDecorator('back', {
                  initialValue: wrap.back || 'true'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
            {type === 'navbar' ? <Col span={12}>
              <Form.Item label="搜索">
                {getFieldDecorator('search', {
                  initialValue: wrap.search || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col> : null}
            <Col span={12}>
              <Form.Item label={
                <Tooltip placement="topLeft" title="点击退出时,返回第一个页面。">
                  <Icon type="question-circle" />
                  退出
                </Tooltip>
              }>
                {getFieldDecorator('logout', {
                  initialValue: wrap.logout || 'false'
                })(
                  <Radio.Group>
                    <Radio value="true">显示</Radio>
                    <Radio value="false">隐藏</Radio>
                  </Radio.Group>
                )}
              </Form.Item>
            </Col>
          </Row>
        </Form>
      </div>
    )
  }
}
export default Form.create()(SettingForm)
src/mob/components/topbar/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/mob/header/index.jsx
@@ -1,4 +1,5 @@
import React, {Component} from 'react'
import { Radio } from 'antd'
import avatar from '@/assets/img/avatar.jpg'
import MainLogo from '@/assets/img/main-logo.png'
@@ -7,13 +8,26 @@
class MobHeader extends Component {
  state = {
    avatar: sessionStorage.getItem('CloudAvatar') || avatar,
    userName: sessionStorage.getItem('CloudUserName')
    userName: sessionStorage.getItem('CloudUserName'),
    typename: sessionStorage.getItem('typename')
  }
  changeView = (e) => {
    let val = e.target.value
    this.props.changeView(val)
  }
  render () {
    const { typename } = this.state
    return (
      <header className="mob-header-container">
        <div className="header-logo"><img src={MainLogo} alt=""/></div>
        {typename === 'pad' ? <div className="change-view">
          <Radio.Group defaultValue="vertical" onChange={this.changeView}>
            <Radio value="vertical">竖屏</Radio>
            <Radio value="horizontal">横屏</Radio>
          </Radio.Group>
        </div> : null}
        <div className="header-user">
          <img src={this.state.avatar} alt=""/>
          <span>
src/mob/header/index.scss
@@ -45,4 +45,12 @@
      }
    }
  }
  .change-view {
    position: absolute;
    left: calc(50% - 70px);
    top: 12px;
    .ant-radio-wrapper {
      color: #ffffff;
    }
  }
}
src/mob/home/index.jsx
File was deleted
src/mob/home/index.scss
File was deleted
src/mob/mobshell/card.jsx
@@ -5,8 +5,10 @@
import './index.scss'
const AntvBar = asyncComponent(() => import('@/menu/components/chart/antv-bar'))
const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
// const MainSearch = asyncComponent(() => import('@/menu/components/search/main-search'))
const AntvPie = asyncComponent(() => import('@/menu/components/chart/antv-pie'))
const AntvDashboard = asyncComponent(() => import('@/menu/components/chart/antv-dashboard'))
const AntvScatter = asyncComponent(() => import('@/menu/components/chart/antv-scatter'))
const AntvTabs = asyncComponent(() => import('@/menu/components/tabs/antv-tabs'))
const DataCard = asyncComponent(() => import('@/menu/components/card/data-card'))
const PropCard = asyncComponent(() => import('@/menu/components/card/prop-card'))
@@ -17,6 +19,12 @@
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 BraftEditor = asyncComponent(() => import('@/menu/components/editor/braft-editor'))
const NormalLogin = asyncComponent(() => import('@/pc/components/login/normal-login'))
const NormalNavbar = asyncComponent(() => import('@/mob/components/navbar/normal-navbar'))
const NormalTopbar = asyncComponent(() => import('@/mob/components/topbar/normal-navbar'))
const NormalMenuBar = asyncComponent(() => import('@/mob/components/menubar/normal-menubar'))
const Balcony = asyncComponent(() => import('@/menu/components/card/balcony'))
const Card = ({ id, card, moveCard, findCard, delCard, updateConfig }) => {
  const originalIndex = findCard(id).index
@@ -51,13 +59,34 @@
    style = { opacity: 0.3}
  }
  let col = 'ant-col-' + (card.width || 24)
  if (card.type === 'login') {
    let height = ''
    if (card.wrap && card.wrap.height) {
      // scaleview
      height = card.wrap.height.replace(/\d+vw/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winWidth || 420) / 100 + 'px'
        // return parseFloat(word) * 350 / 100 + 'px'
      }).replace(/\d+vh/ig, (word) => {
        return parseFloat(word) * (window.GLOB.winHeight || 738) / 100 + 'px'
        // return parseFloat(word) * 615 / 100 + 'px'
      })
    }
    style.minHeight = height
  }
  const getCardComponent = () => {
    if (card.type === 'bar' || card.type === 'line') {
      return (<AntvBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'search') {
      return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    // } else if (card.type === 'search') {
    //   return (<MainSearch card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'pie') {
      return (<AntvPie card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'dashboard') {
      return (<AntvDashboard card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'scatter') {
      return (<AntvScatter card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'form') {
      return (<NormalForm card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'tabs') {
@@ -78,12 +107,29 @@
      return (<NormalGroup group={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'code') {
      return (<CodeSandbox card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'editor') {
      return (<BraftEditor card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'login') {
      return (<NormalLogin card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'navbar') {
      return (<NormalNavbar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'topbar') {
      return (<NormalTopbar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'menubar') {
      return (<NormalMenuBar card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    } else if (card.type === 'balcony') {
      return (<Balcony card={card} updateConfig={updateConfig} deletecomponent={delCard}/>)
    }
  }
  return (
    <div className={'ant-col mk-component-card ant-col-' + (card.width || 24)} ref={node => drag(drop(node))} style={style}>
      {getCardComponent()}
    </div>
  )
  if (card.type === 'navbar' || card.type === 'topbar') {
    return getCardComponent()
  } else {
    return (
      <div className={'ant-col mk-component-card ' + col} ref={node => drag(drop(node))} style={style}>
        {getCardComponent()}
      </div>
    )
  }
}
export default Card
src/mob/mobshell/index.jsx
@@ -19,6 +19,10 @@
    handleList({...menu, components: _cards})
    setCards(_cards)
  }
  if (menu.components.length > cards.length) {
    setCards(menu.components)
  }
  
  const findCard = id => {
    const card = cards.filter(c => `${c.uuid}` === id)[0]
@@ -49,13 +53,16 @@
    let uuids = MenuUtils.getDelButtonIds(card)
    confirm({
      title: `确定删除《${card.name}》吗?`,
      title: `确定删除${card.name ? `《${card.name}》` : '组件'}吗?`,
      content: hasComponent ? '当前组件中含有子组件!' : '',
      onOk() {
        MKEmitter.emit('delButtons', uuids)
        const _cards = cards.filter(item => item.uuid !== card.uuid)
        handleList({...menu, components: _cards})
        setCards(_cards)
        if (uuids.length === 0) return
        MKEmitter.emit('delButtons', uuids)
      },
      onCancel() {}
    })
@@ -78,6 +85,24 @@
          })
          return
        }
      } else if (item.component === 'navbar') {
        if (cards.filter(card => card.type === 'navbar').length > 0) {
          notification.warning({
            top: 92,
            message: '菜单栏不可重复添加!',
            duration: 5
          })
          return
        }
      } else if (item.component === 'topbar') {
        if (cards.filter(card => card.type === 'topbar').length > 0) {
          notification.warning({
            top: 92,
            message: '导航栏不可重复添加!',
            duration: 5
          })
          return
        }
      }
      let name = ''
@@ -92,8 +117,12 @@
        editor: '富文本',
        code: '自定义',
        carousel: '轮播',
        dashboard: '仪表盘',
        form: '表单',
        card: '卡片'
        card: '卡片',
        navbar: '导航栏',
        menubar: '菜单栏',
        login: '登录'
      }
      let i = 1
      
@@ -127,7 +156,25 @@
      }
      const { index: overIndex } = findCard(`${targetId}`)
      const _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
      let _cards = update(cards, { $splice: [[overIndex + 1, 0, newcard]] })
      let Topbar = null
      let Navbar = null
      _cards = _cards.filter(item => {
        if (item.type === 'topbar') {
          Topbar = item
        } else if (item.type === 'navbar') {
          Navbar = item
        }
        return item.type !== 'topbar' && item.type !== 'navbar'
      })
      if (Topbar) {
        _cards.unshift(Topbar)
      }
      if (Navbar) {
        _cards.push(Navbar)
      }
      handleList({...menu, components: _cards})
      setCards(_cards)
@@ -135,8 +182,8 @@
  })
  return (
    <div ref={drop} className="mob-shell-inner" id="menu-shell-inner" style={menu.style}>
      <div className="ant-row">
    <div ref={drop} className="mob-shell-inner" id="menu-shell-inner">
      <div className="ant-row" style={menu.style}>
        {cards.map(card => (
          <Card
            id={card.uuid}
@@ -148,10 +195,10 @@
            updateConfig={updateConfig}
          />
        ))}
        {cards.length === 0 ?
          <Empty description="请添加组件" /> : null
        }
      </div>
      {cards.length === 0 ?
        <Empty description="请添加组件" /> : null
      }
    </div>
  )
}
src/mob/mobshell/index.scss
@@ -1,8 +1,34 @@
.mob-shell-inner {
  // scaleview
  width: 120%;
  height: 120%;
  overflow-y: auto;
  overflow-x: hidden;
  background: #ffffff;
  box-shadow: 0px 0px 2px #000000;
  transform: scale(0.8333333333333334, 0.8333333333333334);
  transform-origin: left top;
  .mk-component-card {
    position: relative;
  }
  >.ant-empty {
    padding-top: 150px;
  >.ant-row {
    min-height: 100%;
    >.ant-empty {
      padding-top: 150px;
    }
  }
}
.mob-shell-inner::-webkit-scrollbar {
  width: 2px;
}
.mob-shell-inner::-webkit-scrollbar-thumb {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
  background: rgba(0, 0, 0, 0.23);
  border-radius: 5px;
}
.mob-shell-inner::-webkit-scrollbar-track {
  box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
  border: 1px solid rgba(0, 0, 0, 0.07);
  background: rgba(0, 0, 0, 0);
  border-radius: 3px;
}
src/mob/modalconfig/controller.jsx
New file
@@ -0,0 +1,64 @@
import React, {Component} from 'react'
import { is, fromJS } from 'immutable'
import MKEmitter from '@/utils/events.js'
import ModalConfig from '@/mob/modalconfig'
class ModalController extends Component {
  state = {
    btn: null,
    config: null,
    visible: false
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  componentDidMount () {
    MKEmitter.addListener('changeModal', this.initConfig)
  }
  /**
   * @description 组件销毁,清除state更新,清除快捷键设置
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
    MKEmitter.removeListener('changeModal', this.initConfig)
  }
  initConfig = (config, btn) => {
    this.setState({
      visible: true,
      config: fromJS(config).toJS(),
      btn: fromJS(btn).toJS()
    })
  }
  handleBack = () => {
    this.setState({
      visible: false,
      config: null,
      btn: null
    })
  }
  handleSave = (modal) => {
    const { config, btn } = this.state
    MKEmitter.emit('submitModal', config, btn, modal)
  }
  render () {
    const { config, btn, visible } = this.state
    if (!visible) return null
    return (
      <ModalConfig btn={btn} componentConfig={config} handleBack={this.handleBack} handleSave={this.handleSave}/>
    )
  }
}
export default ModalController
src/mob/modalconfig/index.jsx
New file
@@ -0,0 +1,466 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import { DndProvider } from 'react-dnd'
import HTML5Backend from 'react-dnd-html5-backend'
import moment from 'moment'
import { Button, Modal, Collapse, notification, Switch, Icon } from 'antd'
import Api from '@/api'
import Utils from '@/utils/utils.js'
import zhCN from '@/locales/zh-CN/model.js'
import enUS from '@/locales/en-US/model.js'
import { getModalForm } from '@/templates/zshare/formconfig'
import SourceElement from '@/templates/modalconfig/dragelement/source'
import SettingForm from '@/templates/modalconfig/settingform'
import asyncComponent from '@/utils/asyncComponent'
import { SearchItems } from './source'
import './index.scss'
const { Panel } = Collapse
const { confirm } = Modal
const CommonDict = sessionStorage.getItem('lang') !== 'en-US' ? zhCN : enUS
const PasteComponent = asyncComponent(() => import('./pastecomponent'))
const ModalForm = asyncComponent(() => import('@/templates/zshare/modalform'))
const DragElement = asyncComponent(() => import('@/mob/components/formdragelement'))
const FieldsComponent = asyncComponent(() => import('@/templates/sharecomponent/fieldscomponent'))
class ComModalConfig extends Component {
  static propTpyes = {
    btn: PropTypes.object,
    handleSave: PropTypes.func,
    handleBack: PropTypes.func
  }
  state = {
    dict: CommonDict,      // 字典
    config: null,          // 页面配置,包括模板类型、模态框设置、添加表名、表单列表
    visible: false,        // 表单编辑模态框,显示控制
    formlist: null,        // 表单编辑模态框,可编辑字段
    card: null,            // 编辑元素
    settingVisible: false, // 全局配置模态框
    originConfig: null,    // 原始菜单
    sqlVerifing: false,    // sql验证
    showField: false,      // 显示表单字段值
    standardform: null
  }
  /**
   * @description 数据预处理
   */
  UNSAFE_componentWillMount () {
    const { btn } = this.props
    let _config = btn.modal
    _config.version = '1.0'
    this.setState({
      config: _config,
      originConfig: fromJS(_config).toJS()
    })
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  /**
   * @description 表单变化
   * 1、表单拖拽添加时,检查是否存在示例表单,如存在则去除示例
   * 2、表单移动后,保存移动后的顺序
   * 3、新增表单时,直接打开编辑框
   */
  handleList = (list, newcard) => {
    let _config = fromJS(this.state.config).toJS()
    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})
    }
  }
  /**
   * @description 表单编辑
   * 1、显示编辑弹窗-visible
   * 2、保存编辑项-card
   * 3、设置编辑参数项-formlist
   */
  handleForm = (_card) => {
    const { componentConfig } = this.props
    const { config } = this.state
    let card = fromJS(_card).toJS()
    let _inputfields = []
    let _tabfields = []
    let _linkableFields = []
    let _linksupFields = [{
      value: '',
      text: '空'
    }]
    let standardform = null
    _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)
    let index = null
    config.fields.forEach((item, i) => {
      if (card.uuid === item.uuid) {
        index = i
      }
      if (!['select', 'link', 'radio', 'checkcard'].includes(item.type)) return
      if (item.field && !uniq.has(item.field)) {
        uniq.set(item.field, true)
        _linkableFields.push({
          value: item.field,
          text: item.label + ' (表单)'
        })
        _linksupFields.push({
          value: item.field,
          text: item.label
        })
      }
    })
    if (index !== null) {
      if (index === 0) {
        standardform = config.fields[index + 1] || null
      } else {
        standardform = config.fields[index - 1] || null
      }
    }
    componentConfig.columns.forEach(col => {
      if (col.field && !uniq.has(col.field)) {
        uniq.set(col.field, true)
        _linkableFields.push({
          value: col.field,
          text: col.label + ' (显示列)'
        })
      }
    })
    if (card.linkSubField && card.linkSubField.length > 0) {
      let fields = _inputfields.map(item => item.field)
      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)
    })
  }
  /**
   * @description 编辑后提交
   * 1、获取编辑后的表单信息
   * 2、去除可能存在的示例表单
   * 3、通过loading刷新
   */
  handleSubmit = () => {
    this.formRef.handleConfirm().then(res => {
      let _config = fromJS(this.state.config).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
      }
      _config.fields = _config.fields.filter(item => !item.origin)
      if (['select', 'multiselect', 'link', 'checkbox', 'radio', 'checkcard'].includes(res.type) && res.resourceType === '1' && /\s/.test(res.dataSource)) {
        this.setState({
          sqlVerifing: true
        })
        let param = {
          func: 's_debug_sql',
          exec_type: 'y',
          LText: res.dataSource
        }
        param.LText = param.LText.replace(/@\$|\$@/ig, '')
        param.LText = Utils.formatOptions(param.LText)
        param.timestamp = moment().format('YYYY-MM-DD HH:mm:ss')
        param.secretkey = Utils.encrypt('', param.timestamp)
        if (window.GLOB.mainSystemApi && res.database === 'sso') {
          param.rduri = window.GLOB.mainSystemApi
        }
        Api.getLocalConfig(param).then(result => {
          if (result.status) {
            this.setState({
              sqlVerifing: false,
              config: _config,
              card: null,
              visible: false
            })
          } else {
            this.setState({sqlVerifing: false})
            Modal.error({
              title: result.message
            })
          }
        })
      } else {
        this.setState({
          config: _config,
          card: null,
          visible: false
        })
      }
    })
  }
  /**
   * @description 表单删除并刷新
   */
  closeForm = (card) => {
    let _this = this
    confirm({
      content: `确定删除${card.label ? `<<${card.label}>>` : ''}吗?`,
      onOk() {
        let _config = fromJS(_this.state.config).toJS()
        _config.fields = _config.fields.filter(item => !(item.uuid === card.uuid))
        _this.setState({
          config: _config,
        })
      },
      onCancel() {}
    })
  }
  submitConfig = () => {
    const { config } = this.state
    this.setState({originConfig: fromJS(config).toJS()})
    this.props.handleSave(config)
    notification.success({
      top: 92,
      message: '保存成功。',
      duration: 2
    })
  }
  cancelConfig = () => {
    const { config, originConfig } = this.state
    if (!is(fromJS(config), fromJS(originConfig))) {
      let _this = this
      confirm({
        content: '配置信息未保存,确定返回吗?',
        onOk() {
          _this.props.handleBack()
        },
        onCancel() {}
      })
    } else {
      this.props.handleBack()
    }
  }
  /**
   * @description 全局设置模态框
   */
  changeSetting = () => {
    this.setState({
      settingVisible: true
    })
  }
  /**
   * @description 保存全局设置
   */
  settingSave = () => {
    const {config} = this.state
    this.settingRef.handleConfirm().then(res => {
      this.setState({
        config: {...config, setting: res},
        settingVisible: false
      })
    })
  }
  editModalCancel = () => {
    const { config, card } = this.state
    if (card.focus) {
      let _fields = config.fields.filter(item => item.uuid !== card.uuid)
      let _config = {...config, fields: _fields}
      this.setState({
        card: null,
        config: _config,
        visible: false
      })
    } else {
      this.setState({
        card: null,
        visible: false
      })
    }
  }
  /**
   * @description 更新
   */
  updateConfig = (config) => {
    this.setState({
      config
    })
  }
  insert = (config) => {
    this.setState({
      config
    }, () => {
      this.handleForm(config.fields[config.fields.length - 1])
    })
  }
  render () {
    const { config, dict } = this.state
    return (
      <div className="mob-form-board">
        <DndProvider backend={HTML5Backend}>
          <div className="tools">
            <Collapse accordion defaultActiveKey="1" bordered={false}>
              <Panel header={dict['header.menu.form']} key="1">
                <div className="search-element">
                  {SearchItems.map((item, index) => {
                    return (<SourceElement key={index} content={item}/>)
                  })}
                </div>
                <FieldsComponent
                  config={config}
                  type="form"
                  updatefield={this.updateConfig}
                />
              </Panel>
            </Collapse>
          </div>
          <div className="modal-control">
            <Button icon="setting" onClick={this.changeSetting}>设置</Button>
            <Button type="primary" onClick={this.submitConfig}>保存</Button>
            <Button onClick={this.cancelConfig}>返回</Button>
            <PasteComponent config={config} updateConfig={this.insert} />
            <Switch checkedChildren={dict['model.switch.open']} unCheckedChildren={dict['model.switch.close']} defaultChecked={this.state.showField} onChange={(val) => this.setState({showField: val})} />
          </div>
          <div className="setting">
            <div className="mob-shell" style={{width: window.GLOB.shellWidth, height: window.GLOB.shellHeight}}>
              <div className="mob-shell-inner">
                <div className="am-navbar">
                  <Icon type="close" />
                  <div className="am-navbar-title">{config.setting.title}</div>
                </div>
                <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}
                />
                <Button className="modal-submit" type="primary">确定</Button>
              </div>
            </div>
          </div>
        </DndProvider>
        <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.card}
            formlist={this.state.formlist}
            inputSubmit={this.handleSubmit}
            standardform={this.state.standardform}
            wrappedComponentRef={(inst) => this.formRef = inst}
          />
        </Modal>
        <Modal
          title={this.state.dict['model.edit']}
          visible={this.state.settingVisible}
          width={850}
          maskClosable={false}
          onOk={this.settingSave}
          onCancel={() => { this.setState({ settingVisible: false }) }}
          destroyOnClose
        >
          <SettingForm
            config={config}
            dict={this.state.dict}
            isSubTab={!!this.props.editTab}
            inputSubmit={this.settingSave}
            wrappedComponentRef={(inst) => this.settingRef = inst}
          />
        </Modal>
      </div>
    )
  }
}
export default ComModalConfig
Diff truncated after the above file
src/mob/modalconfig/index.scss src/mob/modalconfig/pastecomponent/index.jsx src/mob/modalconfig/pastecomponent/index.scss src/mob/modalconfig/source.jsx src/mob/modulesource/index.jsx src/mob/modulesource/option.jsx src/mob/searchconfig/controller.jsx src/mob/searchconfig/groupdragelement/card.jsx src/mob/searchconfig/groupdragelement/index.jsx src/mob/searchconfig/groupdragelement/index.scss src/mob/searchconfig/groupform/index.jsx src/mob/searchconfig/groupform/index.scss src/mob/searchconfig/index.jsx src/mob/searchconfig/index.scss src/mob/searchconfig/pastecomponent/index.jsx src/mob/searchconfig/pastecomponent/index.scss src/mob/searchconfig/searchdragelement/card.jsx src/mob/searchconfig/searchdragelement/index.jsx src/mob/searchconfig/searchdragelement/index.scss src/mob/searchconfig/settingform/index.jsx src/mob/searchconfig/settingform/index.scss src/mob/searchconfig/source.jsx src/pc/bgcontroller/index.jsx src/pc/components/login/normal-login/index.jsx src/pc/components/login/normal-login/index.scss src/pc/components/login/normal-login/loginform.jsx src/pc/components/login/wrapsetting/index.jsx src/pc/components/login/wrapsetting/index.scss src/pc/components/login/wrapsetting/settingform/index.jsx src/pc/components/login/wrapsetting/settingform/index.scss src/pc/components/navbar/normal-navbar/index.jsx src/pc/components/navbar/normal-navbar/linksetting/linkform/index.jsx src/pc/components/navbar/normal-navbar/menusetting/menuform/index.jsx src/pc/components/navbar/normal-navbar/menusetting/menutable/index.jsx src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.jsx src/pc/components/navbar/normal-navbar/wrapsetting/settingform/index.scss src/pc/createview/index.jsx src/pc/createview/index.scss src/pc/createview/settingform/index.jsx src/pc/createview/settingform/index.scss src/pc/menushell/card.jsx src/pc/menushell/index.jsx src/pc/modulesource/index.jsx src/pc/modulesource/option.jsx src/pc/quotecomponent/index.jsx src/pc/transfer/index.jsx src/pc/transfer/index.scss src/pc/transfer/settingform/index.jsx src/pc/transfer/settingform/index.scss src/router/index.js src/tabviews/calendar/index.jsx src/tabviews/commontable/index.jsx src/tabviews/custom/components/card/balcony/index.jsx src/tabviews/custom/components/card/balcony/index.scss src/tabviews/custom/components/card/cardcellList/index.jsx src/tabviews/custom/components/card/cardcellList/index.scss src/tabviews/custom/components/card/data-card/index.jsx src/tabviews/custom/components/card/data-card/index.scss src/tabviews/custom/components/card/prop-card/index.jsx src/tabviews/custom/components/card/prop-card/index.scss src/tabviews/custom/components/card/table-card/index.jsx src/tabviews/custom/components/carousel/data-card/index.jsx src/tabviews/custom/components/carousel/data-card/index.scss src/tabviews/custom/components/carousel/prop-card/index.jsx src/tabviews/custom/components/carousel/prop-card/index.scss src/tabviews/custom/components/chart/antv-bar-line/index.jsx src/tabviews/custom/components/chart/antv-bar-line/index.scss src/tabviews/custom/components/chart/antv-dashboard/index.jsx src/tabviews/custom/components/chart/antv-dashboard/index.scss src/tabviews/custom/components/chart/antv-pie/index.jsx src/tabviews/custom/components/chart/antv-pie/index.scss src/tabviews/custom/components/chart/antv-scatter/asyncButtonComponent.jsx src/tabviews/custom/components/chart/antv-scatter/index.jsx src/tabviews/custom/components/chart/antv-scatter/index.scss src/tabviews/custom/components/code/sand-box/index.jsx src/tabviews/custom/components/editor/braft-editor/index.jsx src/tabviews/custom/components/form/normal-form/index.jsx src/tabviews/custom/components/form/normal-form/index.scss src/tabviews/custom/components/group/normal-group/index.jsx src/tabviews/custom/components/share/normalTable/index.jsx src/tabviews/custom/components/share/normalTable/index.scss src/tabviews/custom/components/share/normalheader/index.jsx src/tabviews/custom/components/share/normalheader/index.scss src/tabviews/custom/components/share/tabtransfer/index.jsx src/tabviews/custom/components/table/normal-table/index.jsx src/tabviews/custom/components/table/normal-table/index.scss src/tabviews/custom/components/tabs/antv-tabs/index.jsx src/tabviews/custom/components/tree/antd-tree/index.jsx src/tabviews/custom/components/tree/antd-tree/index.scss src/tabviews/custom/index.jsx src/tabviews/custom/index.scss src/tabviews/formtab/formgroup/index.jsx src/tabviews/home/index.jsx src/tabviews/scriptmanage/config.jsx src/tabviews/scriptmanage/index.jsx src/tabviews/subtable/index.jsx src/tabviews/subtabtable/index.jsx src/tabviews/tabmanage/index.jsx src/tabviews/treepage/index.jsx src/tabviews/verupmanage/config.jsx src/tabviews/zshare/actionList/asyncButtonComponent.jsx src/tabviews/zshare/actionList/changeuserbutton/index.jsx src/tabviews/zshare/actionList/excelInbutton/index.jsx src/tabviews/zshare/actionList/exceloutbutton/index.jsx src/tabviews/zshare/actionList/newpagebutton/index.jsx src/tabviews/zshare/actionList/normalbutton/index.jsx src/tabviews/zshare/actionList/popupbutton/index.jsx src/tabviews/zshare/actionList/printbutton/index.jsx src/tabviews/zshare/actionList/tabbutton/index.jsx src/tabviews/zshare/dategroup/index.jsx (deleted) src/tabviews/zshare/fileupload/index.jsx src/tabviews/zshare/fileupload/index.scss src/tabviews/zshare/imgScale/index.jsx src/tabviews/zshare/imgScale/index.scss src/tabviews/zshare/mutilform/checkCard/index.jsx src/tabviews/zshare/mutilform/checkCard/index.scss src/tabviews/zshare/mutilform/customSwitch/index.jsx (deleted) src/tabviews/zshare/mutilform/customTextArea/index.jsx (deleted) src/tabviews/zshare/mutilform/index.jsx src/tabviews/zshare/mutilform/mkCheckCard/index.jsx src/tabviews/zshare/mutilform/mkCheckCard/index.scss src/tabviews/zshare/mutilform/mkCheckbox/index.jsx src/tabviews/zshare/mutilform/mkCheckbox/index.scss src/tabviews/zshare/mutilform/mkColor/index.jsx src/tabviews/zshare/mutilform/mkColor/index.scss src/tabviews/zshare/mutilform/mkDatePicker/index.jsx src/tabviews/zshare/mutilform/mkDatePicker/index.scss src/tabviews/zshare/mutilform/mkInput/index.jsx src/tabviews/zshare/mutilform/mkInput/index.scss src/tabviews/zshare/mutilform/mkNumberInput/index.jsx src/tabviews/zshare/mutilform/mkNumberInput/index.scss src/tabviews/zshare/mutilform/mkRadio/index.jsx src/tabviews/zshare/mutilform/mkRadio/index.scss src/tabviews/zshare/mutilform/mkSelect/index.jsx src/tabviews/zshare/mutilform/mkSelect/index.scss src/tabviews/zshare/mutilform/mkSwitch/index.jsx src/tabviews/zshare/mutilform/mkSwitch/index.scss src/tabviews/zshare/mutilform/mkTextArea/index.jsx src/tabviews/zshare/mutilform/mkTextArea/index.scss src/tabviews/zshare/normalTable/index.jsx src/tabviews/zshare/topSearch/advanceform/index.jsx src/tabviews/zshare/topSearch/advanceform/index.scss src/tabviews/zshare/topSearch/dategroup/index.jsx src/tabviews/zshare/topSearch/dategroup/index.scss src/tabviews/zshare/topSearch/dategroup/quarterpicker/index.jsx src/tabviews/zshare/topSearch/dategroup/quarterpicker/index.scss src/tabviews/zshare/topSearch/dategroup/yearpicker/index.jsx src/tabviews/zshare/topSearch/dategroup/yearpicker/index.scss src/tabviews/zshare/topSearch/index.jsx src/tabviews/zshare/topSearch/index.scss src/tabviews/zshare/topSearch/mkDatePicker/index.jsx src/tabviews/zshare/topSearch/mkDatePicker/index.scss src/tabviews/zshare/topSearch/mkSelect/index.jsx src/tabviews/zshare/topSearch/mkSelect/index.scss src/templates/calendarconfig/source.jsx src/templates/comtableconfig/index.jsx src/templates/comtableconfig/source.jsx src/templates/formtabconfig/index.jsx src/templates/formtabconfig/settingform/index.jsx src/templates/menuconfig/editfirstmenu/index.jsx src/templates/menuconfig/editsecmenu/index.jsx src/templates/menuconfig/editthdmenu/index.jsx src/templates/modalconfig/checkCard/index.jsx src/templates/modalconfig/checkCard/index.scss src/templates/modalconfig/dragelement/card.jsx src/templates/modalconfig/dragelement/index.jsx src/templates/modalconfig/index.jsx src/templates/modalconfig/settingform/index.jsx src/templates/modalconfig/settingform/index.scss src/templates/sharecomponent/actioncomponent/actionform/index.jsx src/templates/sharecomponent/actioncomponent/index.scss src/templates/sharecomponent/actioncomponent/verifyexcelin/customscript/index.jsx src/templates/sharecomponent/actioncomponent/verifyexcelout/columnform/index.jsx src/templates/sharecomponent/actioncomponent/verifyexcelout/index.jsx src/templates/sharecomponent/columncomponent/columnform/index.jsx src/templates/sharecomponent/fieldscomponent/index.jsx src/templates/sharecomponent/searchcomponent/dragsearch/card.jsx src/templates/sharecomponent/searchcomponent/dragsearch/index.jsx src/templates/sharecomponent/searchcomponent/index.jsx src/templates/sharecomponent/searchcomponent/index.scss src/templates/sharecomponent/searchcomponent/searcheditable/index.jsx src/templates/sharecomponent/searchcomponent/searchform/index.jsx src/templates/sharecomponent/searchcomponent/searchform/index.scss src/templates/sharecomponent/settingcalcomponent/index.jsx src/templates/sharecomponent/settingcalcomponent/verifycard/index.jsx src/templates/sharecomponent/settingcalcomponent/verifycard/settingform/index.jsx 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/datasource/index.scss 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/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/zshare/basetransferform/index.jsx src/templates/zshare/customscript/index.jsx src/templates/zshare/editTable/index.jsx src/templates/zshare/editTable/index.scss src/templates/zshare/editcomponent/index.jsx src/templates/zshare/formconfig.jsx src/templates/zshare/modalform/datatable/index.jsx src/templates/zshare/modalform/datatable/index.scss src/templates/zshare/modalform/index.jsx src/templates/zshare/modalform/index.scss src/templates/zshare/modalform/modaleditable/index.jsx src/templates/zshare/verifycard/callbackcustomscript/index.jsx src/templates/zshare/verifycard/customform/index.jsx src/templates/zshare/verifycard/customscript/index.jsx src/templates/zshare/verifycard/index.jsx src/utils/option.js src/utils/utils-custom.js src/utils/utils-datamanage.js src/utils/utils.js src/views/appmanage/header/index.jsx src/views/appmanage/header/index.scss src/views/appmanage/index.jsx src/views/appmanage/index.scss src/views/appmanage/scriptform/index.jsx src/views/appmanage/scriptform/index.scss src/views/appmanage/submutilform/index.jsx src/views/appmanage/submutilform/index.scss src/views/appmanage/transform/index.jsx src/views/appmanage/transform/index.scss src/views/billprint/index.jsx src/views/billprint/index.scss src/views/design/header/index.jsx src/views/design/header/index.scss src/views/design/index.jsx src/views/design/sidemenu/config.jsx src/views/interface/api/index.js src/views/interface/header/index.jsx src/views/interface/header/index.scss src/views/interface/history/index.jsx src/views/interface/history/index.scss src/views/interface/index.jsx src/views/interface/index.scss src/views/interface/workspace/editTable/index.jsx src/views/interface/workspace/editTable/index.scss src/views/interface/workspace/index.jsx src/views/interface/workspace/index.scss src/views/interface/workspace/request/index.jsx src/views/interface/workspace/request/index.scss src/views/login/index.jsx src/views/login/index.scss src/views/login/loginform.jsx src/views/menudesign/homeform/index.jsx src/views/menudesign/index.jsx src/views/menudesign/index.scss src/views/menudesign/printmenuform/index.jsx src/views/menudesign/printmenuform/index.scss src/views/mobdesign/index.jsx src/views/mobdesign/index.scss src/views/mobdesign/menuform/index.jsx src/views/pcdesign/index.jsx src/views/pcdesign/index.scss src/views/pcdesign/menuform/index.jsx src/views/printTemplate/index.jsx src/views/printTemplate/mutilform/index.jsx src/views/printTemplate/mutilform/index.scss src/views/printTemplate/option.js src/views/printTemplate/print.js src/views/rolemanage/header/index.jsx src/views/rolemanage/header/index.scss src/views/rolemanage/index.jsx src/views/rolemanage/index.scss